根据数据库配置自动生成model代码(Golang语言),主要是使用go-zero和gorm
- 支持mysql数据库,使用的是gorm.
- 支持数据库熔断,使用的go-zero的
core/breaker
组件. - 支持redis缓存,使用的go-zero的
core/stores/cache
组件. - 支持OpenTracing链路追踪,使用的tracing.
- 支持单字段的主键,包括查询、更新和删除操作.
- 支持多字段的组合主键,包括查询、更新和删除操作.
- 支持单字段的唯一索引,包括查询、更新和删除操作.
- 支持多字段的组合唯一索引,包括查询、更新和删除操作.
$ ./gogen model datasource -h
NAME:
gogen model datasource - generate model from datasource
USAGE:
gogen model datasource [command options] [arguments...]
OPTIONS:
--url value data soucre of database, mysql: "root:password@tcp(127.0.0.1:3306)/database"
--table value the tables in the database,support for comma separation
--dir value the target dir
--cache value generate code with cache [optional]
--tracing value generate code with tracing [optional]
--help, -h show help (default: false)
$ ./gogen model datasource --url "gozero:123456@tcp(192.168.20.151:3406)/gozero" --table "book,goods,user" --dir ../models --tracing true --cache true
$ ll
total 40K
-rwxrwxr-x 1 zhou.yingan zhou.yingan 3.9K Jan 25 16:29 bookmodel.go
-rwxrwxr-x 1 zhou.yingan zhou.yingan 826 Jan 25 16:29 builder.go
-rwxrwxr-x 1 zhou.yingan zhou.yingan 4.1K Jan 25 16:29 dbconn.go
-rwxrwxr-x 1 zhou.yingan zhou.yingan 9.8K Jan 25 16:29 goodsmodel.go
-rwxrwxr-x 1 zhou.yingan zhou.yingan 12K Jan 25 16:29 usermodel.go
数据库相关的封装在dbconn.go
文件中.
package models
import (
"database/sql"
"errors"
"io"
"time"
"github.com/tal-tech/go-zero/core/breaker"
"github.com/tal-tech/go-zero/core/stores/cache"
"github.com/tal-tech/go-zero/core/syncx"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
const (
maxIdleConns = 64
maxOpenConns = 64
maxLifetime = time.Minute
cacheSafeGapBetweenIndexAndPrimary = time.Second * 5
)
var (
exclusiveCalls = syncx.NewSharedCalls()
stats = cache.NewCacheStat("dbc")
connManager = syncx.NewResourceManager()
)
type (
// DBConn gorm db.
DBConn struct {
*gorm.DB
breaker.Breaker
}
// CachedDBConn with cache.
CachedDBConn struct {
conn *DBConn
cache cache.Cache
}
QueryFn func(conn *DBConn, v interface{}) error
ExecFn func(conn *DBConn) (int64, error)
PrimaryFn func(data interface{}) (string, error)
)
// NewDBConn new gorm object.
func NewDBConn(datasource string) *DBConn {
val, err := connManager.GetResource(datasource, func() (io.Closer, error) {
db, err := newDBConnection(datasource)
if err != nil {
return nil, err
}
return db, nil
})
if err != nil {
return nil
}
return val.(*DBConn)
}
func newDBConnection(datasource string) (*DBConn, error) {
db := &DBConn{Breaker: breaker.NewBreaker()}
conn, err := sql.Open("mysql", datasource)
if err != nil {
return nil, err
}
conn.SetMaxIdleConns(maxIdleConns)
conn.SetMaxOpenConns(maxOpenConns)
conn.SetConnMaxLifetime(maxLifetime)
db.DB, err = gorm.Open(mysql.New(mysql.Config{Conn: conn}), &gorm.Config{
PrepareStmt: true,
})
if err != nil {
return nil, err
}
return db, nil
}
// Transact start a transaction.
func (conn *DBConn) Transact(fn func(*DBConn) error) error {
return conn.DoWithAcceptable(
func() error {
return conn.Transaction(func(tx *gorm.DB) error {
db := &DBConn{DB: tx}
return fn(db)
})
}, conn.Acceptable)
}
// Close closer interface
func (conn *DBConn) Close() error {
return nil
}
// Acceptable accept
func (conn *DBConn) Acceptable(err error) bool {
ok := err == nil || errors.Is(err, sql.ErrNoRows) || errors.Is(err, sql.ErrTxDone)
return ok
}
// NewCachedDBConn with cache.
func NewCachedDBConn(datasource string, c cache.CacheConf, opts ...cache.Option) CachedDBConn {
cc := CachedDBConn{
conn: NewDBConn(datasource),
cache: cache.NewCache(c, exclusiveCalls, stats, sql.ErrNoRows, opts...),
}
return cc
}
// QueryRow single row with cache.
func (cc CachedDBConn) QueryRow(v interface{}, key string, query QueryFn) error {
return cc.cache.Take(v, key, func(v interface{}) error {
err := cc.conn.DoWithAcceptable(func() error {
return query(cc.conn, v)
}, cc.conn.Acceptable)
return err
})
}
// QueryRowIndex single row with cache by unique index.
func (cc CachedDBConn) QueryRowIndex(key string, primaryValue, data interface{}, query QueryFn, primaryFn PrimaryFn, queryByPrimary QueryFn) error {
var found bool
err := cc.cache.TakeWithExpire(primaryValue, key, func(v interface{}, expire time.Duration) error {
err := cc.conn.DoWithAcceptable(func() error {
return query(cc.conn, data)
}, cc.conn.Acceptable)
if err != nil {
return nil
}
found = true
primaryKey, err := primaryFn(data)
if err != nil {
return err
}
return cc.cache.SetCacheWithExpire(primaryKey, data, expire+cacheSafeGapBetweenIndexAndPrimary)
})
if err != nil {
return err
}
if found {
return nil
}
primaryKey, err := primaryFn(primaryValue)
if err != nil {
return err
}
err = cc.QueryRow(data, primaryKey, queryByPrimary)
return err
}
// Exec exec with cache.
func (cc CachedDBConn) Exec(exec ExecFn, keys ...string) (int64, error) {
var rowsaffected int64
var err error
err = cc.conn.DoWithAcceptable(
func() error {
rowsaffected, err = exec(cc.conn)
return err
}, cc.conn.Acceptable)
if err != nil {
return rowsaffected, err
}
err = cc.cache.DelCache(keys...)
return rowsaffected, err
}
// Transact start a transaction.
func (cc CachedDBConn) Transact(fn func(*DBConn) error) error {
return cc.conn.Transact(fn)
}
bookmodel.go
对应book
表,goodsmodel.go
对应goods
表,usermodel.go
对应user
表.
goodsmodel.go
的内容:
package models
import (
"context"
"fmt"
"strings"
"time"
"github.com/opentracing/opentracing-go/ext"
"github.com/shenbaise9527/tracing"
)
var (
goodsFieldNames = fieldNames(&Goods{})
goodsRowsNoPA = strings.Join(removeField(goodsFieldNames, "GOODSID", "GOODSCODE"), "=?,") + "=?"
goodsRowsUkGoodsGoodsnameNoPA = strings.Join(removeField(goodsFieldNames, "GOODSNAME", "GOODSID", "GOODSCODE"), "=?,") + "=?"
cacheGoodsPKPrefix = "cache#Goods#PK"
cacheGoodsUkGoodsGoodsnamePrefix = "cache#Goods#UkGoodsGoodsname"
)
type (
// GoodsModel model interface
GoodsModel interface {
// Insert insert the record
Insert(ctx context.Context, data *Goods) error
// FindOne query the record by the primary key
FindOne(ctx context.Context, goodsid int64, goodscode string) (*Goods, error)
// FindByUkGoodsGoodsname query the record by the unique key-UK_GOODS_GOODSNAME
FindByUkGoodsGoodsname(ctx context.Context, goodsname string) (*Goods, error)
// Update update the record by the primary key
Update(ctx context.Context, data *Goods) error
// UpdateByUkGoodsGoodsname update the record by the unique key-UK_GOODS_GOODSNAME
UpdateByUkGoodsGoodsname(ctx context.Context, data *Goods) error
// Delete delete the record
Delete(ctx context.Context, data *Goods) error
// Delete delete the record by the primary key
DeleteByPrimary(ctx context.Context, goodsid int64, goodscode string) error
// DeleteByUkGoodsGoodsname delete the record by the unique key-UK_GOODS_GOODSNAME
DeleteByUkGoodsGoodsname(ctx context.Context, goodsname string) error
}
// defaultGoodsModel model object
defaultGoodsModel struct {
CachedDBConn
table string
}
// Goods .
Goods struct {
Goodsid int64 `json:"goodsid" gorm:"column:GOODSID;autoIncrement;primaryKey"`
Goodscode string `json:"goodscode" gorm:"column:GOODSCODE;primaryKey"` // ()
Goodsname string `json:"goodsname" gorm:"column:GOODSNAME"`
Marketid int64 `json:"marketid" gorm:"column:MARKETID"` // ID
Goodsgroupid int64 `json:"goodsgroupid" gorm:"column:GOODSGROUPID"` // ID
Goodsstatus int32 `json:"goodsstatus" gorm:"column:GOODSSTATUS"` // - 1: 2: 3: 4: 5: 6: 7:
Currencyid int64 `json:"currencyid" gorm:"column:CURRENCYID"` // ID
Goodunitid int64 `json:"goodunitid" gorm:"column:GOODUNITID"` // ID
Agreeunit float64 `json:"agreeunit" gorm:"column:AGREEUNIT"`
Decimalplace int32 `json:"decimalplace" gorm:"column:DECIMALPLACE"`
Listingdate time.Time `json:"listingdate" gorm:"column:LISTINGDATE"`
Lasttradedate time.Time `json:"lasttradedate" gorm:"column:LASTTRADEDATE"` // ()
}
// goodsPrimary primary key struct.
goodsPrimary struct {
Goodsid int64 `json:"goodsid" gorm:"column:GOODSID;autoIncrement;primaryKey"`
Goodscode string `json:"goodscode" gorm:"column:GOODSCODE;primaryKey"` // ()
}
)
// TableName is goods
func (Goods) TableName() string {
return "goods"
}
// NewGoodsModel new model object
func NewGoodsModel(conn CachedDBConn) GoodsModel {
return &defaultGoodsModel{
CachedDBConn: conn,
table: "goods",
}
}
// Insert insert the record
func (m *defaultGoodsModel) Insert(ctx context.Context, data *Goods) error {
var err error
span := tracing.ChildOfSpanFromContext(ctx, "goodsmodel")
defer span.Finish()
ext.DBStatement.Set(span, "Insert")
defer func() {
if err != nil {
ext.Error.Set(span, true)
span.LogKV("error", err.Error())
}
}()
err = m.conn.DoWithAcceptable(
func() error {
err := m.conn.Create(data).Error
return err
}, m.conn.Acceptable)
return err
}
// FindOne query the record by the primary key
func (m *defaultGoodsModel) FindOne(ctx context.Context, goodsid int64, goodscode string) (*Goods, error) {
var err error
primaryKey := fmt.Sprintf("%s#%v#%v", cacheGoodsPKPrefix, goodsid, goodscode)
span := tracing.ChildOfSpanFromContext(ctx, "goodsmodel")
defer span.Finish()
ext.DBStatement.Set(span, "FindOne")
span.SetTag("key", primaryKey)
defer func() {
if err != nil {
ext.Error.Set(span, true)
span.LogKV("error", err.Error())
}
}()
var resp Goods
err = m.QueryRow(&resp, primaryKey, func(conn *DBConn, v interface{}) error {
return conn.Where("goodsid = ? and goodscode = ?", goodsid, goodscode).First(v).Error
})
return &resp, err
}
// FindByUkGoodsGoodsname query the record by the unique key-UK_GOODS_GOODSNAME
func (m *defaultGoodsModel) FindByUkGoodsGoodsname(ctx context.Context, goodsname string) (*Goods, error) {
var err error
ukgoodsgoodsnameKey := fmt.Sprintf("%s#%v", cacheGoodsUkGoodsGoodsnamePrefix, goodsname)
span := tracing.ChildOfSpanFromContext(ctx, "goodsmodel")
defer span.Finish()
ext.DBStatement.Set(span, "FindByUkGoodsGoodsname")
span.SetTag("key", ukgoodsgoodsnameKey)
defer func() {
if err != nil {
ext.Error.Set(span, true)
span.LogKV("error", err.Error())
}
}()
var data Goods
var primaryKey goodsPrimary
err = m.QueryRowIndex(
ukgoodsgoodsnameKey, &primaryKey, &data,
func(conn *DBConn, v interface{}) error {
return conn.Where("goodsname = ?", goodsname).First(v).Error
},
m.buildPrimaryKey,
func(conn *DBConn, v interface{}) error {
return conn.Where("goodsid = ? and goodscode = ?", primaryKey.Goodsid, primaryKey.Goodscode).First(v).Error
},
)
return &data, err
}
func (m *defaultGoodsModel) buildPrimaryKey(v interface{}) (string, error) {
switch d := v.(type) {
case *Goods:
return fmt.Sprintf("%s#%v#%v", cacheGoodsPKPrefix, d.Goodsid, d.Goodscode), nil
case *goodsPrimary:
return fmt.Sprintf("%s#%v#%v", cacheGoodsPKPrefix, d.Goodsid, d.Goodscode), nil
default:
return "", fmt.Errorf("cant support type: %v", d)
}
}
// Update update the record by the primary key
func (m *defaultGoodsModel) Update(ctx context.Context, data *Goods) error {
var err error
primaryKey := fmt.Sprintf("%s#%v#%v", cacheGoodsPKPrefix, data.Goodsid, data.Goodscode)
span := tracing.ChildOfSpanFromContext(ctx, "goodsmodel")
defer span.Finish()
ext.DBStatement.Set(span, "Update")
span.SetTag("key", primaryKey)
defer func() {
if err != nil {
ext.Error.Set(span, true)
span.LogKV("error", err.Error())
}
}()
_, err = m.Exec(func(conn *DBConn) (int64, error) {
query := fmt.Sprintf("update %s set %s where goodsid = ? and goodscode = ?", m.table, goodsRowsNoPA)
db := conn.Exec(query, data.Goodsname, data.Marketid, data.Goodsgroupid, data.Goodsstatus, data.Currencyid, data.Goodunitid, data.Agreeunit, data.Decimalplace, data.Listingdate, data.Lasttradedate, data.Goodsid, data.Goodscode)
return db.RowsAffected, db.Error
}, primaryKey)
return err
}
// UpdateByUkGoodsGoodsname update the record by the unique key-UK_GOODS_GOODSNAME
func (m *defaultGoodsModel) UpdateByUkGoodsGoodsname(ctx context.Context, data *Goods) error {
var err error
primaryKey := fmt.Sprintf("%s#%v#%v", cacheGoodsPKPrefix, data.Goodsid, data.Goodscode)
span := tracing.ChildOfSpanFromContext(ctx, "goodsmodel")
defer span.Finish()
ext.DBStatement.Set(span, "UpdateByUkGoodsGoodsname")
span.SetTag("key", primaryKey)
defer func() {
if err != nil {
ext.Error.Set(span, true)
span.LogKV("error", err.Error())
}
}()
_, err = m.Exec(func(conn *DBConn) (int64, error) {
query := fmt.Sprintf("update %s set %s where goodsname = ?", m.table, goodsRowsUkGoodsGoodsnameNoPA)
db := conn.Exec(query, data.Marketid, data.Goodsgroupid, data.Goodsstatus, data.Currencyid, data.Goodunitid, data.Agreeunit, data.Decimalplace, data.Listingdate, data.Lasttradedate, data.Goodsname)
return db.RowsAffected, db.Error
}, primaryKey)
return err
}
// Delete delete the record
func (m *defaultGoodsModel) Delete(ctx context.Context, data *Goods) error {
var err error
primaryKey := fmt.Sprintf("%s#%v#%v", cacheGoodsPKPrefix, data.Goodsid, data.Goodscode)
span := tracing.ChildOfSpanFromContext(ctx, "goodsmodel")
defer span.Finish()
ext.DBStatement.Set(span, "Delete")
span.SetTag("key", primaryKey)
defer func() {
if err != nil {
ext.Error.Set(span, true)
span.LogKV("error", err.Error())
}
}()
err = m.delete(data)
return err
}
// Delete delete the record by the primary key
func (m *defaultGoodsModel) DeleteByPrimary(ctx context.Context, goodsid int64, goodscode string) error {
var err error
primaryKey := fmt.Sprintf("%s#%v#%v", cacheGoodsPKPrefix, goodsid, goodscode)
span := tracing.ChildOfSpanFromContext(ctx, "goodsmodel")
defer span.Finish()
ext.DBStatement.Set(span, "DeleteByPrimary")
span.SetTag("key", primaryKey)
defer func() {
if err != nil {
ext.Error.Set(span, true)
span.LogKV("error", err.Error())
}
}()
data, err := m.FindOne(ctx, goodsid, goodscode)
if err != nil {
return err
}
err = m.delete(data)
return err
}
// DeleteByUkGoodsGoodsname delete the record by the unique key-UK_GOODS_GOODSNAME
func (m *defaultGoodsModel) DeleteByUkGoodsGoodsname(ctx context.Context, goodsname string) error {
var err error
ukgoodsgoodsnameKey := fmt.Sprintf("%s#%v", cacheGoodsUkGoodsGoodsnamePrefix, goodsname)
span := tracing.ChildOfSpanFromContext(ctx, "goodsmodel")
defer span.Finish()
ext.DBStatement.Set(span, "DeleteByUkGoodsGoodsname")
span.SetTag("key", ukgoodsgoodsnameKey)
defer func() {
if err != nil {
ext.Error.Set(span, true)
span.LogKV("error", err.Error())
}
}()
data, err := m.FindByUkGoodsGoodsname(ctx, goodsname)
if err != nil {
return err
}
err = m.delete(data)
return err
}
func (m *defaultGoodsModel) delete(data *Goods) error {
primaryKey := fmt.Sprintf("%s#%v#%v", cacheGoodsPKPrefix, data.Goodsid, data.Goodscode)
ukgoodsgoodsnameKey := fmt.Sprintf("%s#%v", cacheGoodsUkGoodsGoodsnamePrefix, data.Goodsname)
_, err := m.Exec(func(conn *DBConn) (int64, error) {
db := conn.Delete(Goods{}, "goodsid = ? and goodscode = ?", data.Goodsid, data.Goodscode)
return db.RowsAffected, db.Error
}, primaryKey, ukgoodsgoodsnameKey)
return err
}