前提条件
- 安装
PostgreSql
,创建好数据库(GORM 自动迁移表结构)
- 安装
GoLand
和 Go SDK
- 获取 GORM:
go get -u gorm.io/gorm
- 获取 Postgres 驱动:
go get -u gorm.io/driver/postgres
使用说明
- 复制本代码,打开
GoLand
- 按 Ctrl + Shift + Alt + Insert 创建草稿文件
- 将代码粘贴到草稿文件中,修改数据库连接地址,按 Ctrl + Shift + F10 运行代码查看效果
1package main
2
3import (
4 "database/sql"
5 "encoding/json"
6 "errors"
7 "fmt"
8 "gorm.io/driver/postgres"
9 "gorm.io/gorm"
10 "gorm.io/gorm/logger"
11 "gorm.io/gorm/schema"
12 "log"
13 "os"
14 "strconv"
15 "time"
16)
17
18// 系统参数表结构体(实体类),GORM 约定参考: https://gorm.io/zh_CN/docs/conventions.html
19type TSysParam struct {
20 // gorm.Model
21 ID string `gorm:"type:varchar(32);not null;primaryKey;<-:create;comment:流水号参数唯一 ID"`
22 DataTableName string `gorm:"type:varchar(128);<-;comment:数据库表名,如“t_sys_user”"`
23 DataTableDescription string `gorm:"type:varchar(255);<-;comment:数据库表名(中文)说明信息"`
24 SerialValue int `gorm:"size:32;<-;comment:流水号当前最大值"`
25 SerialLength int `gorm:"size:32;<-;comment:流水号长度,不足前缀以“0”补齐"`
26 Remark string `gorm:"type:varchar(1024);<-;comment:备注信息"`
27 Enabled bool `gorm:"<-;default:true;comment:是否可用"`
28 CreateTime *time.Time `gorm:"type:timestamptz;<-:create;autoCreateTime:milli;comment:创建时间"`
29 CreateBy string `gorm:"type:varchar(32);<-:create;comment:创建人 ID,t_sys_user.id"`
30 LastUpdateTime *time.Time `gorm:"type:timestamptz;<-;comment:最后修改时间"`
31 LastUpdateBy string `gorm:"type:varchar(32);<-;comment:最后修改人 ID,t_sys_user.id"`
32}
33
34// 为 TSysParam 结构体实现获取表名方法,单独设置 TSysParam 结构体的表名为 `t_sys_param`,未配置全局禁用复数表名时可使用此方法
35//func (TSysParam) TableName() string {
36// return "t_sys_param"
37//}
38
39// GORM 参考文档: https://gorm.io/zh_CN/docs/
40func main() {
41 initDbConn()
42
43 // CRUD 示例
44 createExample()
45 readExample()
46 updateExample()
47 deleteExample()
48
49 printStats()
50}
51
52// GORM 数据库定义
53var GormDB *gorm.DB
54
55// 连接池数据库句柄
56var SqlDB *sql.DB
57
58// 错误信息
59var err error
60
61// 初始化数据库连接
62func initDbConn() {
63 GormDB, err = gorm.Open(postgres.New(postgres.Config{
64 // 通过一个现有的数据库连接来初始化,无需使用 DSN
65 // Conn: SqlDB,
66 // 数据源名称
67 DSN: "host=192.168.1.1 port=5432 user=test password=test dbname=db_test sslmode=disable TimeZone=Asia/Shanghai",
68 // 禁用隐式预处理语句
69 PreferSimpleProtocol: true,
70 }), &gorm.Config{
71 // 日志配置
72 Logger: getLogger(),
73 // 自定义命名策略
74 NamingStrategy: schema.NamingStrategy{
75 // 全局使用单数表,禁止自动转换为复数形式表名
76 SingularTable: true,
77 },
78 // 插入数据默认批处理大小
79 CreateBatchSize: 1000,
80 })
81 if err != nil {
82 panic("数据库连接失败!")
83 }
84
85 // 数据库连接池
86 SqlDB, err = GormDB.DB()
87 if err != nil {
88 panic("数据库连接池获取失败!")
89 }
90 // 设置空闲连接池中连接的最大数量
91 SqlDB.SetMaxIdleConns(10)
92 // 设置打开数据库连接的最大数量
93 SqlDB.SetMaxOpenConns(1e3)
94 // 设置连接可复用的最大时间
95 SqlDB.SetConnMaxLifetime(time.Hour)
96 printStats()
97
98 // 自动迁移给定模型为数据库表结构,未创建表或需要修改表结构的情况下可以启用
99 // _ = GormDB.AutoMigrate(&TSysParam{})
100}
101
102// 获取当前时间指针
103func nowTime() *time.Time {
104 now := time.Now()
105 return &now
106}
107
108// 添加数据,参考 https://gorm.io/zh_CN/docs/create.html
109func createExample() {
110 // 添加单条数据
111 sysParam := TSysParam{
112 ID: "test_001",
113 DataTableName: "test_table",
114 DataTableDescription: "测试表",
115 SerialValue: 0,
116 SerialLength: 10,
117 Enabled: true,
118 CreateBy: "00000",
119 CreateTime: nowTime(),
120 }
121
122 result := GormDB.Create(&sysParam)
123 printData(&sysParam, result, "Create")
124
125 // 向指定(Select)字段中保存数据,忽略未指定的字段(NULL)
126 sysParam = TSysParam{
127 ID: "test_002",
128 DataTableName: "test_table_002",
129 DataTableDescription: "测试表",
130 SerialValue: 0,
131 SerialLength: 10,
132 Enabled: true,
133 CreateBy: "00000",
134 }
135 result = GormDB.
136 Select("ID", "DataTableName", "DataTableDescription", "SerialValue", "SerialLength").
137 Create(&sysParam)
138 printData(&sysParam, result, "Create")
139
140 // 添加多条数据
141 dataSize := 3
142 sysParams := make([]TSysParam, dataSize)
143 for i := 0; i < dataSize; i++ {
144 index := strconv.Itoa(i)
145 sysParams[i] = TSysParam{
146 ID: "test_list_" + index,
147 DataTableName: "test_table_" + index,
148 DataTableDescription: "测试表_" + index,
149 SerialValue: 0,
150 SerialLength: 10,
151 Enabled: true,
152 CreateBy: "00000",
153 }
154 }
155 // 未配置全局 CreateBatchSize 参数的情况下,一次性批量保存全部数据
156 result = GormDB.Create(&sysParams)
157 // 指定单次批量保存的条数分批保存,每循环到 batchSize 条保存一次直至全部完成,保存大量数据可用此方法分批保存
158 // result = GormDB.CreateInBatches(&sysParams, dataSize)
159 printData(&sysParams, result, "Create", "(batch)")
160}
161
162// 查询数据,参考 https://gorm.io/zh_CN/docs/query.html https://gorm.io/zh_CN/docs/advanced_query.html
163func readExample() {
164 var sysParam *TSysParam
165 // 根据主键查询单条数据,默认根据主键正序排序
166 result := GormDB.First(&sysParam, "00001")
167 printData(sysParam, result, "First")
168 sysParam = nil
169
170 // 根据自定义条件查询最后一条数据,默认根据主键倒序排序
171 result = GormDB.Last(&sysParam, "enabled = ?", false)
172 printData(sysParam, result, "Last")
173 sysParam = nil
174
175 // 获取一条数据,未指定排序字段
176 result = GormDB.Take(&sysParam, "create_by = ?", "00000")
177 printData(sysParam, result, "Take")
178 sysParam = nil
179
180 // 不使用结构体查询,直接使用表名
181 result = GormDB.Table("t_sys_param").First(&sysParam)
182 printData(sysParam, result, "Table", "First")
183 sysParam = nil
184
185 // 查询全部
186 sysParams := new([]TSysParam)
187 result = GormDB.Find(&sysParams, "enabled = ?", true)
188 // 查询可用数据
189 // result = GormDB.Find(&sysParams, "enabled = ?", true)
190 printData(&sysParams, result, "Find", "(all)")
191
192 // 按 AND 条件查询多条
193 sysParams = new([]TSysParam)
194 result = GormDB.Find(&sysParams, TSysParam{Enabled: true, CreateBy: "00000"})
195 // 不使用结构体,直接使用字段 Map
196 // result = GormDB.Find(&sysParams, map[string]interface{}{"enabled": true, "create_by": "00000"})
197 printData(&sysParams, result, "Find", "(AND)")
198
199 // 按 OR 条件查询多条
200 sysParams = new([]TSysParam)
201 result = GormDB.Where("enabled", true).Or("create_by", "00000").Find(&sysParams)
202 printData(&sysParams, result, "Find", "(OR)")
203
204 // 按 IN 条件查询多条
205 sysParams = new([]TSysParam)
206 result = GormDB.Find(&sysParams, "id IN ?", []string{"00001", "00002"})
207 printData(&sysParams, result, "Find", "(IN)")
208
209 // 按 NOT 条件查询多条
210 sysParams = new([]TSysParam)
211 result = GormDB.Not("serial_value", 0).Find(&sysParams)
212 printData(&sysParams, result, "Find", "(NOT)")
213
214 // 分页排序查询指定字段
215 sysParams = new([]TSysParam)
216 allSysParams := new([]TSysParam)
217 result = GormDB.
218 // 每页 5 条,第二页
219 Offset(5).Limit(5).
220 Order("serial_value DESC, id").
221 Select("data_table_name", "serial_value", "serial_length").
222 Find(&sysParams)
223 printData(&sysParams, result, "Find", "(select-order-paging)")
224
225 // 消除分页,获取全部
226 unpageResult := result.Offset(-1).Limit(-1).Find(&allSysParams)
227 // 总条数 pagingResult.RowsAffected
228 printData(&allSysParams, unpageResult, "Find", "(select-order-paging-all)")
229}
230
231// 修改数据,参考 https://gorm.io/zh_CN/docs/update.html
232func updateExample() {
233 sysParam := TSysParam{
234 ID: "test_001", // 根据主键修改指定数据
235 DataTableName: "test_table_001",
236 DataTableDescription: "测试表-001",
237 SerialValue: 1,
238 SerialLength: 10,
239 Enabled: true,
240 CreateTime: nowTime(), // CreateTime 和 CreateBy 已在标签中配置为可读、可创建、不可修改
241 CreateBy: "00001",
242 LastUpdateTime: nowTime(),
243 LastUpdateBy: "00000",
244 }
245 // 更新所有字段,包含零值
246 result := GormDB.Save(&sysParam).
247 // 查询修改结果重新赋值给 sysParam
248 Find(&sysParam)
249 printData(&sysParam, result, "UPDATE", "Save")
250
251 // 更新单个字段
252 result = GormDB.Model(&sysParam).
253 Where("enabled", true).
254 Update("serial_value", sysParam.SerialValue+1).
255 Find(&sysParam)
256 printData(&sysParam, result, "UPDATE", "Model", "WhereUpdate")
257
258 // 更新多个字段,使用结构体只会更新非零值字段,要更新零值字段需要使用 Select 指定要修改的字段,或者直接使用 Select("*") 更新全部字段
259 sysParam = TSysParam{
260 ID: "test_001", // 根据主键修改指定数据
261 SerialValue: sysParam.SerialValue + 1,
262 SerialLength: 10,
263 Enabled: false, // false 为 GORM Model 结构体零值,不会修改此字段
264 LastUpdateTime: nowTime(),
265 LastUpdateBy: "00000",
266 }
267 result = GormDB.Model(&sysParam).
268 Where("enabled", true).
269 Updates(&sysParam).
270 Find(&sysParam)
271 printData(&sysParam, result, "UPDATE", "Model", "Updates")
272
273 // 使用 map 可修改零值字段
274 result = GormDB.Model(&sysParam).
275 Updates(map[string]interface{}{"serial_value": sysParam.SerialValue + 1, "enabled": false}).
276 Find(&sysParam)
277 printData(&sysParam, result, "UPDATE", "Model", "UpdatesMap")
278
279 // 使用 Omit 忽略更新指定字段
280 result = GormDB.Model(&sysParam).
281 Omit("serial_value").
282 Updates(map[string]interface{}{"serial_value": sysParam.SerialValue + 1, "enabled": true}).
283 Find(&sysParam)
284 printData(&sysParam, result, "UPDATE", "Model", "UpdatesOmit")
285}
286
287// 删除数据,参考 https://gorm.io/zh_CN/docs/delete.html
288func deleteExample() {
289 // !!! 注意,删除数据时如果未指定主键或其他条件,将会触发无条件的批量删除
290 sysParam := TSysParam{
291 ID: "test_002", // 主键
292 }
293
294 // 根据主键删除指定数据
295 result := GormDB.Delete(&sysParam)
296 sysParam = TSysParam{}
297 result.Find(&sysParam)
298 printData(&sysParam, result, "Delete")
299
300 // 通过复合条件删除数据
301 result = GormDB.Where("id LIKE ? AND data_table_name LIKE ?", "test_%", "test_table_%").
302 Delete(&sysParam).
303 Find(&sysParam)
304 printData(&sysParam, result, "DeleteWhere")
305}
306
307// 获取 GORM 日志接口
308func getLogger() logger.Interface {
309 gormLogger := logger.New(
310 // io writer
311 log.New(os.Stdout, "\r\n", log.LstdFlags),
312 logger.Config{
313 // 慢 SQL 阈值
314 SlowThreshold: 3 * time.Second,
315 // 日志级别
316 LogLevel: logger.Info,
317 // 是否启用彩色打印
318 Colorful: true,
319 },
320 )
321
322 return gormLogger
323}
324
325// 打印数据
326func printData(sysParam interface{}, result *gorm.DB, morInfo ...interface{}) {
327 jsonByte, _ := json.Marshal(&sysParam)
328
329 result.Logger.Info(nil, string(jsonByte))
330 fmt.Println("条数:", result.RowsAffected,
331 "\t错误信息:[", result.Error,
332 "]\t是否为无记录错误:", errors.Is(result.Error, gorm.ErrRecordNotFound))
333
334 if len(morInfo) > 0 {
335 fmt.Println(morInfo)
336 }
337}
338
339// 打印数据库统计信息
340func printStats() {
341 dbStats := SqlDB.Stats()
342 jsonByte, _ := json.Marshal(dbStats)
343 fmt.Println(string(jsonByte))
344
345 /*fmt.Printf(`
346 空闲连接数: %d
347 使用中的连接数: %d
348 由于达到设置的空闲连接池的最大数量而关闭的连接数: %d
349 由于达到设置的连接可空闲的最长时间而关闭的连接数: %d
350 由于达到设置的可重用连接的最长时间而关闭的连接数: %d
351 数据库的最大打开连接数: %d
352 等待的连接总数: %d
353 等待新连接被阻止的总时间: %d`, dbStats.Idle, dbStats.InUse, dbStats.MaxIdleClosed, dbStats.MaxIdleTimeClosed,
354 dbStats.MaxLifetimeClosed, dbStats.MaxOpenConnections, dbStats.WaitCount, dbStats.WaitDuration)*/
355}