Skip to content

Commit

Permalink
Update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
jinzhu committed Dec 23, 2023
1 parent 9df1827 commit 2f9a5a0
Show file tree
Hide file tree
Showing 8 changed files with 220 additions and 236 deletions.
193 changes: 66 additions & 127 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
},
"dependencies": {
"cheerio": "^1.0.0-rc.10",
"hexo": "^6.0.0",
"hexo": "^6.3.0",
"hexo-generator-archive": "^1.0.0",
"hexo-generator-category": "^1.0.0",
"hexo-generator-index": "^2.0.0",
Expand All @@ -27,4 +27,4 @@
"lodash": "^4.17.21",
"lunr": "^2.3.9"
}
}
}
54 changes: 23 additions & 31 deletions pages/docs/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,29 @@ title: Context
layout: page
---

GORM provides Context support, you can use it with method `WithContext`
GORM's context support, enabled by the `WithContext` method, is a powerful feature that enhances the flexibility and control of database operations in Go applications. It allows for context management across different operational modes, timeout settings, and even integration into hooks/callbacks and middlewares. Let's delve into these various aspects:

## Single Session Mode
### Single Session Mode

Single session mode usually used when you want to perform a single operation
Single session mode is appropriate for executing individual operations. It ensures that the specific operation is executed within the context's scope, allowing for better control and monitoring.

```go
db.WithContext(ctx).Find(&users)
```

## Continuous session mode
### Continuous Session Mode

Continuous session mode is usually used when you want to perform a group of operations, for example:
Continuous session mode is ideal for performing a series of related operations. It maintains the context across these operations, which is particularly useful in scenarios like transactions.

```go
tx := db.WithContext(ctx)
tx.First(&user, 1)
tx.Model(&user).Update("role", "admin")
```

## Context timeout
### Context Timeout

You can pass in a context with a timeout to `db.WithContext` to set timeout for long running queries, for example:
Setting a timeout on the context passed to `db.WithContext` can control the duration of long-running queries. This is crucial for maintaining performance and avoiding resource lock-ups in database interactions.

```go
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
Expand All @@ -34,23 +34,21 @@ defer cancel()
db.WithContext(ctx).Find(&users)
```

## Context in Hooks/Callbacks
### Context in Hooks/Callbacks

You can access the `Context` object from the current `Statement`, for example:
The context can also be accessed within GORM's hooks/callbacks. This enables contextual information to be used during these lifecycle events.

```go
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
ctx := tx.Statement.Context
// ...
// ... use context
return
}
```

## Chi Middleware Example
### Integration with Chi Middleware

Continuous session mode which might be helpful when handling API requests, for example, you can set up `*gorm.DB` with Timeout Context in middlewares, and then use the `*gorm.DB` when processing all requests

Following is a Chi middleware example:
GORM's context support extends to web server middlewares, such as those in the Chi router. This allows setting a context with a timeout for all database operations within the scope of a web request.

```go
func SetDBMiddleware(next http.Handler) http.Handler {
Expand All @@ -61,32 +59,26 @@ func SetDBMiddleware(next http.Handler) http.Handler {
})
}

// Router setup
r := chi.NewRouter()
r.Use(SetDBMiddleware)

// Route handlers
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
db, ok := ctx.Value("DB").(*gorm.DB)

var users []User
db.Find(&users)

// lots of db operations
db, ok := r.Context().Value("DB").(*gorm.DB)
// ... db operations
})

r.Get("/user", func(w http.ResponseWriter, r *http.Request) {
db, ok := ctx.Value("DB").(*gorm.DB)

var user User
db.First(&user)

// lots of db operations
db, ok := r.Context().Value("DB").(*gorm.DB)
// ... db operations
})
```

{% note %}
**NOTE** Setting `Context` with `WithContext` is goroutine-safe, refer [Session](session.html) for details
{% endnote %}
**Note**: Setting the `Context` with `WithContext` is goroutine-safe. This ensures that database operations are safely managed across multiple goroutines. For more details, refer to the [Session documentation](session.html) in GORM.

### Logger Integration

## Logger
GORM's logger also accepts `Context`, which can be used for log tracking and integrating with existing logging infrastructures.

Logger accepts `Context` too, you can use it for log tracking, refer [Logger](logger.html) for details
Refer to [Logger documentation](logger.html) for more details.
87 changes: 69 additions & 18 deletions pages/docs/error_handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,101 @@ title: Error Handling
layout: page
---

In Go, error handling is important.
Effective error handling is a cornerstone of robust application development in Go, particularly when interacting with databases using GORM. GORM's approach to error handling, influenced by its chainable API, requires a nuanced understanding.

You are encouraged to do error check after any [Finisher Methods](method_chaining.html#finisher_method)
## Basic Error Handling

## Error Handling
GORM integrates error handling into its chainable method syntax. The `*gorm.DB` instance contains an `Error` field, which is set when an error occurs. The common practice is to check this field after executing database operations, especially after [Finisher Methods](method_chaining.html#finisher_method).

Error handling in GORM is different than idiomatic Go code because of its chainable API.

If any error occurs, GORM will set `*gorm.DB`'s `Error` field, you need to check it like this:
After a chain of methods, it's crucial to check the `Error` field:

```go
if err := db.Where("name = ?", "jinzhu").First(&user).Error; err != nil {
// error handling...
// Handle error...
}
```

Or
Or alternatively:

```go
if result := db.Where("name = ?", "jinzhu").First(&user); result.Error != nil {
// error handling...
// Handle error...
}
```

## `ErrRecordNotFound`

GORM returns `ErrRecordNotFound` when no record is found using methods like `First`, `Last`, `Take`.

```go
err := db.First(&user, 100).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
// Handle record not found error...
}
```

## Handling Error Codes

Many databases return errors with specific codes, which can be indicative of various issues like constraint violations, connection problems, or syntax errors. Handling these error codes in GORM requires parsing the error returned by the database and extracting the relevant code

- **Example: Handling MySQL Error Codes**

```go
import (
"github.com/go-sql-driver/mysql"
"gorm.io/gorm"
)

// ...

result := db.Create(&newRecord)
if result.Error != nil {
if mysqlErr, ok := result.Error.(*mysql.MySQLError); ok {
switch mysqlErr.Number {
case 1062: // MySQL code for duplicate entry
// Handle duplicate entry
// Add cases for other specific error codes
default:
// Handle other errors
}
} else {
// Handle non-MySQL errors or unknown errors
}
}
```

{% note warn %}
## Dialect Translated Errors

If you would like to be able to use the dialect translated errors(like ErrDuplicatedKey), then enable the `TranslateError` flag when opening a db connection.
GORM can return specific errors related to the database dialect being used, when `TranslateError` is enabled, GORM converts database-specific errors into its own generalized errors.

```go
db, err := gorm.Open(postgres.Open(postgresDSN), &gorm.Config{TranslateError: true})
```
{% endnote %}

- **ErrDuplicatedKey**

This error occurs when an insert operation violates a unique constraint:

```go
result := db.Create(&newRecord)
if errors.Is(result.Error, gorm.ErrDuplicatedKey) {
// Handle duplicated key error...
}
```

## ErrRecordNotFound
- **ErrForeignKeyViolated**

GORM returns `ErrRecordNotFound` when failed to find data with `First`, `Last`, `Take` (only when dialect translated errors are enabled). If there are several errors happened, you can check the `ErrRecordNotFound` error with `errors.Is`. For example,
This error is encountered when a foreign key constraint is violated:

```go
// Check if returns RecordNotFound error
err := db.First(&user, 100).Error
errors.Is(err, gorm.ErrRecordNotFound)
result := db.Create(&newRecord)
if errors.Is(result.Error, gorm.ErrForeignKeyViolated) {
// Handle foreign key violation error...
}
```

By enabling `TranslateError`, GORM provides a more unified way of handling errors across different databases, translating database-specific errors into common GORM error types.

## Errors

[Errors List](https://github.com/go-gorm/gorm/blob/master/errors.go)
For a complete list of errors that GORM can return, refer to the [Errors List](https://github.com/go-gorm/gorm/blob/master/errors.go) in GORM's documentation.
10 changes: 5 additions & 5 deletions pages/docs/method_chaining.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ For the full list, refer to [GORM Finisher API](https://github.com/go-gorm/gorm/

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](session.html) documentation.

## Understanding the Reusability and Safety of `*gorm.DB` Instances
## Reusability and Safety

A critical aspect of GORM 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:

Expand All @@ -66,7 +66,7 @@ queryDB.Where("age > ?", 20).First(&user2)
// SQL: SELECT * FROM users WHERE name = "jinzhu" AND age > 10 AND age > 20
```

### Using New Session Methods for Safe Reuse
### Example of Safe Reuse

To safely reuse a `*gorm.DB` instance, use a New Session Method:

Expand All @@ -88,7 +88,7 @@ In this scenario, using `Session(&gorm.Session{})` ensures that each query start

Let's clarify with a few examples:

### Example 1: Safe Instance Reuse
- **Example 1: Safe Instance Reuse**

```go
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
Expand All @@ -113,7 +113,7 @@ db.Find(&users)

In this example, each chain of method calls is independent, ensuring clean, non-polluted SQL queries.

### (Bad) Example 2: Unsafe Instance Reuse
- **(Bad) Example 2: Unsafe Instance Reuse**

```go
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
Expand All @@ -135,7 +135,7 @@ tx.Where("age = ?", 28).Find(&users)

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
- **Example 3: Safe Reuse with New Session Methods**

```go
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
Expand Down
6 changes: 3 additions & 3 deletions pages/docs/write_driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ GORM offers built-in support for popular databases like `SQLite`, `MySQL`, `Post

For databases that closely resemble the behavior of `MySQL` or `Postgres`, you can often use the respective dialects directly. However, if your database significantly deviates from these dialects or offers additional features, developing a custom driver is recommended.

## Implementing the Dialector Interface
## Implementing the Dialector

The `Dialector` interface in GORM consists of methods that a database driver must implement to facilitate communication between the database and GORM. Let's break down the key methods:

Expand All @@ -28,9 +28,9 @@ type Dialector interface {

Each method in this interface serves a crucial role in how GORM interacts with the database, from establishing connections to handling queries and migrations.

### SavePoint / Nested Transaction Support
### Nested Transaction Support

If your database supports savepoints or nested transactions, you can implement the `SavePointerDialectorInterface`.
If your database supports savepoints, you can implement the `SavePointerDialectorInterface` to get the `Nested Transaction Support` and `SavePoint` support.

```go
type SavePointerDialectorInterface interface {
Expand Down
2 changes: 1 addition & 1 deletion pages/docs/write_plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ After a plugin is registered, it is stored in GORM's configuration. You can acce
plugin := db.Config.Plugins[pluginName]
```

### Practical Example: Prometheus Plugin
### Practical Example

An example of a GORM plugin is the Prometheus plugin, which integrates Prometheus monitoring with GORM:

Expand Down
Loading

0 comments on commit 2f9a5a0

Please sign in to comment.