- 二、unique唯一索引字段数据冲突问题
一、定义表模型时区问题
1.1 time.Time 与int64
一般情况下,我们在定义表模型的时候,会使用time.Time
,但是会根据当前时间存储。返回给前端的时候做时区转换会比较复杂,所以一般用int64
:
// User 直接对应数据库中的表
// 有些人叫做entity,有些人叫做model
type User struct {
Id int64 `gorm:"primaryKey,autoIncrement"`
// 全部用户唯一
Email string `gorm:"unique"`
Password string
// 创建时间,毫秒数,使用int64解决时区问题
Ctime int64
// 更新时间
Utime int64
}
1.2 优势
在定义数据库表模型时,选择使用 int64
类型来表示时间戳有一些考虑和优势,尤其是在处理时区问题时。以下是一些优势和考虑因素:
- 时区一致性: 使用
int64
表示时间戳可以避免在前端和后端之间进行时区转换的复杂性。int64
类型的时间戳是相对于某个固定的基准时间(通常是UNIX纪元)的毫秒数,不涉及时区信息。这样,你就可以更轻松地在前端和后端之间传递和处理时间信息,而不必担心时区转换引起的问题。 - 序列化和传输: 使用
int64
类型的时间戳可以更方便地在网络上传输和序列化,因为它是一个数字。对于前后端通信而言,时间戳是一种常见的时间表示方式。 - 易于处理: 在一些情况下,直接使用
int64
类型的时间戳可能更容易处理。例如,你可以轻松进行比较、排序和其他与时间相关的计算,而不涉及时区信息。这在某些业务场景下可能是一种简化处理的方式。 - 避免时区混淆: 时区问题可能引起一系列复杂的 bug,而使用
int64
类型可以避免这些问题,只有返回给用户的时候才需要处理时区问题,数据库存储永远是UTC不会出错。并且前端可以直接使用这个时间戳做转换。
二、unique唯一索引字段数据冲突问题
举个例子,当两个用户同时访问,注册同一个邮箱,当线程1插入会成功,线程2插入不会成功,并且会返回系统错误,这会对用户造成很不好的影响。
所以一般使用唯一索引冲突错误码1062
来判断。
// ErrUserDuplicateEmail 表示用户邮箱重复的错误
var ErrUserDuplicateEmail = errors.New("邮箱冲突")
// Insert 将用户数据插入数据库
func (dao *UserDAO) Insert(ctx context.Context, u User) error {
// 存储当前时间的毫秒数
now := time.Now().UnixNano()
u.Ctime = now
u.Utime = now
// 使用Gorm的Create方法将用户数据插入数据库
err := dao.db.WithContext(ctx).Create(&u).Error
// 类型断言,判断是否是MySQL的唯一冲突错误
if mysqlErr, ok := err.(*mysql.MySQLError); ok {
const uniqueConflictsErrNo uint16 = 1062
// MySQL错误码1062表示唯一冲突
if mysqlErr.Number == uniqueConflictsErrNo {
// 返回自定义的唯一冲突错误
return ErrUserDuplicateEmail
}
}
// 返回其他数据库操作可能出现的错误
return err
}