Go 对象关系映射框架 GORM 使用示例

本贴最后更新于 1148 天前,其中的信息可能已经水流花落

前提条件

  • 安装 PostgreSql,创建好数据库(GORM 自动迁移表结构)
  • 安装 GoLandGo SDK
  • 获取 GORM:go get -u gorm.io/gorm
  • 获取 Postgres 驱动:go get -u gorm.io/driver/postgres

使用说明

  1. 复制本代码,打开 GoLand
  2. Ctrl + Shift + Alt + Insert 创建草稿文件
  3. 将代码粘贴到草稿文件中,修改数据库连接地址,按 Ctrl + Shift + F10 运行代码查看效果
package main

import (
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
	"log"
	"os"
	"strconv"
	"time"
)

// 系统参数表结构体(实体类),GORM 约定参考: https://gorm.io/zh_CN/docs/conventions.html
type TSysParam struct {
	// gorm.Model
	ID                   string     `gorm:"type:varchar(32);not null;primaryKey;<-:create;comment:流水号参数唯一 ID"`
	DataTableName        string     `gorm:"type:varchar(128);<-;comment:数据库表名,如“t_sys_user”"`
	DataTableDescription string     `gorm:"type:varchar(255);<-;comment:数据库表名(中文)说明信息"`
	SerialValue          int        `gorm:"size:32;<-;comment:流水号当前最大值"`
	SerialLength         int        `gorm:"size:32;<-;comment:流水号长度,不足前缀以“0”补齐"`
	Remark               string     `gorm:"type:varchar(1024);<-;comment:备注信息"`
	Enabled              bool       `gorm:"<-;default:true;comment:是否可用"`
	CreateTime           *time.Time `gorm:"type:timestamptz;<-:create;autoCreateTime:milli;comment:创建时间"`
	CreateBy             string     `gorm:"type:varchar(32);<-:create;comment:创建人 ID,t_sys_user.id"`
	LastUpdateTime       *time.Time `gorm:"type:timestamptz;<-;comment:最后修改时间"`
	LastUpdateBy         string     `gorm:"type:varchar(32);<-;comment:最后修改人 ID,t_sys_user.id"`
}

// 为 TSysParam 结构体实现获取表名方法,单独设置 TSysParam 结构体的表名为 `t_sys_param`,未配置全局禁用复数表名时可使用此方法
//func (TSysParam) TableName() string {
//	return "t_sys_param"
//}

// GORM 参考文档: https://gorm.io/zh_CN/docs/
func main() {
	initDbConn()

	// CRUD 示例
	createExample()
	readExample()
	updateExample()
	deleteExample()

	printStats()
}

// GORM 数据库定义
var GormDB *gorm.DB

// 连接池数据库句柄
var SqlDB *sql.DB

// 错误信息
var err error

// 初始化数据库连接
func initDbConn() {
	GormDB, err = gorm.Open(postgres.New(postgres.Config{
		// 通过一个现有的数据库连接来初始化,无需使用 DSN
		// Conn: SqlDB,
		// 数据源名称
		DSN: "host=192.168.1.1 port=5432 user=test password=test dbname=db_test sslmode=disable TimeZone=Asia/Shanghai",
		// 禁用隐式预处理语句
		PreferSimpleProtocol: true,
	}), &gorm.Config{
		// 日志配置
		Logger: getLogger(),
		// 自定义命名策略
		NamingStrategy: schema.NamingStrategy{
			// 全局使用单数表,禁止自动转换为复数形式表名
			SingularTable: true,
		},
		// 插入数据默认批处理大小
		CreateBatchSize: 1000,
	})
	if err != nil {
		panic("数据库连接失败!")
	}

	// 数据库连接池
	SqlDB, err = GormDB.DB()
	if err != nil {
		panic("数据库连接池获取失败!")
	}
	// 设置空闲连接池中连接的最大数量
	SqlDB.SetMaxIdleConns(10)
	// 设置打开数据库连接的最大数量
	SqlDB.SetMaxOpenConns(1e3)
	// 设置连接可复用的最大时间
	SqlDB.SetConnMaxLifetime(time.Hour)
	printStats()

	// 自动迁移给定模型为数据库表结构,未创建表或需要修改表结构的情况下可以启用
	// _ = GormDB.AutoMigrate(&TSysParam{})
}

// 获取当前时间指针
func nowTime() *time.Time {
	now := time.Now()
	return &now
}

// 添加数据,参考 https://gorm.io/zh_CN/docs/create.html
func createExample() {
	// 添加单条数据
	sysParam := TSysParam{
		ID:                   "test_001",
		DataTableName:        "test_table",
		DataTableDescription: "测试表",
		SerialValue:          0,
		SerialLength:         10,
		Enabled:              true,
		CreateBy:             "00000",
		CreateTime:           nowTime(),
	}

	result := GormDB.Create(&sysParam)
	printData(&sysParam, result, "Create")

	// 向指定(Select)字段中保存数据,忽略未指定的字段(NULL)
	sysParam = TSysParam{
		ID:                   "test_002",
		DataTableName:        "test_table_002",
		DataTableDescription: "测试表",
		SerialValue:          0,
		SerialLength:         10,
		Enabled:              true,
		CreateBy:             "00000",
	}
	result = GormDB.
		Select("ID", "DataTableName", "DataTableDescription", "SerialValue", "SerialLength").
		Create(&sysParam)
	printData(&sysParam, result, "Create")

	// 添加多条数据
	dataSize := 3
	sysParams := make([]TSysParam, dataSize)
	for i := 0; i < dataSize; i++ {
		index := strconv.Itoa(i)
		sysParams[i] = TSysParam{
			ID:                   "test_list_" + index,
			DataTableName:        "test_table_" + index,
			DataTableDescription: "测试表_" + index,
			SerialValue:          0,
			SerialLength:         10,
			Enabled:              true,
			CreateBy:             "00000",
		}
	}
	// 未配置全局 CreateBatchSize 参数的情况下,一次性批量保存全部数据
	result = GormDB.Create(&sysParams)
	// 指定单次批量保存的条数分批保存,每循环到 batchSize 条保存一次直至全部完成,保存大量数据可用此方法分批保存
	// result = GormDB.CreateInBatches(&sysParams, dataSize)
	printData(&sysParams, result, "Create", "(batch)")
}

// 查询数据,参考 https://gorm.io/zh_CN/docs/query.html  https://gorm.io/zh_CN/docs/advanced_query.html
func readExample() {
	var sysParam *TSysParam
	// 根据主键查询单条数据,默认根据主键正序排序
	result := GormDB.First(&sysParam, "00001")
	printData(sysParam, result, "First")
	sysParam = nil

	// 根据自定义条件查询最后一条数据,默认根据主键倒序排序
	result = GormDB.Last(&sysParam, "enabled = ?", false)
	printData(sysParam, result, "Last")
	sysParam = nil

	// 获取一条数据,未指定排序字段
	result = GormDB.Take(&sysParam, "create_by = ?", "00000")
	printData(sysParam, result, "Take")
	sysParam = nil

	// 不使用结构体查询,直接使用表名
	result = GormDB.Table("t_sys_param").First(&sysParam)
	printData(sysParam, result, "Table", "First")
	sysParam = nil

	// 查询全部
	sysParams := new([]TSysParam)
	result = GormDB.Find(&sysParams, "enabled = ?", true)
	// 查询可用数据
	// result = GormDB.Find(&sysParams, "enabled = ?", true)
	printData(&sysParams, result, "Find", "(all)")

	// 按 AND 条件查询多条
	sysParams = new([]TSysParam)
	result = GormDB.Find(&sysParams, TSysParam{Enabled: true, CreateBy: "00000"})
	// 不使用结构体,直接使用字段 Map
	// result = GormDB.Find(&sysParams, map[string]interface{}{"enabled": true, "create_by": "00000"})
	printData(&sysParams, result, "Find", "(AND)")

	// 按 OR 条件查询多条
	sysParams = new([]TSysParam)
	result = GormDB.Where("enabled", true).Or("create_by", "00000").Find(&sysParams)
	printData(&sysParams, result, "Find", "(OR)")

	// 按 IN 条件查询多条
	sysParams = new([]TSysParam)
	result = GormDB.Find(&sysParams, "id IN ?", []string{"00001", "00002"})
	printData(&sysParams, result, "Find", "(IN)")

	// 按 NOT 条件查询多条
	sysParams = new([]TSysParam)
	result = GormDB.Not("serial_value", 0).Find(&sysParams)
	printData(&sysParams, result, "Find", "(NOT)")

	// 分页排序查询指定字段
	sysParams = new([]TSysParam)
	allSysParams := new([]TSysParam)
	result = GormDB.
		// 每页 5 条,第二页
		Offset(5).Limit(5).
		Order("serial_value DESC, id").
		Select("data_table_name", "serial_value", "serial_length").
		Find(&sysParams)
	printData(&sysParams, result, "Find", "(select-order-paging)")

	// 消除分页,获取全部
	unpageResult := result.Offset(-1).Limit(-1).Find(&allSysParams)
	// 总条数 pagingResult.RowsAffected
	printData(&allSysParams, unpageResult, "Find", "(select-order-paging-all)")
}

// 修改数据,参考 https://gorm.io/zh_CN/docs/update.html
func updateExample() {
	sysParam := TSysParam{
		ID:                   "test_001", // 根据主键修改指定数据
		DataTableName:        "test_table_001",
		DataTableDescription: "测试表-001",
		SerialValue:          1,
		SerialLength:         10,
		Enabled:              true,
		CreateTime:           nowTime(), // CreateTime 和 CreateBy 已在标签中配置为可读、可创建、不可修改
		CreateBy:             "00001",
		LastUpdateTime:       nowTime(),
		LastUpdateBy:         "00000",
	}
	// 更新所有字段,包含零值
	result := GormDB.Save(&sysParam).
		// 查询修改结果重新赋值给 sysParam
		Find(&sysParam)
	printData(&sysParam, result, "UPDATE", "Save")

	// 更新单个字段
	result = GormDB.Model(&sysParam).
		Where("enabled", true).
		Update("serial_value", sysParam.SerialValue+1).
		Find(&sysParam)
	printData(&sysParam, result, "UPDATE", "Model", "WhereUpdate")

	// 更新多个字段,使用结构体只会更新非零值字段,要更新零值字段需要使用 Select 指定要修改的字段,或者直接使用 Select("*") 更新全部字段
	sysParam = TSysParam{
		ID:             "test_001", // 根据主键修改指定数据
		SerialValue:    sysParam.SerialValue + 1,
		SerialLength:   10,
		Enabled:        false, // false 为 GORM Model 结构体零值,不会修改此字段
		LastUpdateTime: nowTime(),
		LastUpdateBy:   "00000",
	}
	result = GormDB.Model(&sysParam).
		Where("enabled", true).
		Updates(&sysParam).
		Find(&sysParam)
	printData(&sysParam, result, "UPDATE", "Model", "Updates")

	// 使用 map 可修改零值字段
	result = GormDB.Model(&sysParam).
		Updates(map[string]interface{}{"serial_value": sysParam.SerialValue + 1, "enabled": false}).
		Find(&sysParam)
	printData(&sysParam, result, "UPDATE", "Model", "UpdatesMap")

	// 使用 Omit 忽略更新指定字段
	result = GormDB.Model(&sysParam).
		Omit("serial_value").
		Updates(map[string]interface{}{"serial_value": sysParam.SerialValue + 1, "enabled": true}).
		Find(&sysParam)
	printData(&sysParam, result, "UPDATE", "Model", "UpdatesOmit")
}

// 删除数据,参考 https://gorm.io/zh_CN/docs/delete.html
func deleteExample() {
	// !!! 注意,删除数据时如果未指定主键或其他条件,将会触发无条件的批量删除
	sysParam := TSysParam{
		ID: "test_002", // 主键
	}

	// 根据主键删除指定数据
	result := GormDB.Delete(&sysParam)
	sysParam = TSysParam{}
	result.Find(&sysParam)
	printData(&sysParam, result, "Delete")

	// 通过复合条件删除数据
	result = GormDB.Where("id LIKE ? AND data_table_name LIKE ?", "test_%", "test_table_%").
		Delete(&sysParam).
		Find(&sysParam)
	printData(&sysParam, result, "DeleteWhere")
}

// 获取 GORM 日志接口
func getLogger() logger.Interface {
	gormLogger := logger.New(
		// io writer
		log.New(os.Stdout, "\r\n", log.LstdFlags),
		logger.Config{
			// 慢 SQL 阈值
			SlowThreshold: 3 * time.Second,
			// 日志级别
			LogLevel: logger.Info,
			// 是否启用彩色打印
			Colorful: true,
		},
	)

	return gormLogger
}

// 打印数据
func printData(sysParam interface{}, result *gorm.DB, morInfo ...interface{}) {
	jsonByte, _ := json.Marshal(&sysParam)

	result.Logger.Info(nil, string(jsonByte))
	fmt.Println("条数:", result.RowsAffected,
		"\t错误信息:[", result.Error,
		"]\t是否为无记录错误:", errors.Is(result.Error, gorm.ErrRecordNotFound))

	if len(morInfo) > 0 {
		fmt.Println(morInfo)
	}
}

// 打印数据库统计信息
func printStats() {
	dbStats := SqlDB.Stats()
	jsonByte, _ := json.Marshal(dbStats)
	fmt.Println(string(jsonByte))

	/*fmt.Printf(`
	空闲连接数: %d
	使用中的连接数: %d
	由于达到设置的空闲连接池的最大数量而关闭的连接数: %d
	由于达到设置的连接可空闲的最长时间而关闭的连接数: %d
	由于达到设置的可重用连接的最长时间而关闭的连接数: %d
	数据库的最大打开连接数: %d
	等待的连接总数: %d
	等待新连接被阻止的总时间: %d`, dbStats.Idle, dbStats.InUse, dbStats.MaxIdleClosed, dbStats.MaxIdleTimeClosed,
	dbStats.MaxLifetimeClosed, dbStats.MaxOpenConnections, dbStats.WaitCount, dbStats.WaitDuration)*/
}
  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    491 引用 • 1383 回帖 • 370 关注
  • GORM
    8 引用 • 19 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
iTanken
飘风不终朝,骤雨不终日。 成都

推荐标签 标签

  • 大数据

    大数据(big data)是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。

    89 引用 • 113 回帖
  • CAP

    CAP 指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

    11 引用 • 5 回帖 • 553 关注
  • 百度

    百度(Nasdaq:BIDU)是全球最大的中文搜索引擎、最大的中文网站。2000 年 1 月由李彦宏创立于北京中关村,致力于向人们提供“简单,可依赖”的信息获取方式。“百度”二字源于中国宋朝词人辛弃疾的《青玉案·元夕》词句“众里寻他千百度”,象征着百度对中文信息检索技术的执著追求。

    63 引用 • 785 回帖 • 249 关注
  • Sphinx

    Sphinx 是一个基于 SQL 的全文检索引擎,可以结合 MySQL、PostgreSQL 做全文搜索,它可以提供比数据库本身更专业的搜索功能,使得应用程序更容易实现专业化的全文检索。

    1 引用 • 171 关注
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    938 引用 • 1456 回帖 • 163 关注
  • Swift

    Swift 是苹果于 2014 年 WWDC(苹果开发者大会)发布的开发语言,可与 Objective-C 共同运行于 Mac OS 和 iOS 平台,用于搭建基于苹果平台的应用程序。

    34 引用 • 37 回帖 • 495 关注
  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 286 关注
  • webpack

    webpack 是一个用于前端开发的模块加载器和打包工具,它能把各种资源,例如 JS、CSS(less/sass)、图片等都作为模块来使用和处理。

    41 引用 • 130 回帖 • 294 关注
  • 锤子科技

    锤子科技(Smartisan)成立于 2012 年 5 月,是一家制造移动互联网终端设备的公司,公司的使命是用完美主义的工匠精神,打造用户体验一流的数码消费类产品(智能手机为主),改善人们的生活质量。

    4 引用 • 31 回帖 • 6 关注
  • 智能合约

    智能合约(Smart contract)是一种旨在以信息化方式传播、验证或执行合同的计算机协议。智能合约允许在没有第三方的情况下进行可信交易,这些交易可追踪且不可逆转。智能合约概念于 1994 年由 Nick Szabo 首次提出。

    1 引用 • 11 回帖 • 6 关注
  • uTools

    uTools 是一个极简、插件化、跨平台的现代桌面软件。通过自由选配丰富的插件,打造你得心应手的工具集合。

    5 引用 • 13 回帖
  • Jenkins

    Jenkins 是一套开源的持续集成工具。它提供了非常丰富的插件,让构建、部署、自动化集成项目变得简单易用。

    51 引用 • 37 回帖
  • Vditor

    Vditor 是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。它使用 TypeScript 实现,支持原生 JavaScript、Vue、React 和 Angular。

    308 引用 • 1658 回帖 • 1 关注
  • Solidity

    Solidity 是一种智能合约高级语言,运行在 [以太坊] 虚拟机(EVM)之上。它的语法接近于 JavaScript,是一种面向对象的语言。

    3 引用 • 18 回帖 • 346 关注
  • 脑图

    脑图又叫思维导图,是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具。

    21 引用 • 58 回帖 • 1 关注
  • Kotlin

    Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,由 JetBrains 设计开发并开源。Kotlin 可以编译成 Java 字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。在 Google I/O 2017 中,Google 宣布 Kotlin 成为 Android 官方开发语言。

    19 引用 • 33 回帖 • 20 关注
  • Latke

    Latke 是一款以 JSON 为主的 Java Web 框架。

    70 引用 • 532 回帖 • 706 关注
  • 服务器

    服务器,也称伺服器,是提供计算服务的设备。由于服务器需要响应服务请求,并进行处理,因此一般来说服务器应具备承担服务并且保障服务的能力。

    124 引用 • 580 回帖
  • PostgreSQL

    PostgreSQL 是一款功能强大的企业级数据库系统,在 BSD 开源许可证下发布。

    21 引用 • 22 回帖 • 1 关注
  • VirtualBox

    VirtualBox 是一款开源虚拟机软件,最早由德国 Innotek 公司开发,由 Sun Microsystems 公司出品的软件,使用 Qt 编写,在 Sun 被 Oracle 收购后正式更名成 Oracle VM VirtualBox。

    10 引用 • 2 回帖 • 1 关注
  • 博客

    记录并分享人生的经历。

    270 引用 • 2386 回帖
  • QQ

    1999 年 2 月腾讯正式推出“腾讯 QQ”,在线用户由 1999 年的 2 人(马化腾和张志东)到现在已经发展到上亿用户了,在线人数超过一亿,是目前使用最广泛的聊天软件之一。

    45 引用 • 557 回帖 • 224 关注
  • CentOS

    CentOS(Community Enterprise Operating System)是 Linux 发行版之一,它是来自于 Red Hat Enterprise Linux 依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,因此有些要求高度稳定的服务器以 CentOS 替代商业版的 Red Hat Enterprise Linux 使用。两者的不同在于 CentOS 并不包含封闭源代码软件。

    238 引用 • 224 回帖
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    673 引用 • 535 回帖
  • IPFS

    IPFS(InterPlanetary File System,星际文件系统)是永久的、去中心化保存和共享文件的方法,这是一种内容可寻址、版本化、点对点超媒体的分布式协议。请浏览 IPFS 入门笔记了解更多细节。

    20 引用 • 245 回帖 • 232 关注
  • GitBook

    GitBook 使您的团队可以轻松编写和维护高质量的文档。 分享知识,提高团队的工作效率,让用户满意。

    3 引用 • 8 回帖 • 1 关注
  • 机器学习

    机器学习(Machine Learning)是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能。

    76 引用 • 37 回帖