侧边栏壁纸
  • 累计撰写 71 篇文章
  • 累计创建 87 个标签
  • 累计收到 5 条评论

目 录CONTENT

文章目录

golang中使用gorm写入time.Time的类型时间问题

KunkkaWu
2024-03-28 / 0 评论 / 0 点赞 / 1,179 阅读 / 1,703 字 / 正在检测是否收录...

概述

当我们使用golang来构建一个web应用或者其他使用到数据库的应用的时候,通常会选择使用gorm库。主要原因还是因为gorm库操作方便,简单易用。

在对数据库的操作中,通常需要对时间进行处理。而gorm在model层的结构体定义中,也提供了time.Time类型。但是在实际的使用中,如果我们不注意的话,可能会遇到一些奇怪的问题。

遇到的问题

1. 空时间类型写入数据库,无法匹配mysql中的datetime类型的时间格式

Error 1292 (22007): Incorrect datetime value: ‘0000-00-00’ for column ‘online_at’ at row 1

排查问题

Model层定义

在对于Tag表的定义中,可以看出我们分别定义了三个时间字段:created_at,updated_at,online_at。通常情况下,在数据库中updated_at字段会设置 on update: CURRENT_TIMESTAMP。也就是说,当有数据写入或者更新的时候,数据库会自动更新updated_at中的时间。所以,我们在写业务逻辑代码的时候,就不需要去更新updated_at的值。
但是,created_atonline_at 两个时间字段,就需要我们在业务逻辑中新增或者修改了。

package model

import (
    "time"
)

// Tag 表
type Tag struct {
    Id        uint      `gorm:"column:id;type:int(11) unsigned;primary_key;AUTO_INCREMENT" json:"id"`
    TagName   string    `gorm:"column:tag_name;type:varchar(20);comment:关键字;NOT NULL" json:"tag_name"`
    CreatedAt time.Time `gorm:"column:created_at;type:datetime;comment:创建时间" json:"created_at"`
    UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;comment:更新时间" json:"updated_at"`
    OnlineAt  time.Time `gorm:"column:online_at;type:datetime;comment:上线时间" json:"online_at"`
}

// TableName -
func (m *Tag) TableName() string {
    return "tag"
}

Controller中的使用

controller层的Create()创建业务逻辑代码中,我们指定了CreatedAt创建时间,并没有指定OnlineAt。 而且在本身的业务逻辑中,也不应该去指定OnlineAt

func (c *TagController) Create(tagName string) {
    // 开启事务
    tx := c.DB.Begin()
    tagModel := &model.Tag{
        TagName:   tagName,
        CreatedAt: time.Now(),
    }
    if err := tx.Create(tagModel).Error; err != nil {
        // 创建失败回滚
        tx.Rollback()
        fmt.Println(err)
    }
    // 提交事务
    if err := tx.Commit().Error; err != nil {
        fmt.Println(err)
    }
}

报错

当我们执行上述代码的时候,发现gorm报错

2024/03/27 11:35:00 /Users/Kunkkawu/go/src/test/gorm_time/controller/tag.go:32 Error 1292 (22007): Incorrect datetime value: '0000-00-00' for column 'online_at' at row 1
[1.955ms] [rows:0] INSERT INTO `tag` (`tag_name`,`created_at`,`updated_at`,`online_at`) VALUES ('gorm_time','2024-03-27 11:35:00.306','2024-03-27 11:35:00.307','0000-00-00 00:00:00')
Error 1292 (22007): Incorrect datetime value: '0000-00-00' for column 'online_at' at row 1
sql: transaction has already been committed or rolled back

从错误信息中可以看出,online_at由于没有设置具体的值,而被零值'0000-00-00 00:00:00'占位了。

解决办法

方法一:定义model的时候,添加字段标签default:null

在定义Tag model的时候,由于没有定义default:null,因此gorm在处理SQL的时候,就会自动使用零值来代替。

// Tag 表
type Tag struct {
    Id        uint      `gorm:"column:id;type:int(11) unsigned;primary_key;AUTO_INCREMENT" json:"id"`
    TagName   string    `gorm:"column:tag_name;type:varchar(20);comment:关键字;NOT NULL" json:"tag_name"`
    CreatedAt time.Time `gorm:"column:created_at;type:datetime;comment:创建时间" json:"created_at"`
    UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;comment:更新时间" json:"updated_at"`
    OnlineAt  time.Time `gorm:"column:online_at;type:datetime;default:null;comment:上线时间" json:"online_at"`
}

方法二:使用*time.Time来代替

在定义Tag model的时候,如果类型定义为 *time.Time, 在gorm处理SQL的时候,零值就会使用null来拼接。因此就不会报错。

// Tag 表
type Tag struct {
    Id        uint       `gorm:"column:id;type:int(11) unsigned;primary_key;AUTO_INCREMENT" json:"id"`
    TagName   string     `gorm:"column:tag_name;type:varchar(20);comment:关键字;NOT NULL" json:"tag_name"`
    CreatedAt *time.Time `gorm:"column:created_at;type:datetime;comment:创建时间" json:"created_at"`
    UpdatedAt *time.Time `gorm:"column:updated_at;type:datetime;comment:更新时间" json:"updated_at"`
    OnlineAt  *time.Time `gorm:"column:online_at;type:datetime;comment:上线时间" json:"online_at"`
}


总结

上述提供的两种方法,都可以解决由于时间类型的零值,带来的错误问题。个人更推荐使用gorm的标签来制定default值。这样在真正需要指定时间的时候,只需要time.Now()即可,而不是t := time.Now() 然后将 &t 赋值。

附录

示例代码

代码结构

  • main.go : 入口主函数
  • controller
    • tag.go : Tag控制器,提供给main.go调用
  • model
    • tag.go : Tag模型,定义表数据结构

model/tag.go

package model

import (
    "time"
)

// Tag 表
type Tag struct {
    Id        uint       `gorm:"column:id;type:int(11) unsigned;primary_key;AUTO_INCREMENT" json:"id"`
    TagName   string     `gorm:"column:tag_name;type:varchar(20);comment:关键字;NOT NULL" json:"tag_name"`
    CreatedAt *time.Time `gorm:"column:created_at;type:datetime;comment:创建时间" json:"created_at"`
    UpdatedAt *time.Time `gorm:"column:updated_at;type:datetime;comment:更新时间" json:"updated_at"`
    OnlineAt  *time.Time `gorm:"column:online_at;type:datetime;comment:上线时间" json:"online_at"`
}

// TableName -
func (m *Tag) TableName() string {
    return "tag"
}

controller/tag.go

package controller

import (
    "fmt"
    "test/utils/sqlmock/model"
    "time"

    "gorm.io/gorm"
)

type TagController struct {
    DB *gorm.DB
}

func (c *TagController) Create(tagName string) {
    // 开启事务
    tx := c.DB.Begin()
    t := time.Now()
    tagModel := &model.Tag{
        TagName:   tagName,
        CreatedAt: &t,
    }
    if err := tx.Create(tagModel).Error; err != nil {
        // 创建失败回滚
        tx.Rollback()
        fmt.Println(err)
    }
    // 提交事务
    if err := tx.Commit().Error; err != nil {
        fmt.Println(err)
    }
}

func (c *TagController) GetInfo(tagName string) {
    tagModel := &model.Tag{}
    if err := c.DB.Where("tag_name = ?", tagName).First(tagModel).Error; err != nil {
        fmt.Println(err)
    }
    fmt.Println(tagModel.Id)
    fmt.Println(tagModel.TagName)
    fmt.Println(tagModel.CreatedAt.Format(time.DateTime))
    fmt.Println(tagModel.UpdatedAt.Format(time.DateTime))
}

main.go

package main

import (
    "test/gorm_time/controller"

    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

func main() {
    db := initDB()
    tagCtrl := controller.TagController{
        DB: db,
    }
    tagCtrl.Create("gorm_time")
    tagCtrl.GetInfo("gorm_time")
}

func initDB() *gorm.DB {
    dsn := "root:@tcp(127.0.0.1:3306)/test?charset=utf8&parseTime=true&loc=Asia%2FShanghai"
    db, err := gorm.Open(mysql.Open(dsn))
    if err != nil {
        panic(err)
    }
    return db
}


0

评论区