# 6.1 GORM
# 6.1.1 第三方库
# 6.1.2 配置参数
| 名称 | 类型 | 描述 |
|---|---|---|
dsn | string | 连接串 |
logLevel | string | 日志级别,可选值:silent, error, warn, info |
maxIdleConns | int | 最大空闲连接数,默认为50 |
maxOpenConns | int | 最大打开连接数,默认为50 |
slowThreshold | string | 慢查询阈值,支持单位:纳秒(ns)、微秒(us 或 µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认2s |
connMaxLifetime | string | 连接最大生命周期,支持单位:纳秒(ns)、微秒(us 或 µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认1h |
# 6.1.3 示例代码
以下完整示例详见:third-gorm-example (opens new window)
- 组件封装
文件位置:third-gorm-example/gorm/mysql.go (opens new window)
package gorm
import (
"fmt"
"strings"
"time"
_ "third-gorm-example/gorm/serializer"
"github.com/dobyte/due/v2/core/pool"
"github.com/dobyte/due/v2/etc"
"github.com/dobyte/due/v2/log"
"gorm.io/driver/mysql"
"gorm.io/gorm"
loggers "gorm.io/gorm/logger"
)
var factory = pool.NewFactory(func(name string) (*gorm.DB, error) {
return NewInstance(fmt.Sprintf("etc.mysql.%s", name))
})
type Config struct {
DSN string `json:"dsn"`
LogLevel string `json:"logLevel"`
MaxIdleConns int `json:"maxIdleConns"`
MaxOpenConns int `json:"maxOpenConns"`
SlowThreshold time.Duration `json:"slowThreshold"`
ConnMaxLifetime time.Duration `json:"connMaxLifetime"`
}
// Instance 获取实例
func Instance(name ...string) *gorm.DB {
var (
err error
ins *gorm.DB
)
if len(name) == 0 {
ins, err = factory.Get("default")
} else {
ins, err = factory.Get(name[0])
}
if err != nil {
log.Fatalf("create mysql instance failed: %v", err)
}
return ins
}
// NewInstance 新建实例
func NewInstance[T string | Config | *Config](config T) (*gorm.DB, error) {
var (
conf *Config
v any = config
)
switch c := v.(type) {
case string:
conf = &Config{
DSN: etc.Get(fmt.Sprintf("%s.dsn", c)).String(),
LogLevel: etc.Get(fmt.Sprintf("%s.logLevel", c)).String(),
MaxIdleConns: etc.Get(fmt.Sprintf("%s.maxIdleConns", c)).Int(),
MaxOpenConns: etc.Get(fmt.Sprintf("%s.maxOpenConns", c)).Int(),
SlowThreshold: etc.Get(fmt.Sprintf("%s.slowThreshold", c), "2s").Duration(),
ConnMaxLifetime: etc.Get(fmt.Sprintf("%s.connMaxLifetime", c), "1h").Duration(),
}
case Config:
conf = &c
case *Config:
conf = c
}
var logLevel loggers.LogLevel
switch strings.ToLower(conf.LogLevel) {
case "silent":
logLevel = loggers.Silent
case "error":
logLevel = loggers.Error
case "warn":
logLevel = loggers.Warn
case "info":
logLevel = loggers.Info
default:
logLevel = loggers.Warn
}
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: conf.DSN,
}), &gorm.Config{Logger: &logger{
logLevel: logLevel,
ignoreRecordNotFoundError: true,
slowThreshold: conf.SlowThreshold,
}})
if err != nil {
return nil, err
}
database, err := db.DB()
if err != nil {
return nil, err
}
if conf.MaxIdleConns > 0 {
database.SetMaxIdleConns(conf.MaxIdleConns)
}
if conf.MaxOpenConns > 0 {
database.SetMaxOpenConns(conf.MaxOpenConns)
}
if conf.ConnMaxLifetime > 0 {
database.SetConnMaxLifetime(conf.ConnMaxLifetime)
}
return db, nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
文件位置:third-gorm-example/gorm/logger.go (opens new window)
package gorm
import (
"context"
"fmt"
"time"
"github.com/dobyte/due/v2/errors"
"github.com/dobyte/due/v2/log"
loggers "gorm.io/gorm/logger"
)
const traceStr = "%s\n[%.3fms] [rows:%v] %s"
type logger struct {
logLevel loggers.LogLevel
ignoreRecordNotFoundError bool
slowThreshold time.Duration
}
var _ loggers.Interface = &logger{}
func (l *logger) LogMode(level loggers.LogLevel) loggers.Interface {
newLogger := *l
newLogger.logLevel = level
return &newLogger
}
func (l *logger) Info(ctx context.Context, msg string, data ...any) {
if l.logLevel >= loggers.Info {
log.Printf(log.LevelInfo, msg, data)
}
}
func (l *logger) Warn(ctx context.Context, msg string, data ...any) {
if l.logLevel >= loggers.Warn {
log.Printf(log.LevelWarn, msg, data)
}
}
func (l *logger) Error(ctx context.Context, msg string, data ...any) {
if l.logLevel >= loggers.Error {
log.Printf(log.LevelError, msg, data)
}
}
// Trace print sql message
func (l *logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
if l.logLevel <= loggers.Silent {
return
}
switch elapsed := time.Since(begin); {
case err != nil && l.logLevel >= loggers.Error && (!errors.Is(err, loggers.ErrRecordNotFound) || !l.ignoreRecordNotFoundError):
sql, rows := fc()
if rows == -1 {
log.Printf(log.LevelError, traceStr, err, float64(elapsed.Nanoseconds())/1e6, "-", sql)
} else {
log.Printf(log.LevelError, traceStr, err, float64(elapsed.Nanoseconds())/1e6, rows, sql)
}
case elapsed > l.slowThreshold && l.slowThreshold != 0 && l.logLevel >= loggers.Error:
sql, rows := fc()
slowLog := fmt.Sprintf("SLOW SQL >= %v", l.slowThreshold)
if rows == -1 {
log.Printf(log.LevelWarn, traceStr, slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql)
} else {
log.Printf(log.LevelWarn, traceStr, slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql)
}
case l.logLevel == loggers.Info:
sql, rows := fc()
if rows == -1 {
log.Printf(log.LevelInfo, traceStr, "", float64(elapsed.Nanoseconds())/1e6, "-", sql)
} else {
log.Printf(log.LevelInfo, traceStr, "", float64(elapsed.Nanoseconds())/1e6, rows, sql)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
文件位置:third-gorm-example/gorm/serializer/json.go (opens new window)
package serializer
import (
"context"
"reflect"
"github.com/dobyte/due/v2/encoding/json"
"gorm.io/gorm/schema"
)
func init() {
schema.RegisterSerializer("json", JSONSerializer{})
}
type JSONSerializer struct {
}
// Scan implements serializer interface
func (JSONSerializer) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue any) (err error) {
fieldValue := reflect.New(field.FieldType)
if dbValue != nil {
var bytes []byte
switch v := dbValue.(type) {
case []byte:
bytes = v
case string:
bytes = []byte(v)
default:
bytes, err = json.Marshal(v)
if err != nil {
return err
}
}
if len(bytes) > 0 {
err = json.Unmarshal(bytes, fieldValue.Interface())
}
}
field.ReflectValueOf(ctx, dst).Set(fieldValue.Elem())
return
}
// Value implements serializer interface
func (JSONSerializer) Value(ctx context.Context, field *schema.Field, dst reflect.Value, fieldValue any) (any, error) {
result, err := json.Marshal(fieldValue)
if err != nil {
return "", err
}
if string(result) == "null" {
switch field.FieldType.Kind() {
case reflect.Array, reflect.Slice:
return "[]", nil
default:
return "{}", nil
}
}
return string(result), nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
- 配置示例
文件位置:third-gorm-example/etc/etc.toml (opens new window)
[mysql.default]
# dsn连接信息
dsn = "root:123456@tcp(127.0.0.1:3306)/due_default?charset=utf8mb4&parseTime=True&loc=Local"
# 日志级别; silent | error | warn | info
logLevel = "info"
# 最大空闲连接数,默认为50
maxIdleConns = 50
# 最大打开连接数,默认为50
maxOpenConns = 50
# 慢查询阈值,支持单位:纳秒(ns)、微秒(us | µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认2s
slowThreshold = "2s"
# 连接最大生命周期,支持单位:纳秒(ns)、微秒(us | µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认1h
connMaxLifetime = "1h"
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- 组件调用
文件位置:third-gorm-example/main.go (opens new window)
package main
import (
gormcomp "third-gorm-example/gorm"
"github.com/dobyte/due/v2/log"
"gorm.io/gorm"
)
func main() {
db := gormcomp.Instance("default")
if err := db.Connection(func(tx *gorm.DB) error {
return nil
}); err != nil {
log.Errorf("db connection error: %v", err)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18