GORM’s method chaining feature allows for a smooth and fluent style of coding. Here are examples using both the Traditional API and the Generics API:
Traditional API
db.Where("name = ?", "jinzhu").Where("age = ?", 18).First(&user) |
Generics API (>= v1.30.0)
ctx := context.Background() |
Both APIs support method chaining, but the Generics API provides enhanced type safety and returns errors directly from operation methods.
Method Categories
GORM organizes methods into three primary categories: Chain Methods
, Finisher Methods
, and New Session Methods
. These categories apply to both the Traditional API and the Generics API.
Chain Methods
Chain methods are used to modify or append Clauses
to the current Statement
. Some common chain methods include:
Where
Select
Omit
Joins
Scopes
Preload
Raw
(Note:Raw
cannot be used in conjunction with other chainable methods to build SQL)
For a comprehensive list, visit GORM Chainable API. Also, the SQL Builder documentation offers more details about Clauses
.
Finisher Methods
Finisher methods are immediate, executing registered callbacks that generate and run SQL commands. This category includes methods:
Create
First
Find
Take
Save
Update
Delete
Scan
Row
Rows
For the full list, refer to GORM Finisher API.
New Session Methods
GORM defines methods like Session
, WithContext
, and Debug
as New Session Methods, which are essential for creating shareable and reusable *gorm.DB
instances. For more details, see Session documentation.
Reusability and Safety
Traditional API
A critical aspect of GORM’s Traditional API is understanding when a *gorm.DB
instance is safe to reuse. Following a Chain Method
or Finisher Method
, GORM returns an initialized *gorm.DB
instance. This instance is not safe for reuse as it may carry over conditions from previous operations, potentially leading to contaminated SQL queries. For example:
Example of Unsafe Reuse
queryDB := DB.Where("name = ?", "jinzhu") |
Example of Safe Reuse
To safely reuse a *gorm.DB
instance, use a New Session Method:
queryDB := DB.Where("name = ?", "jinzhu").Session(&gorm.Session{}) |
In this scenario, using Session(&gorm.Session{})
ensures that each query starts with a fresh context, preventing the pollution of SQL queries with conditions from previous operations. This is crucial for maintaining the integrity and accuracy of your database interactions.
Generics API
One of the significant advantages of GORM’s Generics API is that it inherently addresses the SQL pollution issue. With the Generics API, you don’t need to worry about reusing instances unsafely because:
- The context is passed directly to each operation method
- Errors are returned directly from operation methods
- The generic interface design prevents condition pollution
Here’s an example of how the Generics API handles method chaining safely:
ctx := context.Background() |
The Generics API design significantly reduces the risk of SQL pollution, making your database interactions more reliable and predictable.
Examples for Clarity
Let’s clarify with a few examples using both the Traditional API and the Generics API:
Traditional API Examples
- Example 1: Safe Instance Reuse
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) |
In this example, each chain of method calls is independent, ensuring clean, non-polluted SQL queries.
- (Bad) Example 2: Unsafe Instance Reuse
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) |
In this bad example, reusing the tx
variable leads to compounded conditions, which is generally not desirable.
- Example 3: Safe Reuse with New Session Methods
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) |
In this example, using New Session Methods Session
, WithContext
, Debug
correctly initializes a *gorm.DB
instance for each logical operation, preventing condition pollution and ensuring each query is distinct and based on the specific conditions provided.
Generics API Examples
- Example 4: Method Chaining with Generics API
ctx := context.Background() |
In this example, the Generics API provides type safety while maintaining the fluent method chaining style. The context is passed directly to the finisher methods (First
, Find
), and errors are returned directly from these methods, following Go’s standard error handling pattern.
Overall, these examples illustrate the importance of understanding GORM’s behavior with respect to method chaining and instance management to ensure accurate and efficient database querying. The Generics API offers a more type-safe and less error-prone approach to method chaining compared to the Traditional API.