From 837a1300f626957a483dbf6962b0776e0242b65c Mon Sep 17 00:00:00 2001 From: Jinzhu Date: Sat, 23 Dec 2023 09:24:46 +0800 Subject: [PATCH] Update documents with chatgpt --- pages/docs/advanced_query.md | 325 ++++++++++++++++++++++------------- pages/docs/associations.md | 264 ++++++++++++++++------------ pages/docs/write_plugins.md | 96 ++++++++--- 3 files changed, 426 insertions(+), 259 deletions(-) diff --git a/pages/docs/advanced_query.md b/pages/docs/advanced_query.md index a1f0597e342..278b1955bda 100644 --- a/pages/docs/advanced_query.md +++ b/pages/docs/advanced_query.md @@ -5,7 +5,7 @@ layout: page ## Smart Select Fields -GORM allows selecting specific fields with [`Select`](query.html), if you often use this in your application, maybe you want to define a smaller struct for API usage which can select specific fields automatically, for example: +In GORM, you can efficiently select specific fields using the [`Select`](query.html) method. This is particularly useful when dealing with large models but requiring only a subset of fields, especially in API responses. ```go type User struct { @@ -21,13 +21,13 @@ type APIUser struct { Name string } -// Select `id`, `name` automatically when querying +// GORM will automatically select `id`, `name` fields when querying db.Model(&User{}).Limit(10).Find(&APIUser{}) -// SELECT `id`, `name` FROM `users` LIMIT 10 +// SQL: SELECT `id`, `name` FROM `users` LIMIT 10 ``` {% note warn %} -**NOTE** `QueryFields` mode will select by all fields' name for current model +**NOTE** In `QueryFields` mode, all model fields are selected by their names. {% endnote %} ```go @@ -35,12 +35,13 @@ db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{ QueryFields: true, }) +// Default behavior with QueryFields set to true db.Find(&user) -// SELECT `users`.`name`, `users`.`age`, ... FROM `users` // with this option +// SQL: SELECT `users`.`name`, `users`.`age`, ... FROM `users` -// Session Mode +// Using Session Mode with QueryFields db.Session(&gorm.Session{QueryFields: true}).Find(&user) -// SELECT `users`.`name`, `users`.`age`, ... FROM `users` +// SQL: SELECT `users`.`name`, `users`.`age`, ... FROM `users` ``` ## Locking @@ -48,238 +49,268 @@ db.Session(&gorm.Session{QueryFields: true}).Find(&user) GORM supports different types of locks, for example: ```go +// Basic FOR UPDATE lock db.Clauses(clause.Locking{Strength: "UPDATE"}).Find(&users) -// SELECT * FROM `users` FOR UPDATE +// SQL: SELECT * FROM `users` FOR UPDATE ``` + The above statement will lock the selected rows for the duration of the transaction. This can be used in scenarios where you are preparing to update the rows and want to prevent other transactions from modifying them until your transaction is complete. The `Strength` can be also set to `SHARE` which locks the rows in a way that allows other transactions to read the locked rows but not to update or delete them. ```go -db.Clauses(clause.Locking{ - Strength: "SHARE", -}).Find(&users) -// SELECT * FROM `users` FOR SHARE OF `users` -``` -The `Table` option can be used to specify the table to lock. This is useful when you are joining multiple tables and want to lock only one of them. -```go db.Clauses(clause.Locking{ Strength: "SHARE", Table: clause.Table{Name: clause.CurrentTable}, }).Find(&users) -// SELECT * FROM `users` FOR SHARE OF `users` +// SQL: SELECT * FROM `users` FOR SHARE OF `users` ``` + +The `Table` option can be used to specify the table to lock. This is useful when you are joining multiple tables and want to lock only one of them. + Options can be provided like `NOWAIT` which tries to acquire a lock and fails immediately with an error if the lock is not available. It prevents the transaction from waiting for other transactions to release their locks. + ```go db.Clauses(clause.Locking{ Strength: "UPDATE", Options: "NOWAIT", }).Find(&users) -// SELECT * FROM `users` FOR UPDATE NOWAIT +// SQL: SELECT * FROM `users` FOR UPDATE NOWAIT ``` + Another option can be `SKIP LOCKED` which skips over any rows that are already locked by other transactions. This is useful in high concurrency situations where you want to process rows that are not currently locked by other transactions. +For more advanced locking strategies, refer to [Raw SQL and SQL Builder](sql_builder.html). + ## SubQuery -A subquery can be nested within a query, GORM can generate subquery when using a `*gorm.DB` object as param +Subqueries are a powerful feature in SQL, allowing nested queries. GORM can generate subqueries automatically when using a *gorm.DB object as a parameter. ```go +// Simple subquery db.Where("amount > (?)", db.Table("orders").Select("AVG(amount)")).Find(&orders) -// SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders"); +// SQL: SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders"); +// Nested subquery subQuery := db.Select("AVG(age)").Where("name LIKE ?", "name%").Table("users") db.Select("AVG(age) as avgage").Group("name").Having("AVG(age) > (?)", subQuery).Find(&results) -// SELECT AVG(age) as avgage FROM `users` GROUP BY `name` HAVING AVG(age) > (SELECT AVG(age) FROM `users` WHERE name LIKE "name%") +// SQL: SELECT AVG(age) as avgage FROM `users` GROUP BY `name` HAVING AVG(age) > (SELECT AVG(age) FROM `users` WHERE name LIKE "name%") ``` ### From SubQuery -GORM allows you using subquery in FROM clause with the method `Table`, for example: +GORM allows the use of subqueries in the FROM clause, enabling complex queries and data organization. ```go +// Using subquery in FROM clause db.Table("(?) as u", db.Model(&User{}).Select("name", "age")).Where("age = ?", 18).Find(&User{}) -// SELECT * FROM (SELECT `name`,`age` FROM `users`) as u WHERE `age` = 18 +// SQL: SELECT * FROM (SELECT `name`,`age` FROM `users`) as u WHERE `age` = 18 +// Combining multiple subqueries in FROM clause subQuery1 := db.Model(&User{}).Select("name") subQuery2 := db.Model(&Pet{}).Select("name") db.Table("(?) as u, (?) as p", subQuery1, subQuery2).Find(&User{}) -// SELECT * FROM (SELECT `name` FROM `users`) as u, (SELECT `name` FROM `pets`) as p +// SQL: SELECT * FROM (SELECT `name` FROM `users`) as u, (SELECT `name` FROM `pets`) as p ``` ## Group Conditions -Easier to write complicated SQL query with Group Conditions +Group Conditions in GORM provide a more readable and maintainable way to write complex SQL queries involving multiple conditions. ```go +// Complex SQL query using Group Conditions db.Where( - db.Where("pizza = ?", "pepperoni").Where(db.Where("size = ?", "small").Or("size = ?", "medium")), + db.Where("pizza = ?", "pepperoni").Where(db.Where("size = ?", "small").Or("size = ?", "medium")), ).Or( - db.Where("pizza = ?", "hawaiian").Where("size = ?", "xlarge"), -).Find(&Pizza{}).Statement - -// SELECT * FROM `pizzas` WHERE (pizza = "pepperoni" AND (size = "small" OR size = "medium")) OR (pizza = "hawaiian" AND size = "xlarge") + db.Where("pizza = ?", "hawaiian").Where("size = ?", "xlarge"), +).Find(&Pizza{}) +// SQL: SELECT * FROM `pizzas` WHERE (pizza = "pepperoni" AND (size = "small" OR size = "medium")) OR (pizza = "hawaiian" AND size = "xlarge") ``` ## IN with multiple columns -Selecting IN with multiple columns +GORM supports the IN clause with multiple columns, allowing you to filter data based on multiple field values in a single query. ```go +// Using IN with multiple columns db.Where("(name, age, role) IN ?", [][]interface{}{{"jinzhu", 18, "admin"}, {"jinzhu2", 19, "user"}}).Find(&users) -// SELECT * FROM users WHERE (name, age, role) IN (("jinzhu", 18, "admin"), ("jinzhu 2", 19, "user")); +// SQL: SELECT * FROM users WHERE (name, age, role) IN (("jinzhu", 18, "admin"), ("jinzhu 2", 19, "user")); ``` ## Named Argument -GORM supports named arguments with [`sql.NamedArg`](https://tip.golang.org/pkg/database/sql/#NamedArg) or `map[string]interface{}{}`, for example: +GORM enhances the readability and maintainability of SQL queries by supporting named arguments. This feature allows for clearer and more organized query construction, especially in complex queries with multiple parameters. Named arguments can be utilized using either [`sql.NamedArg`](https://tip.golang.org/pkg/database/sql/#NamedArg) or `map[string]interface{}{}`, providing flexibility in how you structure your queries. ```go +// Example using sql.NamedArg for named arguments db.Where("name1 = @name OR name2 = @name", sql.Named("name", "jinzhu")).Find(&user) -// SELECT * FROM `users` WHERE name1 = "jinzhu" OR name2 = "jinzhu" +// SQL: SELECT * FROM `users` WHERE name1 = "jinzhu" OR name2 = "jinzhu" +// Example using a map for named arguments db.Where("name1 = @name OR name2 = @name", map[string]interface{}{"name": "jinzhu"}).First(&user) -// SELECT * FROM `users` WHERE name1 = "jinzhu" OR name2 = "jinzhu" ORDER BY `users`.`id` LIMIT 1 +// SQL: SELECT * FROM `users` WHERE name1 = "jinzhu" OR name2 = "jinzhu" ORDER BY `users`.`id` LIMIT 1 ``` -Check out [Raw SQL and SQL Builder](sql_builder.html#named_argument) for more detail +For more examples and details, see [Raw SQL and SQL Builder](sql_builder.html#named_argument) ## Find To Map -GORM allows scanning results to `map[string]interface{}` or `[]map[string]interface{}`, don't forget to specify `Model` or `Table`, for example: +GORM provides flexibility in querying data by allowing results to be scanned into a `map[string]interface{}` or `[]map[string]interface{}`, which can be useful for dynamic data structures. + +When using `Find To Map`, it's crucial to include `Model` or `Table` in your query to explicitly specify the table name. This ensures that GORM understands which table to query against. ```go +// Scanning the first result into a map with Model result := map[string]interface{}{} db.Model(&User{}).First(&result, "id = ?", 1) +// SQL: SELECT * FROM `users` WHERE id = 1 LIMIT 1 +// Scanning multiple results into a slice of maps with Table var results []map[string]interface{} db.Table("users").Find(&results) +// SQL: SELECT * FROM `users` ``` ## FirstOrInit -Get first matched record or initialize a new instance with given conditions (only works with struct or map conditions) +GORM's `FirstOrInit` method is utilized to fetch the first record that matches given conditions, or initialize a new instance if no matching record is found. This method is compatible with both struct and map conditions and allows additional flexibility with the `Attrs` and `Assign` methods. ```go -// User not found, initialize it with give conditions +// If no User with the name "non_existing" is found, initialize a new User +var user User db.FirstOrInit(&user, User{Name: "non_existing"}) -// user -> User{Name: "non_existing"} +// user -> User{Name: "non_existing"} if not found -// Found user with `name` = `jinzhu` +// Retrieving a user named "jinzhu" db.Where(User{Name: "jinzhu"}).FirstOrInit(&user) -// user -> User{ID: 111, Name: "Jinzhu", Age: 18} +// user -> User{ID: 111, Name: "Jinzhu", Age: 18} if found -// Found user with `name` = `jinzhu` +// Using a map to specify the search condition db.FirstOrInit(&user, map[string]interface{}{"name": "jinzhu"}) -// user -> User{ID: 111, Name: "Jinzhu", Age: 18} +// user -> User{ID: 111, Name: "Jinzhu", Age: 18} if found ``` -Initialize struct with more attributes if record not found, those `Attrs` won't be used to build the SQL query +### Using `Attrs` for Initialization + +When no record is found, you can use `Attrs` to initialize a struct with additional attributes. These attributes are included in the new struct but are not used in the SQL query. ```go -// User not found, initialize it with give conditions and Attrs +// If no User is found, initialize with given conditions and additional attributes db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrInit(&user) -// SELECT * FROM USERS WHERE name = 'non_existing' ORDER BY id LIMIT 1; -// user -> User{Name: "non_existing", Age: 20} - -// User not found, initialize it with give conditions and Attrs -db.Where(User{Name: "non_existing"}).Attrs("age", 20).FirstOrInit(&user) -// SELECT * FROM USERS WHERE name = 'non_existing' ORDER BY id LIMIT 1; -// user -> User{Name: "non_existing", Age: 20} +// SQL: SELECT * FROM USERS WHERE name = 'non_existing' ORDER BY id LIMIT 1; +// user -> User{Name: "non_existing", Age: 20} if not found -// Found user with `name` = `jinzhu`, attributes will be ignored +// If a User named "Jinzhu" is found, `Attrs` are ignored db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 20}).FirstOrInit(&user) -// SELECT * FROM USERS WHERE name = jinzhu' ORDER BY id LIMIT 1; -// user -> User{ID: 111, Name: "Jinzhu", Age: 18} +// SQL: SELECT * FROM USERS WHERE name = 'Jinzhu' ORDER BY id LIMIT 1; +// user -> User{ID: 111, Name: "Jinzhu", Age: 18} if found ``` -`Assign` attributes to struct regardless it is found or not, those attributes won't be used to build SQL query and the final data won't be saved into database +### Using `Assign` for Attributes + +The `Assign` method allows you to set attributes on the struct regardless of whether the record is found or not. These attributes are set on the struct but are not used to build the SQL query and the final data won't be saved into the database. ```go -// User not found, initialize it with give conditions and Assign attributes +// Initialize with given conditions and Assign attributes, regardless of record existence db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrInit(&user) -// user -> User{Name: "non_existing", Age: 20} +// user -> User{Name: "non_existing", Age: 20} if not found -// Found user with `name` = `jinzhu`, update it with Assign attributes +// If a User named "Jinzhu" is found, update the struct with Assign attributes db.Where(User{Name: "Jinzhu"}).Assign(User{Age: 20}).FirstOrInit(&user) -// SELECT * FROM USERS WHERE name = jinzhu' ORDER BY id LIMIT 1; -// user -> User{ID: 111, Name: "Jinzhu", Age: 20} +// SQL: SELECT * FROM USERS WHERE name = 'Jinzhu' ORDER BY id LIMIT 1; +// user -> User{ID: 111, Name: "Jinzhu", Age: 20} if found ``` +`FirstOrInit`, along with `Attrs` and `Assign`, provides a powerful and flexible way to ensure a record exists and is initialized or updated with specific attributes in a single step. + ## FirstOrCreate -Get first matched record or create a new one with given conditions (only works with struct, map conditions), `RowsAffected` returns created/updated record's count +`FirstOrCreate` in GORM is used to fetch the first record that matches given conditions or create a new one if no matching record is found. This method is effective with both struct and map conditions. The `RowsAffected` property is useful to determine the number of records created or updated. ```go -// User not found, create a new record with give conditions +// Create a new record if not found result := db.FirstOrCreate(&user, User{Name: "non_existing"}) -// INSERT INTO "users" (name) VALUES ("non_existing"); +// SQL: INSERT INTO "users" (name) VALUES ("non_existing"); // user -> User{ID: 112, Name: "non_existing"} -// result.RowsAffected // => 1 +// result.RowsAffected // => 1 (record created) -// Found user with `name` = `jinzhu` -result := db.Where(User{Name: "jinzhu"}).FirstOrCreate(&user) -// user -> User{ID: 111, Name: "jinzhu", "Age": 18} -// result.RowsAffected // => 0 +// If the user is found, no new record is created +result = db.Where(User{Name: "jinzhu"}).FirstOrCreate(&user) +// user -> User{ID: 111, Name: "jinzhu", Age: 18} +// result.RowsAffected // => 0 (no record created) ``` -Create struct with more attributes if record not found, those `Attrs` won't be used to build SQL query +### Using `Attrs` with FirstOrCreate + +`Attrs` can be used to specify additional attributes for the new record if it is not found. These attributes are used for creation but not in the initial search query. ```go -// User not found, create it with give conditions and Attrs +// Create a new record with additional attributes if not found db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user) -// SELECT * FROM users WHERE name = 'non_existing' ORDER BY id LIMIT 1; -// INSERT INTO "users" (name, age) VALUES ("non_existing", 20); +// SQL: SELECT * FROM users WHERE name = 'non_existing'; +// SQL: INSERT INTO "users" (name, age) VALUES ("non_existing", 20); // user -> User{ID: 112, Name: "non_existing", Age: 20} -// Found user with `name` = `jinzhu`, attributes will be ignored +// If the user is found, `Attrs` are ignored db.Where(User{Name: "jinzhu"}).Attrs(User{Age: 20}).FirstOrCreate(&user) -// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1; +// SQL: SELECT * FROM users WHERE name = 'jinzhu'; // user -> User{ID: 111, Name: "jinzhu", Age: 18} ``` -`Assign` attributes to the record regardless it is found or not and save them back to the database. +### Using `Assign` with FirstOrCreate + +The `Assign` method sets attributes on the record regardless of whether it is found or not, and these attributes are saved back to the database. ```go -// User not found, initialize it with give conditions and Assign attributes +// Initialize and save new record with `Assign` attributes if not found db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user) -// SELECT * FROM users WHERE name = 'non_existing' ORDER BY id LIMIT 1; -// INSERT INTO "users" (name, age) VALUES ("non_existing", 20); +// SQL: SELECT * FROM users WHERE name = 'non_existing'; +// SQL: INSERT INTO "users" (name, age) VALUES ("non_existing", 20); // user -> User{ID: 112, Name: "non_existing", Age: 20} -// Found user with `name` = `jinzhu`, update it with Assign attributes +// Update found record with `Assign` attributes db.Where(User{Name: "jinzhu"}).Assign(User{Age: 20}).FirstOrCreate(&user) -// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1; -// UPDATE users SET age=20 WHERE id = 111; -// user -> User{ID: 111, Name: "jinzhu", Age: 20} +// SQL: SELECT * FROM users WHERE name = 'jinzhu'; +// SQL: UPDATE users SET age=20 WHERE id = 111; +// user -> User{ID: 111, Name: "Jinzhu", Age: 20} ``` ## Optimizer/Index Hints -Optimizer hints allow to control the query optimizer to choose a certain query execution plan, GORM supports it with `gorm.io/hints`, e.g: +GORM includes support for optimizer and index hints, allowing you to influence the query optimizer's execution plan. This can be particularly useful in optimizing query performance or when dealing with complex queries. + +Optimizer hints are directives that suggest how a database's query optimizer should execute a query. GORM facilitates the use of optimizer hints through the gorm.io/hints package. ```go import "gorm.io/hints" +// Using an optimizer hint to set a maximum execution time db.Clauses(hints.New("MAX_EXECUTION_TIME(10000)")).Find(&User{}) -// SELECT * /*+ MAX_EXECUTION_TIME(10000) */ FROM `users` +// SQL: SELECT * /*+ MAX_EXECUTION_TIME(10000) */ FROM `users` ``` -Index hints allow passing index hints to the database in case the query planner gets confused. +### Index Hints + +Index hints provide guidance to the database about which indexes to use. They can be beneficial if the query planner is not selecting the most efficient indexes for a query. ```go import "gorm.io/hints" +// Suggesting the use of a specific index db.Clauses(hints.UseIndex("idx_user_name")).Find(&User{}) -// SELECT * FROM `users` USE INDEX (`idx_user_name`) +// SQL: SELECT * FROM `users` USE INDEX (`idx_user_name`) +// Forcing the use of certain indexes for a JOIN operation db.Clauses(hints.ForceIndex("idx_user_name", "idx_user_id").ForJoin()).Find(&User{}) -// SELECT * FROM `users` FORCE INDEX FOR JOIN (`idx_user_name`,`idx_user_id`)" +// SQL: SELECT * FROM `users` FORCE INDEX FOR JOIN (`idx_user_name`,`idx_user_id`) ``` -Refer [Optimizer Hints/Index/Comment](hints.html) for more details +These hints can significantly impact query performance and behavior, especially in large databases or complex data models. For more detailed information and additional examples, refer to [Optimizer Hints/Index/Comment](hints.html) in the GORM documentation. ## Iteration -GORM supports iterating through Rows +GORM supports the iteration over query results using the `Rows` method. This feature is particularly useful when you need to process large datasets or perform operations on each record individually. + +You can iterate through rows returned by a query, scanning each row into a struct. This method provides granular control over how each record is handled. ```go rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Rows() @@ -287,131 +318,176 @@ defer rows.Close() for rows.Next() { var user User - // ScanRows is a method of `gorm.DB`, it can be used to scan a row into a struct + // ScanRows scans a row into a struct db.ScanRows(rows, &user) - // do something + // Perform operations on each user } ``` +This approach is ideal for complex data processing that cannot be easily achieved with standard query methods. + ## FindInBatches -Query and process records in batch +`FindInBatches` allows querying and processing records in batches. This is especially useful for handling large datasets efficiently, reducing memory usage and improving performance. + +With `FindInBatches`, GORM processes records in specified batch sizes. Inside the batch processing function, you can apply operations to each batch of records. ```go -// batch size 100 +// Processing records in batches of 100 result := db.Where("processed = ?", false).FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error { for _, result := range results { - // batch processing found records + // Operations on each record in the batch } + // Save changes to the records in the current batch tx.Save(&results) - tx.RowsAffected // number of records in this batch - - batch // Batch 1, 2, 3 + // tx.RowsAffected provides the count of records in the current batch + // The variable 'batch' indicates the current batch number - // returns error will stop future batches + // Returning an error will stop further batch processing return nil }) -result.Error // returned error -result.RowsAffected // processed records count in all batches +// result.Error contains any errors encountered during batch processing +// result.RowsAffected provides the count of all processed records across batches ``` +`FindInBatches` is an effective tool for processing large volumes of data in manageable chunks, optimizing resource usage and performance. + ## Query Hooks -GORM allows hooks `AfterFind` for a query, it will be called when querying a record, refer [Hooks](hooks.html) for details +GORM offers the ability to use hooks, such as `AfterFind`, which are triggered during the lifecycle of a query. These hooks allow for custom logic to be executed at specific points, such as after a record has been retrieved from the databas. + +This hook is useful for post-query data manipulation or default value settings. For more detailed information and additional hook types, refer to [Hooks](hooks.html) in the GORM documentation. ```go func (u *User) AfterFind(tx *gorm.DB) (err error) { + // Custom logic after finding a user if u.Role == "" { - u.Role = "user" + u.Role = "user" // Set default role if not specified } return } + +// Usage of AfterFind hook happens automatically when a User is queried ``` ## Pluck -Query single column from database and scan into a slice, if you want to query multiple columns, use `Select` with [`Scan`](query.html#scan) instead +The `Pluck` method in GORM is used to query a single column from the database and scan the result into a slice. This method is ideal for when you need to retrieve specific fields from a model. + +If you need to query more than one column, you can use `Select` with [Scan](query.html) or [Find](query.html) instead. ```go +// Retrieving ages of all users var ages []int64 -db.Model(&users).Pluck("age", &ages) +db.Model(&User{}).Pluck("age", &ages) +// Retrieving names of all users var names []string db.Model(&User{}).Pluck("name", &names) +// Retrieving names from a different table db.Table("deleted_users").Pluck("name", &names) -// Distinct Pluck +// Using Distinct with Pluck db.Model(&User{}).Distinct().Pluck("Name", &names) -// SELECT DISTINCT `name` FROM `users` +// SQL: SELECT DISTINCT `name` FROM `users` -// Requesting more than one column, use `Scan` or `Find` like this: +// Querying multiple columns db.Select("name", "age").Scan(&users) db.Select("name", "age").Find(&users) ``` ## Scopes -`Scopes` allows you to specify commonly-used queries which can be referenced as method calls +`Scopes` in GORM are a powerful feature that allows you to define commonly-used query conditions as reusable methods. These scopes can be easily referenced in your queries, making your code more modular and readable. + +### Defining Scopes + +`Scopes` are defined as functions that modify and return a `gorm.DB` instance. You can define a variety of conditions as scopes based on your application's requirements. ```go +// Scope for filtering records where amount is greater than 1000 func AmountGreaterThan1000(db *gorm.DB) *gorm.DB { return db.Where("amount > ?", 1000) } +// Scope for orders paid with a credit card func PaidWithCreditCard(db *gorm.DB) *gorm.DB { return db.Where("pay_mode_sign = ?", "C") } +// Scope for orders paid with cash on delivery (COD) func PaidWithCod(db *gorm.DB) *gorm.DB { return db.Where("pay_mode_sign = ?", "C") } -func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB { - return func (db *gorm.DB) *gorm.DB { +// Scope for filtering orders by status +func OrderStatus(status []string) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { return db.Where("status IN (?)", status) } } +``` + +### Applying Scopes in Queries + +You can apply one or more scopes to a query by using the `Scopes` method. This allows you to chain multiple conditions dynamically. +```go +// Applying scopes to find all credit card orders with an amount greater than 1000 db.Scopes(AmountGreaterThan1000, PaidWithCreditCard).Find(&orders) -// Find all credit card orders and amount greater than 1000 +// Applying scopes to find all COD orders with an amount greater than 1000 db.Scopes(AmountGreaterThan1000, PaidWithCod).Find(&orders) -// Find all COD orders and amount greater than 1000 +// Applying scopes to find all orders with specific statuses and an amount greater than 1000 db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders) -// Find all paid, shipped orders that amount greater than 1000 ``` -Checkout [Scopes](scopes.html) for details +`Scopes` are a clean and efficient way to encapsulate common query logic, enhancing the maintainability and readability of your code. For more detailed examples and usage, refer to [Scopes](scopes.html) in the GORM documentation. ## Count -Get matched records count +The `Count` method in GORM is used to retrieve the number of records that match a given query. It's a useful feature for understanding the size of a dataset, particularly in scenarios involving conditional queries or data analysis. + +### Getting the Count of Matched Records + +You can use `Count` to determine the number of records that meet specific criteria in your queries. ```go var count int64 + +// Counting users with specific names db.Model(&User{}).Where("name = ?", "jinzhu").Or("name = ?", "jinzhu 2").Count(&count) -// SELECT count(1) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2' +// SQL: SELECT count(1) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2' +// Counting users with a single name condition db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count) -// SELECT count(1) FROM users WHERE name = 'jinzhu'; (count) +// SQL: SELECT count(1) FROM users WHERE name = 'jinzhu' +// Counting records in a different table db.Table("deleted_users").Count(&count) -// SELECT count(1) FROM deleted_users; +// SQL: SELECT count(1) FROM deleted_users +``` + +### Count with Distinct and Group -// Count with Distinct +GORM also allows counting distinct values and grouping results. + +```go +// Counting distinct names db.Model(&User{}).Distinct("name").Count(&count) -// SELECT COUNT(DISTINCT(`name`)) FROM `users` +// SQL: SELECT COUNT(DISTINCT(`name`)) FROM `users` +// Counting distinct values with a custom select db.Table("deleted_users").Select("count(distinct(name))").Count(&count) -// SELECT count(distinct(name)) FROM deleted_users +// SQL: SELECT count(distinct(name)) FROM deleted_users -// Count with Group +// Counting grouped records users := []User{ {Name: "name1"}, {Name: "name2"}, @@ -420,5 +496,6 @@ users := []User{ } db.Model(&User{}).Group("name").Count(&count) -count // => 3 +// Count after grouping by name +// count => 3 ``` diff --git a/pages/docs/associations.md b/pages/docs/associations.md index 98ba5028715..a2062af5101 100644 --- a/pages/docs/associations.md +++ b/pages/docs/associations.md @@ -5,7 +5,11 @@ layout: page ## Auto Create/Update -GORM will auto-save associations and its reference using [Upsert](create.html#upsert) when creating/updating a record. +GORM automates the saving of associations and their references when creating or updating records, using an upsert technique that primarily updates foreign key references for existing associations. + +### Auto-Saving Associations on Create + +When you create a new record, GORM will automatically save its associated data. This includes inserting data into related tables and managing foreign key references. ```go user := User{ @@ -22,6 +26,7 @@ user := User{ }, } +// Creating a user along with its associated addresses, emails, and languages db.Create(&user) // BEGIN TRANSACTION; // INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1"), ("Shipping Address - Address 1") ON DUPLICATE KEY DO NOTHING; @@ -34,63 +39,76 @@ db.Create(&user) db.Save(&user) ``` -If you want to update associations's data, you should use the `FullSaveAssociations` mode: +### Updating Associations with `FullSaveAssociations` + +For scenarios where a full update of the associated data is required (not just the foreign key references), the `FullSaveAssociations` mode should be used. ```go +// Update a user and fully update all its associations db.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&user) -// ... -// INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1"), ("Shipping Address - Address 1") ON DUPLICATE KEY SET address1=VALUES(address1); -// INSERT INTO "users" (name,billing_address_id,shipping_address_id) VALUES ("jinzhu", 1, 2); -// INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu@example.com"), (111, "jinzhu-2@example.com") ON DUPLICATE KEY SET email=VALUES(email); -// ... +// SQL: Fully updates addresses, users, emails tables, including existing associated records ``` +Using `FullSaveAssociations` ensures that the entire state of the model, including all its associations, is reflected in the database, maintaining data integrity and consistency throughout the application. + ## Skip Auto Create/Update -To skip the auto save when creating/updating, you can use `Select` or `Omit`, for example: +GORM provides flexibility to skip automatic saving of associations during create or update operations. This can be achieved using the `Select` or `Omit` methods, which allow you to specify exactly which fields or associations should be included or excluded in the operation. + +### Using `Select` to Include Specific Fields + +The `Select` method lets you specify which fields of the model should be saved. This means that only the selected fields will be included in the SQL operation. ```go user := User{ - Name: "jinzhu", - BillingAddress: Address{Address1: "Billing Address - Address 1"}, - ShippingAddress: Address{Address1: "Shipping Address - Address 1"}, - Emails: []Email{ - {Email: "jinzhu@example.com"}, - {Email: "jinzhu-2@example.com"}, - }, - Languages: []Language{ - {Name: "ZH"}, - {Name: "EN"}, - }, + // User and associated data } +// Only include the 'Name' field when creating the user db.Select("Name").Create(&user) -// INSERT INTO "users" (name) VALUES ("jinzhu", 1, 2); +// SQL: INSERT INTO "users" (name) VALUES ("jinzhu"); +``` + +### Using `Omit` to Exclude Fields or Associations + +Conversely, `Omit` allows you to exclude certain fields or associations when saving a model. +```go +// Skip creating the 'BillingAddress' when creating the user db.Omit("BillingAddress").Create(&user) -// Skip create BillingAddress when creating a user +// Skip all associations when creating the user db.Omit(clause.Associations).Create(&user) -// Skip all associations when creating a user ``` {% note warn %} **NOTE:** -For many2many associations, GORM will upsert the associations before creating the join table references, if you want to skip the upserting of associations, you could skip it like: +For many-to-many associations, GORM upserts the associations before creating join table references. To skip this upserting, use `Omit` with the association name followed by `.*`: ```go +// Skip upserting 'Languages' associations db.Omit("Languages.*").Create(&user) ``` -The following code will skip the creation of the association and its references +To skip creating both the association and its references: ```go +// Skip creating 'Languages' associations and their references db.Omit("Languages").Create(&user) ``` {% endnote %} +Using `Select` and `Omit`, you can fine-tune how GORM handles the creation or updating of your models, giving you control over the auto-save behavior of associations. + ## Select/Omit Association fields +In GORM, when creating or updating records, you can use the `Select` and `Omit` methods to specifically include or exclude certain fields of an associated model. + +With `Select`, you can specify which fields of an associated model should be included when saving the primary model. This is particularly useful for selectively saving parts of an association. + +Conversely, `Omit` lets you exclude certain fields of an associated model from being saved. This can be useful when you want to prevent specific parts of an association from being persisted. + + ```go user := User{ Name: "jinzhu", @@ -98,49 +116,88 @@ user := User{ ShippingAddress: Address{Address1: "Shipping Address - Address 1", Address2: "addr2"}, } -// Create user and his BillingAddress, ShippingAddress -// When creating the BillingAddress only use its address1, address2 fields and omit others +// Create user and his BillingAddress, ShippingAddress, including only specified fields of BillingAddress db.Select("BillingAddress.Address1", "BillingAddress.Address2").Create(&user) +// SQL: Creates user and BillingAddress with only 'Address1' and 'Address2' fields +// Create user and his BillingAddress, ShippingAddress, excluding specific fields of BillingAddress db.Omit("BillingAddress.Address2", "BillingAddress.CreatedAt").Create(&user) +// SQL: Creates user and BillingAddress, omitting 'Address2' and 'CreatedAt' fields ``` +## Delete Associations + +GORM allows for the deletion of specific associated relationships (has one, has many, many2many) using the `Select` method when deleting a primary record. This feature is particularly useful for maintaining database integrity and ensuring related data is appropriately managed upon deletion. + +You can specify which associations should be deleted along with the primary record by using `Select`. + +```go +// Delete a user's account when deleting the user +db.Select("Account").Delete(&user) + +// Delete a user's Orders and CreditCards associations when deleting the user +db.Select("Orders", "CreditCards").Delete(&user) + +// Delete all of a user's has one, has many, and many2many associations +db.Select(clause.Associations).Delete(&user) + +// Delete each user's account when deleting multiple users +db.Select("Account").Delete(&users) +``` + +{% note warn %} +**NOTE:** +It's important to note that associations will only be deleted if the primary key of the deleting record is not zero. GORM uses these primary keys as conditions to delete the selected associations. + +```go +// This will not work as intended +db.Select("Account").Where("name = ?", "jinzhu").Delete(&User{}) +// SQL: Deletes all users with name 'jinzhu', but their accounts won't be deleted + +// Correct way to delete a user and their account +db.Select("Account").Where("name = ?", "jinzhu").Delete(&User{ID: 1}) +// SQL: Deletes the user with name 'jinzhu' and ID '1', and the user's account + +// Deleting a user with a specific ID and their account +db.Select("Account").Delete(&User{ID: 1}) +// SQL: Deletes the user with ID '1', and the user's account +``` +{% endnote %} + ## Association Mode -Association Mode contains some commonly used helper methods to handle relationships +Association Mode in GORM offers various helper methods to handle relationships between models, providing an efficient way to manage associated data. + +### Starting Association Mode + +To start Association Mode, specify the source model and the relationship's field name. The source model must contain a primary key, and the relationship's field name should match an existing association. ```go -// Start Association Mode var user User db.Model(&user).Association("Languages") -// `user` is the source model, it must contains primary key -// `Languages` is a relationship's field name -// If the above two requirements matched, the AssociationMode should be started successfully, or it should return error -db.Model(&user).Association("Languages").Error +// Check for errors +error := db.Model(&user).Association("Languages").Error ``` -### Find Associations +### Finding Associations -Find matched associations +Retrieve associated records with or without additional conditions. ```go +// Simple find db.Model(&user).Association("Languages").Find(&languages) -``` -Find associations with conditions - -```go +// Find with conditions codes := []string{"zh-CN", "en-US", "ja-JP"} db.Model(&user).Where("code IN ?", codes).Association("Languages").Find(&languages) - -db.Model(&user).Where("code IN ?", codes).Order("code desc").Association("Languages").Find(&languages) ``` -### Append Associations +### Appending Associations -Append new associations for `many to many`, `has many`, replace current association for `has one`, `belongs to` +Add new associations for `many to many`, `has many`, or replace the current association for `has one`, `belongs to`. ```go +// Append new languages db.Model(&user).Association("Languages").Append([]Language{languageZH, languageEN}) db.Model(&user).Association("Languages").Append(&Language{Name: "DE"}) @@ -148,38 +205,43 @@ db.Model(&user).Association("Languages").Append(&Language{Name: "DE"}) db.Model(&user).Association("CreditCard").Append(&CreditCard{Number: "411111111111"}) ``` -### Replace Associations +### Replacing Associations -Replace current associations with new ones +Replace current associations with new ones. ```go +// Replace existing languages db.Model(&user).Association("Languages").Replace([]Language{languageZH, languageEN}) db.Model(&user).Association("Languages").Replace(Language{Name: "DE"}, languageEN) ``` -### Delete Associations +### Deleting Associations -Remove the relationship between source & arguments if exists, only delete the reference, won't delete those objects from DB. +Remove the relationship between the source and arguments, only deleting the reference. ```go +// Delete specific languages db.Model(&user).Association("Languages").Delete([]Language{languageZH, languageEN}) + db.Model(&user).Association("Languages").Delete(languageZH, languageEN) ``` -### Clear Associations +### Clearing Associations -Remove all reference between source & association, won't delete those associations +Remove all references between the source and association. ```go +// Clear all languages db.Model(&user).Association("Languages").Clear() ``` -### Count Associations +### Counting Associations -Return the count of current associations +Get the count of current associations, with or without conditions. ```go +// Count all languages db.Model(&user).Association("Languages").Count() // Count with conditions @@ -187,91 +249,79 @@ codes := []string{"zh-CN", "en-US", "ja-JP"} db.Model(&user).Where("code IN ?", codes).Association("Languages").Count() ``` -### Batch Data +### Batch Data Handling + +Association Mode allows you to handle relationships for multiple records in a batch. This includes finding, appending, replacing, deleting, and counting operations for associated data. -Association Mode supports batch data, e.g: +- **Finding Associations**: Retrieve associated data for a collection of records. ```go -// Find all roles for all users db.Model(&users).Association("Role").Find(&roles) +``` -// Delete User A from all user's team +- **Deleting Associations**: Remove specific associations across multiple records. + +```go db.Model(&users).Association("Team").Delete(&userA) +``` -// Get distinct count of all users' teams +- **Counting Associations**: Get the count of associations for a batch of records. + +```go db.Model(&users).Association("Team").Count() +``` -// For `Append`, `Replace` with batch data, the length of the arguments needs to be equal to the data's length or else it will return an error +- **Appending/Replacing Associations**: Manage associations for multiple records. Note the need for matching argument lengths with the data. + +```go var users = []User{user1, user2, user3} -// e.g: we have 3 users, Append userA to user1's team, append userB to user2's team, append userA, userB and userC to user3's team + +// Append different teams to different users in a batch +// Append userA to user1's team, userB to user2's team, and userA, userB, userC to user3's team db.Model(&users).Association("Team").Append(&userA, &userB, &[]User{userA, userB, userC}) -// Reset user1's team to userAļ¼Œreset user2's team to userB, reset user3's team to userA, userB and userC + +// Replace teams for multiple users in a batch +// Reset user1's team to userA, user2's team to userB, and user3's team to userA, userB, and userC db.Model(&users).Association("Team").Replace(&userA, &userB, &[]User{userA, userB, userC}) ``` ## Delete Association Record -By default, `Replace`/`Delete`/`Clear` in `gorm.Association` only delete the reference, -that is, set old associations's foreign key to null. - -You can delete those objects with `Unscoped` (it has nothing to do with `ManyToMany`). +In GORM, the `Replace`, `Delete`, and `Clear` methods in Association Mode primarily affect the foreign key references, not the associated records themselves. Understanding and managing this behavior is crucial for data integrity. -How to delete is decided by `gorm.DB`. +### Default Behavior in Association Deletion -```go -// Soft delete -// UPDATE `languages` SET `deleted_at`= ... -db.Model(&user).Association("Languages").Unscoped().Clear() +- **Reference Update**: These methods update the association's foreign key to null, effectively removing the link between the source and associated models. +- **No Physical Record Deletion**: The actual associated records remain untouched in the database. -// Delete permanently -// DELETE FROM `languages` WHERE ... -db.Unscoped().Model(&item).Association("Languages").Unscoped().Clear() -``` +### Modifying Deletion Behavior with `Unscoped` -## Delete with Select +For scenarios requiring actual deletion of associated records, the `Unscoped` method alters this behavior. -You are allowed to delete selected has one/has many/many2many relations with `Select` when deleting records, for example: +- **Soft Delete**: Marks associated records as deleted (sets `deleted_at` field) without removing them from the database. ```go -// delete user's account when deleting user -db.Select("Account").Delete(&user) - -// delete user's Orders, CreditCards relations when deleting user -db.Select("Orders", "CreditCards").Delete(&user) - -// delete user's has one/many/many2many relations when deleting user -db.Select(clause.Associations).Delete(&user) - -// delete each user's account when deleting users -db.Select("Account").Delete(&users) +db.Model(&user).Association("Languages").Unscoped().Clear() ``` -{% note warn %} -**NOTE:** -Associations will only be deleted if the deleting records's primary key is not zero, GORM will use those primary keys as conditions to delete selected associations +- **Permanent Delete**: Physically deletes the association records from the database. ```go -// DOESN'T WORK -db.Select("Account").Where("name = ?", "jinzhu").Delete(&User{}) -// will delete all user with name `jinzhu`, but those user's account won't be deleted - -db.Select("Account").Where("name = ?", "jinzhu").Delete(&User{ID: 1}) -// will delete the user with name = `jinzhu` and id = `1`, and user `1`'s account will be deleted - -db.Select("Account").Delete(&User{ID: 1}) -// will delete the user with id = `1`, and user `1`'s account will be deleted +// db.Unscoped().Model(&user) +db.Unscoped().Model(&user).Association("Languages").Unscoped().Clear() ``` -{% endnote %} ## Association Tags -| Tag | Description | -| --- | --- | -| foreignKey | Specifies column name of the current model that is used as a foreign key to the join table | -| references | Specifies column name of the reference's table that is mapped to the foreign key of the join table | -| polymorphic | Specifies polymorphic type such as model name | -| polymorphicValue | Specifies polymorphic value, default table name | -| many2many | Specifies join table name | -| joinForeignKey | Specifies foreign key column name of join table that maps to the current table | -| joinReferences | Specifies foreign key column name of join table that maps to the reference's table | -| constraint | Relations constraint, e.g: `OnUpdate`,`OnDelete` | +Association tags in GORM are used to specify how associations between models are handled. These tags define the relationship's details, such as foreign keys, references, and constraints. Understanding these tags is essential for setting up and managing relationships effectively. + +| Tag | Description | +|------------------|-----------------------------------------------------------------------------------------------------| +| `foreignKey` | Specifies the column name of the current model used as a foreign key in the join table. | +| `references` | Indicates the column name in the reference table that the foreign key of the join table maps to. | +| `polymorphic` | Defines the polymorphic type, typically the model name. | +| `polymorphicValue` | Sets the polymorphic value, usually the table name, if not specified otherwise. | +| `many2many` | Names the join table used in a many-to-many relationship. | +| `joinForeignKey` | Identifies the foreign key column in the join table that maps back to the current model's table. | +| `joinReferences` | Points to the foreign key column in the join table that links to the reference model's table. | +| `constraint` | Specifies relational constraints like `OnUpdate`, `OnDelete` for the association. | diff --git a/pages/docs/write_plugins.md b/pages/docs/write_plugins.md index e2d3af0f679..52c96dae9e6 100644 --- a/pages/docs/write_plugins.md +++ b/pages/docs/write_plugins.md @@ -3,15 +3,19 @@ title: Write Plugins layout: page --- +Certainly! Let's delve into the functionality and customization options offered by GORM's Callbacks: + +--- + ## Callbacks -GORM itself is powered by `Callbacks`, it has callbacks for `Create`, `Query`, `Update`, `Delete`, `Row`, `Raw`, you could fully customize GORM with them as you want +GORM leverages `Callbacks` to power its core functionalities. These callbacks provide hooks for various database operations like `Create`, `Query`, `Update`, `Delete`, `Row`, and `Raw`, allowing for extensive customization of GORM's behavior. -Callbacks are registered into the global `*gorm.DB`, not the session-level, if you require `*gorm.DB` with different callbacks, you need to initialize another `*gorm.DB` +Callbacks are registered at the global `*gorm.DB` level, not on a session basis. This means if you need different callback behaviors, you should initialize a separate `*gorm.DB` instance. -### Register Callback +### Registering a Callback -Register a callback into callbacks +You can register a callback for specific operations. For example, to add a custom image cropping functionality: ```go func cropImage(db *gorm.DB) { @@ -60,65 +64,69 @@ func cropImage(db *gorm.DB) { } } +// Register the callback for the Create operation db.Callback().Create().Register("crop_image", cropImage) -// register a callback for Create process ``` -### Delete Callback +### Deleting a Callback -Delete a callback from callbacks +If a callback is no longer needed, it can be removed: ```go +// Remove the 'gorm:create' callback from Create operations db.Callback().Create().Remove("gorm:create") -// delete callback `gorm:create` from Create callbacks ``` -### Replace Callback +### Replacing a Callback -Replace a callback having the same name with the new one +Callbacks with the same name can be replaced with a new function: ```go +// Replace the 'gorm:create' callback with a new function db.Callback().Create().Replace("gorm:create", newCreateFunction) -// replace callback `gorm:create` with new function `newCreateFunction` for Create process ``` -### Register Callback with orders +### Ordering Callbacks -Register callbacks with orders +Callbacks can be registered with specific orders to ensure they execute at the right time in the operation lifecycle. ```go -// before gorm:create +// Register to execute before the 'gorm:create' callback db.Callback().Create().Before("gorm:create").Register("update_created_at", updateCreated) -// after gorm:create +// Register to execute after the 'gorm:create' callback db.Callback().Create().After("gorm:create").Register("update_created_at", updateCreated) -// after gorm:query +// Register to execute after the 'gorm:query' callback db.Callback().Query().After("gorm:query").Register("my_plugin:after_query", afterQuery) -// after gorm:delete +// Register to execute after the 'gorm:delete' callback db.Callback().Delete().After("gorm:delete").Register("my_plugin:after_delete", afterDelete) -// before gorm:update +// Register to execute before the 'gorm:update' callback db.Callback().Update().Before("gorm:update").Register("my_plugin:before_update", beforeUpdate) -// before gorm:create and after gorm:before_create +// Register to execute before 'gorm:create' and after 'gorm:before_create' db.Callback().Create().Before("gorm:create").After("gorm:before_create").Register("my_plugin:before_create", beforeCreate) -// before any other callbacks +// Register to execute before any other callbacks db.Callback().Create().Before("*").Register("update_created_at", updateCreated) -// after any other callbacks +// Register to execute after any other callbacks db.Callback().Create().After("*").Register("update_created_at", updateCreated) ``` -### Defined Callbacks +### Predefined Callbacks + +GORM comes with a set of predefined callbacks that drive its standard features. It's recommended to review these [defined callbacks](https://github.com/go-gorm/gorm/blob/master/callbacks/callbacks.go) before creating custom plugins or additional callback functions. + +## Plugins -GORM has defined [some callbacks](https://github.com/go-gorm/gorm/blob/master/callbacks/callbacks.go) to power current GORM features, check them out before starting your plugins +GORM's plugin system allows for the extension and customization of its core functionalities. Plugins in GORM are integrated using the `Use` method and must conform to the `Plugin` interface. -## Plugin +### The `Plugin` Interface -GORM provides a `Use` method to register plugins, the plugin needs to implement the `Plugin` interface +To create a plugin for GORM, you need to define a struct that implements the `Plugin` interface: ```go type Plugin interface { @@ -127,10 +135,42 @@ type Plugin interface { } ``` -The `Initialize` method will be invoked when registering the plugin into GORM first time, and GORM will save the registered plugins, access them like: +- **`Name` Method**: Returns a unique string identifier for the plugin. +- **`Initialize` Method**: Contains the logic to set up the plugin. This method is called when the plugin is registered with GORM for the first time. + +### Registering a Plugin + +Once your plugin conforms to the `Plugin` interface, you can register it with a GORM instance: ```go -db.Config.Plugins[pluginName] +// Example of registering a plugin +db.Use(MyCustomPlugin{}) ``` -Checkout [Prometheus](prometheus.html) as example +### Accessing Registered Plugins + +After a plugin is registered, it is stored in GORM's configuration. You can access registered plugins via the `Plugins` map: + +```go +// Access a registered plugin by its name +plugin := db.Config.Plugins[pluginName] +``` + +### Practical Example: Prometheus Plugin + +An example of a GORM plugin is the Prometheus plugin, which integrates Prometheus monitoring with GORM: + +```go +// Registering the Prometheus plugin +db.Use(prometheus.New(prometheus.Config{ + // Configuration options here +})) +``` + +[Prometheus plugin documentation](prometheus.html) provides detailed information on its implementation and usage. + +### Benefits of Using Plugins + +- **Extensibility**: Plugins offer a way to enhance GORM's capabilities without modifying its core code. +- **Customization**: They enable customization of GORM's behavior to fit specific application requirements. +- **Modularity**: Plugins promote a modular architecture, making it easier to maintain and update different aspects of the application.