数据库高级用法¶
本文档介绍 Go Web Frame 中数据库的高级用法。
事务处理¶
基本事务¶
func (s *OrderService) CreateOrder(input *CreateOrderInput) (*Order, error) {
var order *Order
tx := ctx.GetTransaction()
err := tx.Exec(func(tx *db.DB) error {
// 使用 GetReNewModel 在事务中创建模型
orderModel := wf.GetReNewModel[*OrderModel](tx, ctx)
// 步骤 1:创建订单
order = &Order{UserID: input.UserID, Total: input.Total}
if err := orderModel.Save(order); err != nil {
return err
}
// 步骤 2:创建订单项
itemModel := wf.GetReNewModel[*OrderItemModel](tx, ctx)
for _, item := range input.Items {
orderItem := &OrderItem{OrderID: order.Id, ProductID: item.ProductID}
if err := itemModel.Save(orderItem); err != nil {
return err
}
}
return nil
})
return order, err
}
命名事务¶
func (s *UserService) UpdateUserWithTransaction(user *User) error {
tx := ctx.GetTransaction()
return tx.Exec(func(tx *db.DB) error {
userModel := wf.GetReNewModel[*UserModel](tx, ctx)
return userModel.Save(user)
})
}
模型组¶
模型组的核心用途是**支持多数据库**。每个模型组绑定一个独立的数据库连接,同组内的模型共享该连接和事务。
默认模型组¶
使用 builder.Model() 注册模型时,框架自动创建默认模型组,使用配置文件中 web.db 指定的数据库连接:
func main() {
builder := wf.NewBuilder(cfg)
// 注册模型到默认模型组(自动使用 web.db 配置的数据库)
builder.Model(&UserModel{}, &OrderModel{})
app := builder.Build()
app.Start()
}
多数据库模型组¶
当应用需要连接多个数据库时,使用 wf.NewModelGroupBuilder() 创建独立的模型组:
func main() {
cfg, _ := config.LoadSingleFileConfig("application.yml")
builder := wf.NewBuilder(cfg)
// 默认数据库(MySQL)
builder.Model(&UserModel{}, &OrderModel{})
// 第二个数据库(SQLite)
sqliteDB, _ := db.ConnectionSQLite("./logs.db")
logGroup := wf.NewModelGroupBuilder().
Name("log_group").
DB(sqliteDB).
Model(&LogModel{}).
AutoCreateTable(true).
Build()
builder.ModelGroup(logGroup)
// 第三个数据库(另一个 MySQL 实例)
archiveDB, _ := db.ConnectionMysql("archive.internal", 3306, "reader", "pass", "archive", "utf8mb4")
archiveGroup := wf.NewModelGroupBuilder().
Name("archive_group").
DB(archiveDB).
Model(&ArchiveModel{}).
AutoCreateTable(true).
Build()
builder.ModelGroup(archiveGroup)
app := builder.Build()
app.Start()
}
也可通过配置文件创建连接:
// 从配置文件读取 MySQL 配置
var mysqlConfig db.MysqlConfig
cfg.UnmarshalKey("archive_db", &mysqlConfig)
archiveDB, err := mysqlConfig.Connection()
// 从配置文件读取 SQLite 配置
var sqliteConfig db.SQLiteConfig
cfg.UnmarshalKey("log_db", &sqliteConfig)
logDB, err := sqliteConfig.Connection()
// 从配置文件读取 PostgreSQL 配置
var pgConfig db.PostgresConfig
cfg.UnmarshalKey("analytics_db", &pgConfig)
pgDB, err := pgConfig.Connection()
多数据库事务¶
不同模型组的事务是独立的,通过名称区分:
// 默认模型组的事务
tx := ctx.GetTransaction()
err := tx.Exec(func(tx *db.DB) error {
userModel := wf.GetReNewModel[*UserModel](tx, ctx)
return userModel.Save(user)
})
// 指定模型组的事务
logTx := ctx.GetTransactionByName("log_group")
err := logTx.Exec(func(tx *db.DB) error {
logModel := wf.GetReNewModel[*LogModel](tx, ctx)
return logModel.Save(logEntry)
})
// 归档数据库的事务
archiveTx := ctx.GetTransactionByName("archive_group")
err := archiveTx.Exec(func(tx *db.DB) error {
archiveModel := wf.GetReNewModel[*ArchiveModel](tx, ctx)
return archiveModel.Save(archiveRecord)
})
注意:跨模型组不支持分布式事务。如需跨库一致性,请在应用层自行处理补偿逻辑。
动态切换数据库¶
模型组支持运行时切换数据库连接,所有模型会自动重新初始化:
newDB, err := db.ConnectionMysql("new-host", 3306, "user", "pass", "newdb", "utf8mb4")
modelGroup := ctx.GetModelGroup("log_group")
err = modelGroup.SwitchDB(newDB, ctx)
查询构建器高级用法¶
复杂查询¶
// 多条件查询(Where 可多次调用)
users, err := userModel.Query().
Where("status = ?", 1).
Where("age > ?", 18).
Order("create_time desc").
List(10)
关联查询¶
// 预加载关联
user, err := userModel.Query().
Preload("Profile").
Preload("Roles").
Where("id = ?", 1).
One()
原生 SQL¶
// 原生 SQL 查询
var users []User
users, err := userModel.Query().
Exec("SELECT * FROM t_user WHERE status = ?", 1)
自定义数据库驱动¶
框架内置了 MySQL、PostgreSQL 和 SQLite 三种数据库驱动。如果需要使用其他数据库(如 SQL Server、ClickHouse 等),可以通过 db.RegisterDB 注册自定义驱动。
注意:框架底层使用 GORM 作为 ORM 引擎,因此自定义数据库必须有对应的 GORM 驱动支持。可以在 GORM 官方文档 查看支持的数据库列表。
注册自定义驱动¶
// 1. 定义配置结构体,实现 db.IConfig 接口
type ClickHouseConfig struct {
Host string
Port int
Database string
User string
Password string
}
func (c *ClickHouseConfig) Connection() (*db.DB, error) {
// 实现连接逻辑(需要引入对应的 GORM 驱动)
dsn := fmt.Sprintf("clickhouse://%s:%s@%s:%d/%s", c.User, c.Password, c.Host, c.Port, c.Database)
gormDB, err := gorm.Open(clickhouse.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
return &db.DB{DB: gormDB}, nil
}
// 2. 在应用启动前注册
func main() {
db.RegisterDB("clickhouse", &ClickHouseConfig{})
builder := wf.NewBuilder(config.LoadAutoConfig())
app := builder.Build()
app.Start()
}
配置文件¶
注册后,在配置文件中指定 type 为注册的类型名即可:
数据库配置¶
SQLite¶
MySQL¶
web:
db:
type: mysql
host: localhost
port: 3306
database: mydb
user: root
password: your_password
max_open_conns: 100 # 最大打开连接数
max_idle_conns: 10 # 最大空闲连接数
conn_max_lifetime: 3600 # 连接最大生命周期(秒)
PostgreSQL¶
web:
db:
type: postgres
host: localhost
port: 5432
database: mydb
user: postgres
password: your_password
max_open_conns: 100 # 最大打开连接数
max_idle_conns: 10 # 最大空闲连接数
conn_max_lifetime: 3600 # 连接最大生命周期(秒)
连接池配置¶
在 YAML 配置文件中设置连接池参数:
web:
db:
type: mysql
host: localhost
port: 3306
database: mydb
user: root
password: your_password
max_open_conns: 100 # 最大打开连接数
max_idle_conns: 10 # 最大空闲连接数
conn_max_lifetime: 3600 # 连接最大生命周期(秒)
数据库迁移¶
自动迁移¶
func (m *UserModel) Init(db *db.DB, ctx *core.Context) error {
m.Model = model.NewModel[entity.User](db, "t_user")
// 自动迁移表结构
return m.AutoMigrate()
}
手动迁移¶
func (m *UserModel) Init(db *db.DB, ctx *core.Context) error {
m.Model = model.NewModel[entity.User](db, "t_user")
// 检查表是否存在
exists, err := m.IsExist()
if err != nil {
return err
}
// 如果不存在则创建
if !exists {
return m.CreateTable()
}
return nil
}