Go 语言使用 XORM 操作 MySQL 的陷阱

本文涉及的产品
RDS Agent(兼容OpenClaw),2核4GB
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
简介: Go 语言使用 XORM 操作 MySQL 的陷阱

介绍

在 Go 语言开发中,大家为了方便,通常会选择使用 ORM 操作数据库,比如使用 XORM 或 GORM 操作 MySQL。

虽然使用 ORM 操作 MySQL 比直接使用标准库 `sql`[1]三方 MySQL 数据库驱动包[2]操作 MySQL 更加方便,但是也会遇到一些陷阱。

本文我们来介绍一下使用 XORM[3] 操作 MySQL 可能会遇到的陷阱。

使用 XORM 操作 MySQL 的陷阱

类型零值

在 Golang 中,每个数据类型都有各自的类型零值,比如 int 的零值是 0,string 的零值是 ''等。

示例代码:

package main
import (
 "fmt"
 _ "github.com/go-sql-driver/mysql"
 "xorm.io/xorm"
)
func main() {
 // 创建 Engine
 engine, err := xorm.NewEngine("mysql", "root:root@/example?charset=utf8")
 defer func() {
  err = engine.Close()
  if err != nil {
   fmt.Printf("engine close err=%v\n", err)
   return
  }
 }()
 if err != nil {
  fmt.Printf("init xorm engine fail, err=%v\n", err)
  return
 }
 // 更新数据
 example := &Example{
  Title: "go",
  View:  0,
 }
 condi := &Example{
  Id: 2,
 }
 affected, err := engine.Update(example, condi)
 if err != nil {
  fmt.Printf("Update err=%v\n", err)
  return
 }
 fmt.Printf("affected=%d\n", affected)
}
type Example struct {
 Id      int    `json:"id" form:"id"`
 Title   string `json:"title" form:"title"`
 View    int    `json:"view" form:"view"`
 Created int    `json:"created" form:"created" xorm:"created"`
 Updated int    `json:"updated" form:"updated" xorm:"updated"`
}

阅读上面这段代码,我们可以发现示例代码中将 id=2 的数据 view 字段更新为 0,因为 0 是 int 的类型零值,XORM 的 Update 方法会自动忽略类型零值,所以该数据 view 字段的值没有更改。

但是,在实际项目开发中,我们可能需要将某个字段的值更新为该字段类型的类型零值,此时我们该怎么操作呢?

affected, err := engine.Cols("title", "view").Update(example, condi)

我们可以使用 Cols() 方法,指定需要更新的字段,这样即便需要更新字段的值是该字段类型的类型零值,也可以正常更改。

提示:建议在设计数据库表时,字段的值尽量使用非类型零值。

自增 id

在插入数据时,我们可能需要返回自增 id,我们先看一段代码:

// 插入数据
example := &Example{
  Title: "PHP",
  View:  90,
}
affected, err := engine.Insert(example)
if err != nil {
  fmt.Printf("Insert err=%v\n", err)
  return
}
fmt.Printf("affected=%v\n", affected)

阅读上面这段代码,我们插入一条数据,返回结果是影响行数和错误信息,而不是直接返回该条数据的自增 id。

可能有些读者朋友们会接着使用查询方法,查询最新一条数据的 id,在并发请求数低的场景中,该方法是可以查到新插入数据的自增 id。

但是在并发请求数高的场景中,该方法查到的最新一条数据的 id,未必是我们刚插入的数据的自增 id。

id := example.Id
fmt.Printf("affected=%v || id=%d\n", affected, id)

阅读上面这段代码,我们想要获取新插入数据的自增 id,直接 example.Id 即可获取,但是前提条件是结构体中,id 字段使用 xorm:"autoincr" 标签。

更新 created 字段

我们在结构体中,使用标签 xorm:createdxorm:updated 即可自动插入当前时间。

但是,使用 xorm:created 标签的字段,只有在第一次插入数据时写入当前时间,此后将不再会更改;使用 xorm:updated 标签的字段,在第一次插入数据时写入当前时间,此后每次 Update 操作,时间都会更改。

如果我们的业务需求是需要更改使用 xorm:created 标签的字段,可以做到吗?

// 更改数据
example := &Example{
  Title: "JavaScript",
  View:  98,
}
condi := &Example{
  Id: 2,
}
affected, err := engine.Update(example, condi)
if err != nil {
  fmt.Printf("Update err=%v\n", err)
  return
}
fmt.Printf("affected=%d\n", affected)

阅读上面这段代码,我们发现执行 Update 方法之后,使用 xorm:updated 标签的字段的值被更改,而使用 xorm:created 标签的字段的值没被更改。

我们换一种更新数据的方式,代码如下:

// 更改数据
sql := "UPDATE example SET title=?, view=?, created=? WHERE id=?"
res, err := engine.Exec(sql, "Python", 60, time.Now().Unix(), 2)
if err != nil {
  fmt.Printf("Update err=%v\n", err)
  return
}
affected, err := res.RowsAffected()
if err != nil {
  fmt.Printf("RowsAffected err=%v\n", err)
  return
}
fmt.Printf("affected=%d\n", affected)

阅读上面这段代码,我们可以发现使用 Exec 方法执行原生 SQL 可以满足我们的需求。

03

总结

本文我们介绍了使用 XORM 操作 MySQL 时,新手读者朋友们可能会遇到的陷阱,希望可以给大家带来一些帮助。

读者朋友们在使用 XORM 或 GORM 操作 MySQL 时,还遇到过哪些陷阱,欢迎读者朋友们在留言区分享。

推荐阅读

参考资料

[1]

标准库 sql: https://pkg.go.dev/database/sql

[2]

三方 MySQL 数据库驱动包: https://github.com/go-sql-driver/mysql/

[3]

XORM: https://xorm.io


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
目录
相关文章
|
8月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
399 2
|
10月前
|
Cloud Native 安全 Java
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
613 1
|
10月前
|
Cloud Native Go API
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
623 0
|
10月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
440 0
|
10月前
|
Cloud Native Java 中间件
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
482 0
|
10月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
573 0
|
10月前
|
数据采集 Go API
Go语言实战案例:多协程并发下载网页内容
本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
|
SQL 关系型数据库 MySQL
【Go语言专栏】使用Go语言连接MySQL数据库
【4月更文挑战第30天】本文介绍了如何使用Go语言连接和操作MySQL数据库,包括选择`go-sql-driver/mysql`驱动、安装导入、建立连接、执行SQL查询、插入/更新/删除操作、事务处理以及性能优化和最佳实践。通过示例代码,展示了连接数据库、使用连接池、事务管理和性能调优的方法,帮助开发者构建高效、稳定的Web应用。
2201 0
|
SQL 关系型数据库 MySQL
Go语言中如何连接 MySQL,基础必备!
在现代应用中,数据库操作至关重要。本教程将指导你使用Go语言进行MySQL的CRUD操作。首先,确保已创建`test_db`数据库及`users`表。接着安装MySQL驱动:`go get -u github.com/go-sql-driver/mysql`。通过示例代码,你将学会连接数据库、创建、查询、更新及删除用户记录。尽管此方法直接,但在实际项目中可能略显繁琐,后续会介绍更高效的库如sqlx或gorm。现在,让我们从基础开始,掌握Go语言中的数据库交互技巧。
282 3
|
SQL 安全 关系型数据库
go语言连接mysql、sqlx、sql注入
go语言连接mysql、sqlx、sql注入

推荐镜像

更多