首页 > 其他分享 >gorm的upsert操作不同字段

gorm的upsert操作不同字段

时间:2024-05-23 18:07:21浏览次数:16  
标签:uid create db update time 操作 gorm upsert

场景:

“INSERT INTO ... ON DUPLICATE KEY UPDATE”的应用,在 UPDATE 时不能更新字段 f_create_uid 和 f_create_time 的值,而必须更新 f_update_uid 和 f_update_time 的值。关键点在于指定 UPDATE 不更新的字段列表,实现依赖 gorm 的 tag,但如果 struct 的 field 名同表的 field 名,这没有此依赖。

示例表:

DROP TABLE IF EXISTS TableA;
CREATE TABLE TableA (
    f_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
    f_name VARCHAR(25) NOT NULL,
    f_address VARCHAR(100) NOT NULL,
    f_create_uid VARCHAR(20) NOT NULL,
    f_update_uid VARCHAR(20) NOT NULL,
    f_create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    f_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (f_id),
    UNIQUE KEY (f_name)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

使用 (sql2struct)[https://github.com/eyjian/sql2struct] 工具生成的 struct:

type TableA struct {
    Id uint32 `gorm:"column:f_id;primaryKey;autoIncrement" json:"id" db:"f_id" form:"id"`
    Name string `gorm:"column:f_name" json:"name" db:"f_name" form:"name"`
    Address string `gorm:"column:f_address" json:"address" db:"f_address" form:"address"`
    CreateUid string `gorm:"column:f_create_uid" json:"create_uid" db:"f_create_uid" form:"create_uid"`
    UpdateUid string `gorm:"column:f_update_uid" json:"update_uid" db:"f_update_uid" form:"update_uid"`
    CreateTime time.Time `gorm:"column:f_create_time" json:"create_time" db:"f_create_time" form:"create_time"`
    UpdateTime time.Time `gorm:"column:f_update_time" json:"update_time" db:"f_update_time" form:"update_time"`
}

表的“INSERT INTO ... ON DUPLICATE KEY UPDATE”操作:

package main

import (
    "flag"
    "fmt"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/clause"
    "os"
    "reflect"
    "strings"
    "time"
)

/*
DROP TABLE IF EXISTS TableA;
CREATE TABLE TableA (
    f_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
    f_name VARCHAR(25) NOT NULL,
    f_address VARCHAR(100) NOT NULL,
    f_create_uid VARCHAR(20) NOT NULL,
    f_update_uid VARCHAR(20) NOT NULL,
    f_create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    f_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (f_id),
    UNIQUE KEY (f_name)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
*/

// TableA 本例依赖 gorm 的 tag
type TableA struct {
    Id         uint32    `gorm:"column:f_id;primaryKey;autoIncrement" json:"id" db:"f_id" form:"id"`
    Name       string    `gorm:"column:f_name" json:"name" db:"f_name" form:"name"`
    Address    string    `gorm:"column:f_address" json:"address" db:"f_address" form:"address"`
    CreateUid  string    `gorm:"column:f_create_uid" json:"create_uid" db:"f_create_uid" form:"create_uid"`
    UpdateUid  string    `gorm:"column:f_update_uid" json:"update_uid" db:"f_update_uid" form:"update_uid"`
    CreateTime time.Time `gorm:"column:f_create_time" json:"create_time" db:"f_create_time" form:"create_time"`
    UpdateTime time.Time `gorm:"column:f_update_time" json:"update_time" db:"f_update_time" form:"update_time"`
}

func (t *TableA) TableName() string {
    return "TableA"
}

var (
    dsn = flag.String("dsn", "", "dbuser:dbpassword@tcp(dbhost:dbport)/dbname?charset=utf8mb4&parseTime=True&loc=Local")
)

func main() {
    // 命令行参数解析
    flag.Parse()

    // 命令行参数检查
    if *dsn == "" {
        fmt.Printf("parameter[-dsn] is not set\n")
        flag.Usage()
        os.Exit(1)
    }

    // 连接数据库
    tx, err := gorm.Open(mysql.Open(*dsn), &gorm.Config{})
    if err != nil {
        fmt.Printf("open mysql error: %s\n", err.Error())
        os.Exit(1)
    }
    tx = tx.Debug() //.Table("TableA") // 这里不能有 Table 调用,否则会导致 Omit 影响后面的 Find,导致 Find 不是全部的字段,即不是”SELECT * FROM“

    // 获取 DsFundBatchStudent 结构体的所有字段
    typeOfModel := reflect.TypeOf((*TableA)(nil)).Elem()
    numFields := typeOfModel.NumField()           // 取得总的字段数
    updateColumns := make([]string, 0, numFields) // 存放所有需更新的字段

    // 遍历所有字段,排除不需要更新的字段
    for i := 0; i < numFields; i++ {
        field := typeOfModel.Field(i)
        fieldName := field.Name
        if fieldName != "Id" && fieldName != "CreateUid" && fieldName != "CreateTime" && fieldName != "UpdateTime" {
            // 使用结构体标签(tag)获取字段的列名
            columnName := field.Tag.Get("gorm")
            if strings.HasPrefix(columnName, "column:") {
                columnName = strings.TrimPrefix(columnName, "column:")
            }
            updateColumns = append(updateColumns, columnName)
        }
    }
    fmt.Printf("updateColumns=>%+v\n", updateColumns)

    aa10 := []*TableA{
        &TableA{
            Name:      "zhangsan",
            Address:   "shenzhen",
            CreateUid: "lisi",
            UpdateUid: "lisi",
        },
        &TableA{
            Name:      "wangwu",
            Address:   "nanjing",
            CreateUid: "lisi",
            UpdateUid: "lisi",
        },
    }
    // 插入时忽略的字段
    db := tx
    db = db.Omit("f_id", "f_update_time", "f_create_time")
    db = db.Clauses(clause.OnConflict{
        //Columns: []clause.Column{{Name: "f_id"}}, // 指定冲突检查的列
        DoUpdates: clause.AssignmentColumns(updateColumns), // 指定更新的列
    })
    err = db.Create(aa10).Error
    if err != nil {
        fmt.Printf("create aa1 error: %s\n", err.Error())
        os.Exit(1)
    }

    var a11 []*TableA
    db = tx // .Table("TableA") // 这里的 Table 调用可有可无
    err = db.Order("f_name").Find(&a11).Error
    if err != nil {
        fmt.Printf("find aa11 error: %s\n", err.Error())
        os.Exit(1)
    }
    for _, a110 := range a11 {
        fmt.Printf("%+v\n", *a110)
    }

    aa20 := []*TableA{
        &TableA{
            Name:      "zhangsan",
            Address:   "hangzhou",
            CreateUid: "xiaoming",
            UpdateUid: "xiaoming",
        },
        &TableA{
            Name:      "zhouba",
            Address:   "guangzhou",
            CreateUid: "xiaoming",
            UpdateUid: "xiaoming",
        },
    }
    // 插入时忽略的字段
    db = tx
    db = db.Omit("f_id", "f_update_time", "f_create_time")
    db = db.Clauses(clause.OnConflict{
        //Columns: []clause.Column{{Name: "f_id"}}, // 指定冲突检查的列
        DoUpdates: clause.AssignmentColumns(updateColumns), // 指定更新的列
    })
    err = db.Create(aa20).Error
    if err != nil {
        fmt.Printf("create aa2 error: %s\n", err.Error())
        os.Exit(1)
    }

    var a21 []*TableA
    db = tx //.Table("TableA") // 这里的 Table 调用可有可无
    err = db.Order("f_name").Find(&a21).Error
    if err != nil {
        fmt.Printf("find a21 error: %s\n", err.Error())
        os.Exit(1)
    }
    for _, a210 := range a11 {
        fmt.Printf("%+v\n", *a210)
    }

    // 当本程序反复执行时,由于只有 zhangsan 的 UpdateUid 的交替变化,
    // 故只会观察到 zhangsan 一行的 f_update_time 在一直变化,
    // wangwu 和 zhouba 执行一次后保持不再变化,因为没有任何字段值发生变化。
}

标签:uid,create,db,update,time,操作,gorm,upsert
From: https://www.cnblogs.com/aquester/p/18209111

相关文章

  • WSL安装乌邦图操作系统
    步骤1:首先开启WSL参考官方网站:https://docs.microsoft.com/zh-cn/windows/wsl/install-manual步骤2:下载安装包到网站:https://docs.microsoft.com/zh-cn/windows/wsl/install-manual#downloading-distributions博主下载的是ubuntu18.04:https://wslstorestorage.blob.core.windo......
  • 使用interface化解一场因操作系统不同导致的编译问题
    场景描述起因:因项目需求,需要编写一个agent,需支持Linux和Windows操作系统。Agent里面有一个功能需要获取到服务器上所有已经被占用的端口。实现方式:针对不同的操作系统,实现方式有所不同linux:使用服务器自带的netstat指令,然后使用os/exec库来调用shell脚本实现wind......
  • 搜索引擎ElasticSearch18_ElasticSearch的客户端操作2
    实际开发中,主要有三种方式可以作为elasticsearch服务的客户端:第一种,elasticsearch-head插件第二种,使用elasticsearch提供的Restful接口直接访问第三种,使用elasticsearch提供的API进行访问一、安装Postman工具Postman中文版是postman这款强大网页调试工具的windows客户端,提......
  • Poco框架实操:对节点可实施的操作
    此文章来源于项目官方公众号:“AirtestProject”版权声明:允许转载,但转载必须保留原链接;请勿用作商业或者非法用途一、前言之前我们介绍了Poco的节点关系,以及获取节点属性的一些方法,具体详情可以点击查看我们历史推文~今天我们就来看看我们对Poco节点还能进行什么操作吧!二、Po......
  • 记录一次Xlwings操作excel替换内容
    importosimporttkinter.filedialogimportxlwingsasxwclassSubstitute:path=tkinter.filedialog.askdirectory(title='!!!!!!!!!!!!!!!!!请选择excel存放路径')#修改此处替换文本,前为原文本,后为新文本,最后一组后面不需要逗号dict1={'购销合同':'买卖合同','货物运输合......
  • socketserver模块、操作系统、操作系统的发展史
    【一】socketserver模块【1】简介socketserver中包含了两种类,一种为服务类(serverclass):前者提供了许多方法像绑定,监听,运行……(也就是建立连接的过程)。一种为请求处理类(requesthandleclass)专注于如何处理用户所发送的数据(也就是事务逻辑)。......
  • word替换快捷操作
    Sub替换词语()DimdocAsDocumentDimfindTextAsStringDimreplaceTextAsString'设置第一个要查找和替换的文本findText="机构管理"replaceText="部门管理"'获取当前活动文档Setdoc=ActiveDocument'开始第一个查找和替换......
  • MYSQL使用SELECT语句进行DELETE操作
    使用SELECT语句进行DELETE操作语法如下:DELETEFROM[表名]WHERE[筛选条件]有时我们可以需要通过使用SELECT语句来确定要删除的记录,然后再将找到的记录删除假设有一个名为s_user的表,用于存储用户信息。现在,我们希望删除所有已经失活的用户。DELETEFROMs_userWHERE......
  • GridLayout 等控件来完成多行按钮操作
     第一步,在布局文件中添加一个GridLayout控件,设置它的行列数和间距等属性,例如:<GridLayoutandroid:id="@+id/grid_layout"android:layout_width="match_parent"android:layout_height="wrap_content"android:columnCount="4"andr......
  • Unity编辑器Scene窗口快捷操作
    1.按住crtl,可以一个一个单位移动、缩放、旋转物体,单位距离在Edit-Snapsetting中设置,设置单位大小2.选中物体,按住alt+鼠标左键,可以环视目标物体3.按住V键,可以将物体的顶点接到其他物体的顶点 如果要设置更改其他在Scene窗口中的操作,可以利用MonoBehaviour下的OnDrawGizmos或......