前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >golang中使用gorm写入time.Time的类型时间问题

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

原创
作者头像
KunkkaWu
发布2024-03-27 15:25:42
5100
发布2024-03-27 15:25:42

TOC

概述

当我们使用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 两个时间字段,就需要我们在业务逻辑中新增或者修改了。

代码语言: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中的使用

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

代码语言:go
复制
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报错

代码语言:go
复制
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的时候,就会自动使用零值来代替。

代码语言:go
复制
// 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来拼接。因此就不会报错。

代码语言:go
复制
// 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

代码语言: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

代码语言: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

代码语言: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
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 遇到的问题
  • 排查问题
    • Model层定义
      • Controller中的使用
        • 报错
          • 解决办法
            • 方法一:定义model的时候,添加字段标签default:null
            • 方法二:使用*time.Time来代替
        • 总结
        • 附录
          • 示例代码
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
          http://www.vxiaotou.com