Skip to content

Commit 70778a5

Browse files
author
武斌
committed
fix
1 parent aef6861 commit 70778a5

14 files changed

Lines changed: 5472 additions & 0 deletions

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ require (
5050
github.com/wubin1989/gorm v0.0.5
5151
github.com/wubin1989/gorm-dameng v0.5.1
5252
github.com/wubin1989/mysql v0.0.2
53+
github.com/wubin1989/sqlite v0.0.3
5354
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
5455
go.uber.org/mock v0.4.0
5556
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,8 @@ github.com/wubin1989/mysql v0.0.2 h1:OgJ3LS6LfCBtWboQ2ghw6UXETMWyNb2fe2MLBELIpDg
672672
github.com/wubin1989/mysql v0.0.2/go.mod h1:X6kei/oCZkghaulJzYjIEnFQLe59WYFMczj4hDObDN8=
673673
github.com/wubin1989/postgres v0.0.2 h1:skgTBzvhXouB8sk8QW3CIL12IHc4yjxp0HFlmQWk+w0=
674674
github.com/wubin1989/postgres v0.0.2/go.mod h1:ak4xecaBWCvjryICTXlDtHwkK8QV1ibtBF/d2YzI+lw=
675+
github.com/wubin1989/sqlite v0.0.3 h1:HpeLMcNQ0pFHCk3QNAYjYUt6OpZiWykBe1o5FC6NxTg=
676+
github.com/wubin1989/sqlite v0.0.3/go.mod h1:sJwi+4OqdP6KL56LUuJsqeubNREEpNpj+/Hp3YZ9zQI=
675677
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
676678
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
677679
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=

unitofwork/README.md

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
# 工作单元模式 GORM 插件
2+
3+
这是一个为 GORM 设计的工作单元模式插件,提供了完整的工作单元模式实现,包括自动事务管理、实体跟踪、依赖关系管理、脏检查等功能。
4+
5+
## 功能特性
6+
7+
-**自动事务管理**: 自动管理数据库事务的开始、提交和回滚
8+
-**实体跟踪**: 自动跟踪实体的创建、更新和删除操作
9+
-**依赖关系管理**: 基于实体依赖关系自动排序操作执行顺序
10+
-**脏检查**: 自动检测实体变更,只更新发生变化的字段
11+
-**批量优化**: 自动合并和优化数据库操作
12+
-**乐观锁**: 支持乐观锁并发控制
13+
-**软删除**: 支持软删除机制
14+
-**并发安全**: 线程安全的实现
15+
-**上下文管理**: 完善的上下文传递和管理
16+
-**详细日志**: 可配置的详细操作日志
17+
18+
## 快速开始
19+
20+
### 1. 安装插件
21+
22+
```go
23+
import "github.com/unionj-cloud/toolkit/unitofwork"
24+
```
25+
26+
### 2. 定义实体
27+
28+
实体需要实现 `Entity` 接口,推荐继承 `BaseEntity`
29+
30+
```go
31+
type User struct {
32+
unitofwork.BaseEntity
33+
Name string `gorm:"size:100;not null" json:"name"`
34+
Email string `gorm:"size:255;uniqueIndex" json:"email"`
35+
Age int `json:"age"`
36+
}
37+
38+
func (u *User) GetTableName() string {
39+
return "users"
40+
}
41+
42+
func (u *User) Validate() error {
43+
if u.Name == "" {
44+
return fmt.Errorf("用户名不能为空")
45+
}
46+
if u.Email == "" {
47+
return fmt.Errorf("邮箱不能为空")
48+
}
49+
return nil
50+
}
51+
```
52+
53+
### 3. 注册插件
54+
55+
```go
56+
// 创建数据库连接
57+
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
58+
if err != nil {
59+
panic("数据库连接失败")
60+
}
61+
62+
// 注册工作单元插件
63+
plugin := unitofwork.NewPlugin()
64+
if err := db.Use(plugin); err != nil {
65+
panic("插件注册失败")
66+
}
67+
```
68+
69+
### 4. 使用工作单元
70+
71+
```go
72+
err := unitofwork.WithUnitOfWork(context.Background(), db, func(db *gorm.DB, uow *unitofwork.UnitOfWork) error {
73+
// 创建用户
74+
user := &User{
75+
Name: "张三",
76+
Email: "zhangsan@example.com",
77+
Age: 25,
78+
}
79+
if err := db.Create(user).Error; err != nil {
80+
return err
81+
}
82+
83+
// 创建文章
84+
post := &Post{
85+
Title: "我的第一篇文章",
86+
Content: "文章内容...",
87+
UserID: user.GetID(),
88+
}
89+
return db.Create(post).Error
90+
})
91+
```
92+
93+
## 配置选项
94+
95+
### 插件配置
96+
97+
```go
98+
plugin := unitofwork.NewPlugin(
99+
// 启用/禁用自动管理
100+
unitofwork.WithPluginAutoManage(true),
101+
102+
// 自定义上下文键名
103+
unitofwork.WithPluginContextKey("my_uow"),
104+
105+
// 配置工作单元选项
106+
unitofwork.WithPluginUnitOfWorkConfig(&unitofwork.Config{
107+
EnableDirtyCheck: true, // 启用脏检查
108+
BatchSize: 1000, // 批量操作大小
109+
EnableOperationMerge: true, // 启用操作合并
110+
MaxEntityCount: 5000, // 最大实体数量
111+
EnableDetailLog: false, // 启用详细日志
112+
}),
113+
114+
// 配置实体依赖关系
115+
unitofwork.WithPluginDependencyMapping(map[reflect.Type][]reflect.Type{
116+
reflect.TypeOf(&Post{}): {reflect.TypeOf(&User{})}, // Post 依赖于 User
117+
reflect.TypeOf(&Tag{}): {reflect.TypeOf(&User{})}, // Tag 依赖于 User
118+
}),
119+
)
120+
```
121+
122+
### 工作单元配置
123+
124+
```go
125+
uow := unitofwork.NewUnitOfWork(db,
126+
unitofwork.WithDirtyCheck(true), // 启用脏检查
127+
unitofwork.WithBatchSize(500), // 批量大小
128+
unitofwork.WithOperationMerge(true), // 操作合并
129+
unitofwork.WithMaxEntityCount(1000), // 实体数量限制
130+
unitofwork.WithDetailLog(true), // 详细日志
131+
)
132+
```
133+
134+
## 高级用法
135+
136+
### 依赖关系管理
137+
138+
```go
139+
// 配置实体依赖关系
140+
dependencyMapping := map[reflect.Type][]reflect.Type{
141+
reflect.TypeOf(&Post{}): {reflect.TypeOf(&User{})},
142+
reflect.TypeOf(&Tag{}): {reflect.TypeOf(&User{})},
143+
}
144+
145+
plugin := unitofwork.NewPlugin(
146+
unitofwork.WithPluginDependencyMapping(dependencyMapping),
147+
)
148+
149+
// 使用时,操作会自动按依赖关系排序
150+
err := unitofwork.WithUnitOfWork(ctx, db, func(db *gorm.DB, uow *unitofwork.UnitOfWork) error {
151+
// 即使先创建 Post,实际执行时会先创建 User
152+
post := &Post{Title: "文章", Content: "内容", UserID: 1}
153+
db.Create(post)
154+
155+
user := &User{Name: "作者", Email: "author@example.com", Age: 30}
156+
user.ID = 1
157+
db.Create(user)
158+
159+
return nil
160+
})
161+
```
162+
163+
### 脏检查
164+
165+
```go
166+
// 启用脏检查后,只有实际发生变化的字段才会被更新
167+
err := unitofwork.WithUnitOfWork(ctx, db, func(db *gorm.DB, uow *unitofwork.UnitOfWork) error {
168+
// 加载实体并创建快照
169+
var user User
170+
db.First(&user, 1)
171+
uow.TakeSnapshot(&user)
172+
173+
// 修改实体
174+
user.Age = 26 // 只有这个字段会被更新
175+
176+
return db.Save(&user).Error
177+
})
178+
```
179+
180+
### 手动工作单元管理
181+
182+
```go
183+
// 禁用自动管理,手动控制工作单元
184+
plugin := unitofwork.NewPlugin(unitofwork.WithPluginAutoManage(false))
185+
db.Use(plugin)
186+
187+
// 手动创建工作单元
188+
uow := unitofwork.NewUnitOfWork(db)
189+
190+
// 手动注册实体
191+
user := &User{Name: "手动用户", Email: "manual@example.com", Age: 25}
192+
if err := uow.Create(user); err != nil {
193+
return err
194+
}
195+
196+
// 手动提交
197+
if err := uow.Commit(); err != nil {
198+
return err
199+
}
200+
```
201+
202+
### 错误处理和回滚
203+
204+
```go
205+
err := unitofwork.WithUnitOfWork(ctx, db, func(db *gorm.DB, uow *unitofwork.UnitOfWork) error {
206+
// 创建用户
207+
user := &User{Name: "用户", Email: "user@example.com", Age: 25}
208+
if err := db.Create(user).Error; err != nil {
209+
return err
210+
}
211+
212+
// 模拟业务逻辑错误
213+
if someCondition {
214+
return errors.New("业务逻辑错误")
215+
}
216+
217+
return nil
218+
})
219+
220+
if err != nil {
221+
// 发生错误时,所有操作都会自动回滚
222+
fmt.Printf("操作失败: %v", err)
223+
}
224+
```
225+
226+
## 实体接口
227+
228+
### 基础接口
229+
230+
```go
231+
type Entity interface {
232+
GetID() uint
233+
SetID(id uint)
234+
GetTableName() string
235+
IsNew() bool
236+
}
237+
```
238+
239+
### 扩展接口
240+
241+
```go
242+
// 乐观锁支持
243+
type HasRevision interface {
244+
Entity
245+
GetRevision() int64
246+
SetRevision(revision int64)
247+
GetRevisionNext() int64
248+
}
249+
250+
// 时间戳支持
251+
type HasTimestamps interface {
252+
Entity
253+
GetCreatedAt() time.Time
254+
SetCreatedAt(createdAt time.Time)
255+
GetUpdatedAt() time.Time
256+
SetUpdatedAt(updatedAt time.Time)
257+
}
258+
259+
// 软删除支持
260+
type SoftDelete interface {
261+
Entity
262+
GetDeletedAt() gorm.DeletedAt
263+
SetDeletedAt(deletedAt gorm.DeletedAt)
264+
IsDeleted() bool
265+
}
266+
267+
// 验证支持
268+
type Validatable interface {
269+
Entity
270+
Validate() error
271+
}
272+
```
273+
274+
## 性能优化
275+
276+
1. **批量操作**: 自动将相同类型的操作合并为批量操作
277+
2. **操作优化**: 移除冗余操作(如创建后立即删除)
278+
3. **依赖排序**: 按依赖关系优化执行顺序
279+
4. **内存保护**: 可配置的实体数量限制
280+
5. **连接池**: 复用数据库连接
281+
282+
## 最佳实践
283+
284+
1. **实体设计**: 继承 `BaseEntity` 并实现必要的接口
285+
2. **依赖配置**: 提前配置好实体间的依赖关系
286+
3. **错误处理**: 适当处理验证错误和业务逻辑错误
287+
4. **批量大小**: 根据数据量调整合适的批量大小
288+
5. **日志配置**: 在开发环境启用详细日志,生产环境关闭
289+
6. **并发控制**: 利用乐观锁处理并发更新
290+
291+
## 注意事项
292+
293+
1. 实体必须实现 `Entity` 接口才能被工作单元管理
294+
2. 启用自动管理时,GORM 的默认 CRUD 操作会被拦截
295+
3. 依赖关系配置应该在插件初始化时完成
296+
4. 脏检查会增加内存使用,大量数据时需要注意
297+
5. 工作单元是线程安全的,但建议每个请求使用独立的工作单元
298+
299+
## 许可证
300+
301+
MIT License

0 commit comments

Comments
 (0)