GF框架直接使用SQL语句查询数据库的指南
在GF框架中,我们既可以使用ORM进行便捷的数据库操作,也可以直接使用原生SQL语句来处理复杂的查询需求。本文将详细介绍如何在GF框架中直接使用SQL语句进行数据库查询。
基本用法
1. 直接执行SQL查询
package mainimport ("context""fmt""github.com/gogf/gf/v2/frame/g"
)// 定义数据模型
type ItemOrderInfo struct {OrderId  string `json:"orderId" orm:"order_id" dc:"订单ID"`ItemName string `json:"itemName" orm:"item_name" dc:"商品名称"`
}func main() {ctx := context.Background()var resultsFromSecondDB []ItemOrderInfoerr := g.DB("mysql").Ctx(ctx).Raw("SELECT * FROM rt_itemorder_info WHERE order_id = ?", "12345",).Scan(&resultsFromSecondDB)if err != nil {panic("查询售后订单失败: " + err.Error())}fmt.Printf("查询结果: %+v\n", resultsFromSecondDB)
}
2. 使用命名参数
// 使用命名参数的方式
func queryWithNamedParams() {ctx := context.Background()var results []ItemOrderInfoerr := g.DB("mysql").Ctx(ctx).Raw("SELECT * FROM rt_itemorder_info WHERE order_id = @orderId AND item_name LIKE @itemName",g.Map{"orderId":  "12345","itemName": "%手机%",},).Scan(&results)if err != nil {g.Log().Error(ctx, "查询失败:", err)return}g.Dump(results)
}
高级用法
3. 多数据库连接配置
首先在配置文件中配置多个数据库连接:
# config.yaml
database:default: "mysql"connections:mysql:link: "mysql:root:123456@tcp(127.0.0.1:3306)/test"debug: truesecondDB:link: "mysql:root:123456@tcp(127.0.0.1:3306)/second_db"debug: true
然后使用指定的数据库连接:
func queryFromDifferentDB() {ctx := context.Background()// 使用默认数据库var defaultResults []ItemOrderInfoerr := g.DB().Ctx(ctx).Raw("SELECT * FROM rt_itemorder_info").Scan(&defaultResults)if err != nil {g.Log().Error(ctx, err)}// 使用指定的第二个数据库var secondDBResults []ItemOrderInfoerr = g.DB("secondDB").Ctx(ctx).Raw("SELECT * FROM rt_itemorder_info WHERE order_id = ?", "12345",).Scan(&secondDBResults)if err != nil {panic("查询售后订单失败: " + err.Error())}
}
4. 复杂查询示例
// 复杂SQL查询
func complexQuery() {ctx := context.Background()type OrderDetail struct {OrderId    string    `json:"orderId" orm:"order_id"`ItemName   string    `json:"itemName" orm:"item_name"`TotalAmount float64   `json:"totalAmount" orm:"total_amount"`CreateTime time.Time `json:"createTime" orm:"create_time"`}var orderDetails []OrderDetailsql := `SELECT o.order_id,o.item_name,o.total_amount,o.create_timeFROM rt_itemorder_info oWHERE o.create_time BETWEEN ? AND ?AND o.total_amount > ?ORDER BY o.create_time DESCLIMIT 100`startTime := "2024-01-01 00:00:00"endTime := "2024-12-31 23:59:59"minAmount := 100.0err := g.DB("mysql").Ctx(ctx).Raw(sql, startTime, endTime, minAmount).Scan(&orderDetails)if err != nil {g.Log().Errorf(ctx, "复杂查询失败: %v", err)return}fmt.Printf("找到 %d 条订单记录\n", len(orderDetails))
}
5. 事务中的原生SQL
// 在事务中使用原生SQL
func transactionWithRawSQL() error {ctx := context.Background()return g.DB("mysql").Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {// 查询订单var orderInfo ItemOrderInfoerr := tx.Raw("SELECT * FROM rt_itemorder_info WHERE order_id = ? FOR UPDATE", "12345").Scan(&orderInfo)if err != nil {return err}// 更新订单状态_, err = tx.Raw("UPDATE rt_itemorder_info SET item_name = ? WHERE order_id = ?","更新后的商品名称","12345",).Exec()if err != nil {return err}// 插入日志_, err = tx.Raw("INSERT INTO order_operation_log (order_id, operation, operate_time) VALUES (?, ?, ?)","12345","修改商品名称",time.Now(),).Exec()return err})
}
错误处理最佳实践
// 更健壮的错误处理
func robustQuery(req *Request) ([]ItemOrderInfo, error) {ctx := context.Background()var results []ItemOrderInfoif req.ProductNameAndCode == "" {return nil, fmt.Errorf("产品名称和编码不能为空")}err := g.DB("mysql").Ctx(ctx).Raw("SELECT order_id, item_name FROM rt_itemorder_info WHERE order_id = ?", req.ProductNameAndCode,).Scan(&results)if err != nil {g.Log().Errorf(ctx, "查询订单失败, 参数: %s, 错误: %v", req.ProductNameAndCode, err)return nil, fmt.Errorf("查询售后订单失败: %w", err)}if len(results) == 0 {g.Log().Warningf(ctx, "未找到订单记录, 参数: %s", req.ProductNameAndCode)return nil, nil}return results, nil
}type Request struct {ProductNameAndCode string `json:"productNameAndCode"`
}
性能优化建议
6. 使用预处理语句
// 预处理语句提高性能
func preparedStatementQuery() {ctx := context.Background()// GF框架的Raw方法默认使用预处理语句,无需额外配置var results []ItemOrderInfoerr := g.DB("mysql").Ctx(ctx).Raw("SELECT * FROM rt_itemorder_info WHERE order_id = ? AND item_name LIKE ?","12345","%手机%",).Scan(&results)if err != nil {g.Log().Error(ctx, err)}
}
7. 分页查询
// 分页查询
func paginationQuery(page, pageSize int) ([]ItemOrderInfo, int, error) {ctx := context.Background()var results []ItemOrderInfooffset := (page - 1) * pageSize// 查询数据err := g.DB("mysql").Ctx(ctx).Raw("SELECT * FROM rt_itemorder_info ORDER BY create_time DESC LIMIT ? OFFSET ?",pageSize, offset,).Scan(&results)if err != nil {return nil, 0, err}// 查询总数var total interr = g.DB("mysql").Ctx(ctx).Raw("SELECT COUNT(*) FROM rt_itemorder_info").Scan(&total)if err != nil {return nil, 0, err}return results, total, nil
}
总结
GF框架提供了灵活的原生SQL支持,通过Raw()方法我们可以:
- 直接执行任意SQL语句
- 使用参数绑定防止SQL注入
- 支持多数据库连接
- 在事务中使用原生SQL
- 享受GF框架的连接池和错误处理优势
使用原生SQL时需要注意:
- 始终使用参数绑定来防止SQL注入
- 合理处理错误,避免直接panic
- 在复杂业务场景中考虑使用事务
- 注意SQL语句的性能优化
这种方式特别适合复杂查询、报表生成、数据迁移等ORM难以处理的场景。# GF框架直接使用SQL语句查询数据库的完整指南
在GF框架中,我们既可以使用ORM进行便捷的数据库操作,也可以直接使用原生SQL语句来处理复杂的查询需求。本文将详细介绍如何在GF框架中直接使用SQL语句进行数据库查询。
基本用法
1. 直接执行SQL查询
package mainimport ("context""fmt""github.com/gogf/gf/v2/frame/g"
)// 定义数据模型
type ItemOrderInfo struct {OrderId  string `json:"orderId" orm:"order_id" dc:"订单ID"`ItemName string `json:"itemName" orm:"item_name" dc:"商品名称"`
}func main() {ctx := context.Background()var resultsFromSecondDB []ItemOrderInfoerr := g.DB("mysql").Ctx(ctx).Raw("SELECT * FROM rt_itemorder_info WHERE order_id = ?", "12345",).Scan(&resultsFromSecondDB)if err != nil {panic("查询售后订单失败: " + err.Error())}fmt.Printf("查询结果: %+v\n", resultsFromSecondDB)
}
2. 使用命名参数
// 使用命名参数的方式
func queryWithNamedParams() {ctx := context.Background()var results []ItemOrderInfoerr := g.DB("mysql").Ctx(ctx).Raw("SELECT * FROM rt_itemorder_info WHERE order_id = @orderId AND item_name LIKE @itemName",g.Map{"orderId":  "12345","itemName": "%手机%",},).Scan(&results)if err != nil {g.Log().Error(ctx, "查询失败:", err)return}g.Dump(results)
}
高级用法
3. 多数据库连接配置
首先在配置文件中配置多个数据库连接:
# config.yaml
database:default: "mysql"connections:mysql:link: "mysql:root:123456@tcp(127.0.0.1:3306)/test"debug: truesecondDB:link: "mysql:root:123456@tcp(127.0.0.1:3306)/second_db"debug: true
然后使用指定的数据库连接:
func queryFromDifferentDB() {ctx := context.Background()// 使用默认数据库var defaultResults []ItemOrderInfoerr := g.DB().Ctx(ctx).Raw("SELECT * FROM rt_itemorder_info").Scan(&defaultResults)if err != nil {g.Log().Error(ctx, err)}// 使用指定的第二个数据库var secondDBResults []ItemOrderInfoerr = g.DB("secondDB").Ctx(ctx).Raw("SELECT * FROM rt_itemorder_info WHERE order_id = ?", "12345",).Scan(&secondDBResults)if err != nil {panic("查询售后订单失败: " + err.Error())}
}
4. 复杂查询示例
// 复杂SQL查询
func complexQuery() {ctx := context.Background()type OrderDetail struct {OrderId    string    `json:"orderId" orm:"order_id"`ItemName   string    `json:"itemName" orm:"item_name"`TotalAmount float64   `json:"totalAmount" orm:"total_amount"`CreateTime time.Time `json:"createTime" orm:"create_time"`}var orderDetails []OrderDetailsql := `SELECT o.order_id,o.item_name,o.total_amount,o.create_timeFROM rt_itemorder_info oWHERE o.create_time BETWEEN ? AND ?AND o.total_amount > ?ORDER BY o.create_time DESCLIMIT 100`startTime := "2024-01-01 00:00:00"endTime := "2024-12-31 23:59:59"minAmount := 100.0err := g.DB("mysql").Ctx(ctx).Raw(sql, startTime, endTime, minAmount).Scan(&orderDetails)if err != nil {g.Log().Errorf(ctx, "复杂查询失败: %v", err)return}fmt.Printf("找到 %d 条订单记录\n", len(orderDetails))
}
5. 事务中的原生SQL
// 在事务中使用原生SQL
func transactionWithRawSQL() error {ctx := context.Background()return g.DB("mysql").Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {// 查询订单var orderInfo ItemOrderInfoerr := tx.Raw("SELECT * FROM rt_itemorder_info WHERE order_id = ? FOR UPDATE", "12345").Scan(&orderInfo)if err != nil {return err}// 更新订单状态_, err = tx.Raw("UPDATE rt_itemorder_info SET item_name = ? WHERE order_id = ?","更新后的商品名称","12345",).Exec()if err != nil {return err}// 插入日志_, err = tx.Raw("INSERT INTO order_operation_log (order_id, operation, operate_time) VALUES (?, ?, ?)","12345","修改商品名称",time.Now(),).Exec()return err})
}
错误处理最佳实践
// 更健壮的错误处理
func robustQuery(req *Request) ([]ItemOrderInfo, error) {ctx := context.Background()var results []ItemOrderInfoif req.ProductNameAndCode == "" {return nil, fmt.Errorf("产品名称和编码不能为空")}err := g.DB("mysql").Ctx(ctx).Raw("SELECT order_id, item_name FROM rt_itemorder_info WHERE order_id = ?", req.ProductNameAndCode,).Scan(&results)if err != nil {g.Log().Errorf(ctx, "查询订单失败, 参数: %s, 错误: %v", req.ProductNameAndCode, err)return nil, fmt.Errorf("查询售后订单失败: %w", err)}if len(results) == 0 {g.Log().Warningf(ctx, "未找到订单记录, 参数: %s", req.ProductNameAndCode)return nil, nil}return results, nil
}type Request struct {ProductNameAndCode string `json:"productNameAndCode"`
}
性能优化建议
6. 使用预处理语句
// 预处理语句提高性能
func preparedStatementQuery() {ctx := context.Background()// GF框架的Raw方法默认使用预处理语句,无需额外配置var results []ItemOrderInfoerr := g.DB("mysql").Ctx(ctx).Raw("SELECT * FROM rt_itemorder_info WHERE order_id = ? AND item_name LIKE ?","12345","%手机%",).Scan(&results)if err != nil {g.Log().Error(ctx, err)}
}
7. 分页查询
// 分页查询
func paginationQuery(page, pageSize int) ([]ItemOrderInfo, int, error) {ctx := context.Background()var results []ItemOrderInfooffset := (page - 1) * pageSize// 查询数据err := g.DB("mysql").Ctx(ctx).Raw("SELECT * FROM rt_itemorder_info ORDER BY create_time DESC LIMIT ? OFFSET ?",pageSize, offset,).Scan(&results)if err != nil {return nil, 0, err}// 查询总数var total interr = g.DB("mysql").Ctx(ctx).Raw("SELECT COUNT(*) FROM rt_itemorder_info").Scan(&total)if err != nil {return nil, 0, err}return results, total, nil
}
总结
GF框架提供了灵活的原生SQL支持,通过Raw()方法我们可以:
- 直接执行任意SQL语句
- 使用参数绑定防止SQL注入
- 支持多数据库连接
- 在事务中使用原生SQL
- 享受GF框架的连接池和错误处理优势
使用原生SQL时需要注意:
- 始终使用参数绑定来防止SQL注入
- 合理处理错误,避免直接panic
- 在复杂业务场景中考虑使用事务
- 注意SQL语句的性能优化
这种方式特别适合复杂查询、报表生成、数据迁移等ORM难以处理的场景。
