Skip to content

Commit f984c04

Browse files
committed
repack gorm package
1 parent ae291c9 commit f984c04

File tree

17 files changed

+1855
-305
lines changed

17 files changed

+1855
-305
lines changed

.github/RELEASE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
## Change log
22

3-
- Modify sponge ui.
3+
- Add auto test scripts.
4+
- Repack gorm package.

pkg/ggorm/README.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
### mysql
2+
3+
`mysql` library wrapped in [gorm](gorm.io/gorm), with added features such as tracer, paging queries, etc.
4+
5+
<br>
6+
7+
### Example of use
8+
9+
#### Initializing the connection
10+
11+
```go
12+
import (
13+
"github.com/zhufuyi/sponge/pkg/ggorm"
14+
)
15+
16+
var dsn = "root:123456@(192.168.1.6:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
17+
18+
// (1) connect to the database using the default settings
19+
db, err := ggorm.InitMysql(dsn)
20+
21+
// (2) customised settings to connect to the database
22+
db, err := ggorm.Init(
23+
dsn,
24+
ggorm.WithLogging(logger.Get()), // print log
25+
ggorm.WithLogRequestIDKey("request_id"), // print request_id
26+
ggorm.WithMaxIdleConns(5),
27+
ggorm.WithMaxOpenConns(50),
28+
ggorm.WithConnMaxLifetime(time.Minute*3),
29+
// ggorm.WithSlowThreshold(time.Millisecond*100), // only print logs that take longer than 100 milliseconds to execute
30+
// ggorm.WithEnableTrace(), // enable tracing
31+
// ggorm.WithRWSeparation(SlavesDsn, MastersDsn...) // read-write separation
32+
// ggorm.WithGormPlugin(yourPlugin) // custom gorm plugin
33+
)
34+
```
35+
36+
<br>
37+
38+
#### Model
39+
40+
```go
41+
package model
42+
43+
import (
44+
"github.com/zhufuyi/sponge/pkg/ggorm"
45+
)
46+
47+
// UserExample object fields mapping table
48+
type UserExample struct {
49+
ggorm.Model `gorm:"embedded"`
50+
51+
Name string `gorm:"type:varchar(40);unique_index;not null" json:"name"`
52+
Age int `gorm:"not null" json:"age"`
53+
Gender string `gorm:"type:varchar(10);not null" json:"gender"`
54+
}
55+
56+
// TableName get table name
57+
func (table *UserExample) TableName() string {
58+
return gorm.GetTableName(table)
59+
}
60+
```
61+
62+
<br>
63+
64+
#### Transaction
65+
66+
```go
67+
func createUser() error {
68+
// note that you should use tx as the database handle when you are in a transaction
69+
tx := db.Begin()
70+
defer func() {
71+
if err := recover(); err != nil { // rollback after a panic during transaction execution
72+
tx.Rollback()
73+
fmt.Printf("transaction failed, err = %v\n", err)
74+
}
75+
}()
76+
77+
var err error
78+
if err = tx.Error; err != nil {
79+
return err
80+
}
81+
82+
if err = tx.Where("id = ?", 1).First(table).Error; err != nil {
83+
tx.Rollback()
84+
return err
85+
}
86+
87+
panic("mock panic")
88+
89+
if err = tx.Create(&userExample{Name: "Mr Li", Age: table.Age + 2, Gender: "male"}).Error; err != nil {
90+
tx.Rollback()
91+
return err
92+
}
93+
94+
return tx.Commit().Error
95+
}
96+
```
97+
<br>
98+
99+
### gorm User Guide
100+
101+
- https://gorm.io/zh_CN/docs/index.html

pkg/ggorm/base_model.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package mysql
2+
3+
import (
4+
"reflect"
5+
"time"
6+
7+
"github.com/huandu/xstrings"
8+
"gorm.io/gorm"
9+
)
10+
11+
// Model embedded structs, add `gorm: "embedded"` when defining table structs
12+
type Model struct {
13+
ID uint64 `gorm:"column:id;AUTO_INCREMENT;primary_key" json:"id"`
14+
CreatedAt time.Time `gorm:"column:created_at" json:"createdAt"`
15+
UpdatedAt time.Time `gorm:"column:updated_at" json:"updatedAt"`
16+
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index" json:"-"`
17+
}
18+
19+
// Model2 embedded structs, json tag named is snake case
20+
type Model2 struct {
21+
ID uint64 `gorm:"column:id;AUTO_INCREMENT;primary_key" json:"id"`
22+
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
23+
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
24+
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index" json:"-"`
25+
}
26+
27+
// KV map type
28+
type KV = map[string]interface{}
29+
30+
// GetTableName get table name
31+
func GetTableName(object interface{}) string {
32+
tableName := ""
33+
34+
typeof := reflect.TypeOf(object)
35+
switch typeof.Kind() {
36+
case reflect.Ptr:
37+
tableName = typeof.Elem().Name()
38+
case reflect.Struct:
39+
tableName = typeof.Name()
40+
default:
41+
return tableName
42+
}
43+
44+
return xstrings.ToSnakeCase(tableName)
45+
}

pkg/ggorm/gorm.go

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// Package mysql is a library wrapped on top of gorm.io/gorm, with added features such as link tracing, paging queries, etc.
2+
package mysql
3+
4+
import (
5+
"database/sql"
6+
"fmt"
7+
"log"
8+
"os"
9+
10+
"github.com/uptrace/opentelemetry-go-extra/otelgorm"
11+
mysqlDriver "gorm.io/driver/mysql"
12+
"gorm.io/driver/postgres"
13+
"gorm.io/gorm"
14+
"gorm.io/gorm/logger"
15+
"gorm.io/gorm/schema"
16+
"gorm.io/plugin/dbresolver"
17+
)
18+
19+
// InitMysql init mysql or tidb
20+
func InitMysql(dsn string, opts ...Option) (*gorm.DB, error) {
21+
o := defaultOptions()
22+
o.apply(opts...)
23+
24+
sqlDB, err := sql.Open("mysql", dsn)
25+
if err != nil {
26+
return nil, err
27+
}
28+
sqlDB.SetMaxIdleConns(o.maxIdleConns) // set the maximum number of connections in the idle connection pool
29+
sqlDB.SetMaxOpenConns(o.maxOpenConns) // set the maximum number of open database connections
30+
sqlDB.SetConnMaxLifetime(o.connMaxLifetime) // set the maximum time a connection can be reused
31+
32+
db, err := gorm.Open(mysqlDriver.New(mysqlDriver.Config{Conn: sqlDB}), gormConfig(o))
33+
if err != nil {
34+
return nil, err
35+
}
36+
db.Set("gorm:table_options", "CHARSET=utf8mb4") // automatic appending of table suffixes when creating tables
37+
38+
// register trace plugin
39+
if o.enableTrace {
40+
err = db.Use(otelgorm.NewPlugin())
41+
if err != nil {
42+
return nil, fmt.Errorf("using gorm opentelemetry, err: %v", err)
43+
}
44+
}
45+
46+
// register read-write separation plugin
47+
if len(o.slavesDsn) > 0 {
48+
err = db.Use(rwSeparationPlugin(o))
49+
if err != nil {
50+
return nil, err
51+
}
52+
}
53+
54+
// register plugins
55+
for _, plugin := range o.plugins {
56+
err = db.Use(plugin)
57+
if err != nil {
58+
return nil, err
59+
}
60+
}
61+
62+
return db, nil
63+
}
64+
65+
// InitPostgresql init postgresql
66+
func InitPostgresql(dsn string, opts ...Option) (*gorm.DB, error) {
67+
o := defaultOptions()
68+
o.apply(opts...)
69+
70+
db, err := gorm.Open(postgres.Open(dsn), gormConfig(o))
71+
72+
if err != nil {
73+
return nil, err
74+
}
75+
76+
// register trace plugin
77+
if o.enableTrace {
78+
err = db.Use(otelgorm.NewPlugin())
79+
if err != nil {
80+
return nil, fmt.Errorf("using gorm opentelemetry, err: %v", err)
81+
}
82+
}
83+
84+
// register read-write separation plugin
85+
if len(o.slavesDsn) > 0 {
86+
err = db.Use(rwSeparationPlugin(o))
87+
if err != nil {
88+
return nil, err
89+
}
90+
}
91+
92+
// register plugins
93+
for _, plugin := range o.plugins {
94+
err = db.Use(plugin)
95+
if err != nil {
96+
return nil, err
97+
}
98+
}
99+
100+
return db, nil
101+
}
102+
103+
// gorm setting
104+
func gormConfig(o *options) *gorm.Config {
105+
config := &gorm.Config{
106+
// disable foreign key constraints, not recommended for production environments
107+
DisableForeignKeyConstraintWhenMigrating: o.disableForeignKey,
108+
// removing the plural of an epithet
109+
NamingStrategy: schema.NamingStrategy{SingularTable: true},
110+
}
111+
112+
// print SQL
113+
if o.isLog {
114+
if o.gLog == nil {
115+
config.Logger = logger.Default.LogMode(o.logLevel)
116+
} else {
117+
config.Logger = NewCustomGormLogger(o)
118+
}
119+
} else {
120+
config.Logger = logger.Default.LogMode(logger.Silent)
121+
}
122+
123+
// print only slow queries
124+
if o.slowThreshold > 0 {
125+
config.Logger = logger.New(
126+
log.New(os.Stdout, "\r\n", log.LstdFlags), // use the standard output asWriter
127+
logger.Config{
128+
SlowThreshold: o.slowThreshold,
129+
Colorful: true,
130+
LogLevel: logger.Warn, // set the logging level, only above the specified level will output the slow query log
131+
},
132+
)
133+
}
134+
135+
return config
136+
}
137+
138+
func rwSeparationPlugin(o *options) gorm.Plugin {
139+
slaves := []gorm.Dialector{}
140+
for _, dsn := range o.slavesDsn {
141+
slaves = append(slaves, mysqlDriver.New(mysqlDriver.Config{
142+
DSN: dsn,
143+
}))
144+
}
145+
146+
masters := []gorm.Dialector{}
147+
for _, dsn := range o.mastersDsn {
148+
masters = append(masters, mysqlDriver.New(mysqlDriver.Config{
149+
DSN: dsn,
150+
}))
151+
}
152+
153+
return dbresolver.Register(dbresolver.Config{
154+
Sources: masters,
155+
Replicas: slaves,
156+
Policy: dbresolver.RandomPolicy{},
157+
})
158+
}

0 commit comments

Comments
 (0)