diff --git a/.gitignore b/.gitignore index 074f8ae4..c74ed4f0 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,5 @@ _testmain.go /.vscode __debug_bin /test/ + +*.db \ No newline at end of file diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000..2816b6d6 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,7 @@ +# examples + +***Run generate.sh*** + +一个简单的`GEN`最佳实践。你可以通过配置`generate.sh`中的`TARGET_DIR`值指定执行不同的代码生成命令。 + +A simple best practice of `GEN`. You can configure `TARGET_DIR` value in `generate.sh` to generate different code. diff --git a/examples/biz/query.go b/examples/biz/query.go new file mode 100644 index 00000000..01042bae --- /dev/null +++ b/examples/biz/query.go @@ -0,0 +1,45 @@ +package biz + +import ( + "context" + "fmt" + + "gorm.io/gen/examples/dal/query" +) + +var q = query.Q + +func Query(ctx context.Context) { + t := q.Mytable + do := t.WithContext(context.Background()) + + data, err := do.Take() + catchError("Take", err) + fmt.Printf("got %+v\n", data) + + dataArray, err := do.Find() + catchError("Find", err) + fmt.Printf("got %+v\n", dataArray) + + data, err = do.Where(t.ID.Eq(1)).Take() + catchError("Take", err) + fmt.Printf("got %+v\n", data) + + dataArray, err = do.Where(t.Age.Gt(18)).Order(t.Username).Find() + catchError("Find", err) + fmt.Printf("got %+v\n", dataArray) + + dataArray, err = do.Select(t.ID, t.Username).Order(t.Age.Desc()).Find() + catchError("Find", err) + fmt.Printf("got %+v\n", dataArray) + + info, err := do.Where(t.ID.Eq(1)).UpdateSimple(t.Age.Add(1)) + catchError("Update", err) + fmt.Printf("got %+v\n", info) +} + +func catchError(detail string, err error) { + if err != nil { + fmt.Printf("%s: %v\n", detail, err) + } +} diff --git a/examples/cmd/gen/generate.go b/examples/cmd/gen/generate.go new file mode 100644 index 00000000..42f26095 --- /dev/null +++ b/examples/cmd/gen/generate.go @@ -0,0 +1,26 @@ +package main + +import ( + "gorm.io/gen" + "gorm.io/gen/examples/conf" + "gorm.io/gen/examples/dal" +) + +func init() { + dal.DB = dal.ConnectDB(conf.MySQLDSN) + + prepare(dal.DB) // prepare table for generate +} + +func main() { + g := gen.NewGenerator(gen.Config{ + OutPath: "../../dal/query", + }) + + g.UseDB(dal.DB) + + // generate all table from database + g.ApplyBasic(g.GenerateAllTable()...) + + g.Execute() +} diff --git a/examples/cmd/gen/prepare.go b/examples/cmd/gen/prepare.go new file mode 100644 index 00000000..e0263dc9 --- /dev/null +++ b/examples/cmd/gen/prepare.go @@ -0,0 +1,19 @@ +package main + +import ( + "gorm.io/gorm" +) + +// prepare table for test + +const mytableSQL = "CREATE TABLE IF NOT EXISTS `mytables` (" + + " `ID` int(11) NOT NULL," + + " `username` varchar(16) DEFAULT NULL," + + " `age` int(8) NOT NULL," + + " `phone` varchar(11) NOT NULL," + + " INDEX `idx_username` (`username`)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" + +func prepare(db *gorm.DB) { + db.Exec(mytableSQL) +} diff --git a/examples/cmd/sync_table/generate.go b/examples/cmd/sync_table/generate.go new file mode 100644 index 00000000..1a5da2f4 --- /dev/null +++ b/examples/cmd/sync_table/generate.go @@ -0,0 +1,55 @@ +package main + +import ( + "strings" + + "gorm.io/gen" + "gorm.io/gen/examples/conf" + "gorm.io/gen/examples/dal" +) + +func init() { + dal.DB = dal.ConnectDB(conf.MySQLDSN) + + prepare(dal.DB) // prepare table for generate +} + +// dataMap mapping relationship +var dataMap = map[string]func(detailType string) (dataType string){ + // int mapping + "int": func(detailType string) (dataType string) { return "int32" }, + + // bool mapping + "tinyint": func(detailType string) (dataType string) { + if strings.HasPrefix(detailType, "tinyint(1)") { + return "bool" + } + return "byte" + }, +} + +func main() { + g := gen.NewGenerator(gen.Config{ + OutPath: "../../dal/query", + ModelPkgPath: "../../dal/model", + + // generate model global configuration + FieldNullable: true, // generate pointer when field is nullable + FieldWithIndexTag: true, // generate with gorm index tag + FieldWithTypeTag: true, // generate with gorm column type tag + }) + + g.UseDB(dal.DB) + + // specify diy mapping relationship + g.WithDataTypeMap(dataMap) + + // generate all field with json tag end with "_example" + g.WithJSONTagNameStrategy(func(c string) string { return c + "_example" }) + + mytable := g.GenerateModel("mytables") + g.ApplyBasic(mytable) + // g.ApplyBasic(g.GenerateAllTable()...) // generate all table in db server + + g.Execute() +} diff --git a/examples/cmd/sync_table/prepare.go b/examples/cmd/sync_table/prepare.go new file mode 100644 index 00000000..e0263dc9 --- /dev/null +++ b/examples/cmd/sync_table/prepare.go @@ -0,0 +1,19 @@ +package main + +import ( + "gorm.io/gorm" +) + +// prepare table for test + +const mytableSQL = "CREATE TABLE IF NOT EXISTS `mytables` (" + + " `ID` int(11) NOT NULL," + + " `username` varchar(16) DEFAULT NULL," + + " `age` int(8) NOT NULL," + + " `phone` varchar(11) NOT NULL," + + " INDEX `idx_username` (`username`)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" + +func prepare(db *gorm.DB) { + db.Exec(mytableSQL) +} diff --git a/examples/cmd/ultimate/generate.go b/examples/cmd/ultimate/generate.go new file mode 100644 index 00000000..5b924167 --- /dev/null +++ b/examples/cmd/ultimate/generate.go @@ -0,0 +1,39 @@ +package main + +import ( + "gorm.io/gen" + "gorm.io/gen/examples/conf" + "gorm.io/gen/examples/dal" +) + +func init() { + dal.DB = dal.ConnectDB(conf.MySQLDSN) + + prepare(dal.DB) // prepare table for generate +} + +var dataMap = map[string]func(detailType string) (dataType string){ + "int": func(detailType string) (dataType string) { return "int64" }, + "json": func(string) string { return "json.RawMessage" }, +} + +func main() { + g := gen.NewGenerator(gen.Config{ + OutPath: "../../dal/query", + Mode: gen.WithDefaultQuery, + + WithUnitTest: true, + + FieldNullable: true, + FieldWithIndexTag: true, + }) + + g.UseDB(dal.DB) + + g.WithDataTypeMap(dataMap) + g.WithJSONTagNameStrategy(func(c string) string { return "-" }) + + g.ApplyBasic(g.GenerateAllTable()...) + + g.Execute() +} diff --git a/examples/cmd/ultimate/prepare.go b/examples/cmd/ultimate/prepare.go new file mode 100644 index 00000000..e0263dc9 --- /dev/null +++ b/examples/cmd/ultimate/prepare.go @@ -0,0 +1,19 @@ +package main + +import ( + "gorm.io/gorm" +) + +// prepare table for test + +const mytableSQL = "CREATE TABLE IF NOT EXISTS `mytables` (" + + " `ID` int(11) NOT NULL," + + " `username` varchar(16) DEFAULT NULL," + + " `age` int(8) NOT NULL," + + " `phone` varchar(11) NOT NULL," + + " INDEX `idx_username` (`username`)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" + +func prepare(db *gorm.DB) { + db.Exec(mytableSQL) +} diff --git a/examples/conf/mysql.go b/examples/conf/mysql.go new file mode 100644 index 00000000..922f7829 --- /dev/null +++ b/examples/conf/mysql.go @@ -0,0 +1,5 @@ +package conf + +const MySQLDSN = "root:local_maridb_test@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=True" + +const SQLiteDBName = "gen_sqlite.db" diff --git a/examples/dal/model/mytables.gen.go b/examples/dal/model/mytables.gen.go new file mode 100644 index 00000000..2fbba28c --- /dev/null +++ b/examples/dal/model/mytables.gen.go @@ -0,0 +1,20 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package model + +const TableNameMytable = "mytables" + +// Mytable mapped from table +type Mytable struct { + ID int32 `gorm:"column:ID;type:int(11);not null" json:"ID_example"` + Username *string `gorm:"column:username;type:varchar(16);index:idx_username,priority:1;default:NULL" json:"username_example"` + Age int32 `gorm:"column:age;type:int(8);not null" json:"age_example"` + Phone string `gorm:"column:phone;type:varchar(11);not null" json:"phone_example"` +} + +// TableName Mytable's table name +func (*Mytable) TableName() string { + return TableNameMytable +} diff --git a/examples/dal/mysql.go b/examples/dal/mysql.go new file mode 100644 index 00000000..ed5e7524 --- /dev/null +++ b/examples/dal/mysql.go @@ -0,0 +1,29 @@ +package dal + +import ( + "fmt" + "strings" + + "gorm.io/driver/mysql" + "gorm.io/driver/sqlite" + "gorm.io/gorm" +) + +var DB *gorm.DB + +func ConnectDB(dsn string) *gorm.DB { + var db *gorm.DB + var err error + + if strings.HasSuffix(dsn, "sqlite.db") { + db, err = gorm.Open(sqlite.Open(dsn), &gorm.Config{}) + } else { + db, err = gorm.Open(mysql.Open(dsn)) + } + + if err != nil { + panic(fmt.Errorf("connect db fail: %s", err)) + } + + return db +} diff --git a/examples/dal/query/gen.go b/examples/dal/query/gen.go new file mode 100644 index 00000000..cc8abee7 --- /dev/null +++ b/examples/dal/query/gen.go @@ -0,0 +1,80 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package query + +import ( + "context" + "database/sql" + + "gorm.io/gorm" +) + +var ( + Q = new(Query) + Mytable *mytable +) + +func SetDefault(db *gorm.DB) { + *Q = *Use(db) + Mytable = &Q.Mytable +} + +func Use(db *gorm.DB) *Query { + return &Query{ + db: db, + Mytable: newMytable(db), + } +} + +type Query struct { + db *gorm.DB + + Mytable mytable +} + +func (q *Query) Available() bool { return q.db != nil } + +func (q *Query) clone(db *gorm.DB) *Query { + return &Query{ + db: db, + Mytable: q.Mytable.clone(db), + } +} + +type queryCtx struct { + Mytable mytableDo +} + +func (q *Query) WithContext(ctx context.Context) *queryCtx { + return &queryCtx{ + Mytable: *q.Mytable.WithContext(ctx), + } +} + +func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error { + return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...) +} + +func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx { + return &QueryTx{q.clone(q.db.Begin(opts...))} +} + +type QueryTx struct{ *Query } + +func (q *QueryTx) Commit() error { + return q.db.Commit().Error +} + +func (q *QueryTx) Rollback() error { + return q.db.Rollback().Error +} + +func (q *QueryTx) SavePoint(name string) error { + return q.db.SavePoint(name).Error +} + +func (q *QueryTx) RollbackTo(name string) error { + return q.db.RollbackTo(name).Error +} diff --git a/examples/dal/query/mytables.gen.go b/examples/dal/query/mytables.gen.go new file mode 100644 index 00000000..2943e034 --- /dev/null +++ b/examples/dal/query/mytables.gen.go @@ -0,0 +1,280 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package query + +import ( + "context" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + + "gorm.io/gen" + "gorm.io/gen/examples/dal/model" + "gorm.io/gen/field" +) + +func newMytable(db *gorm.DB) mytable { + _mytable := mytable{} + + _mytable.mytableDo.UseDB(db) + _mytable.mytableDo.UseModel(&model.Mytable{}) + + tableName := _mytable.mytableDo.TableName() + _mytable.ALL = field.NewField(tableName, "*") + _mytable.ID = field.NewInt32(tableName, "ID") + _mytable.Username = field.NewString(tableName, "username") + _mytable.Age = field.NewInt32(tableName, "age") + _mytable.Phone = field.NewString(tableName, "phone") + + _mytable.fillFieldMap() + + return _mytable +} + +type mytable struct { + mytableDo mytableDo + + ALL field.Field + ID field.Int32 + Username field.String + Age field.Int32 + Phone field.String + + fieldMap map[string]field.Expr +} + +func (m mytable) As(alias string) *mytable { + m.mytableDo.DO = *(m.mytableDo.As(alias).(*gen.DO)) + + m.ALL = field.NewField(alias, "*") + m.ID = field.NewInt32(alias, "ID") + m.Username = field.NewString(alias, "username") + m.Age = field.NewInt32(alias, "age") + m.Phone = field.NewString(alias, "phone") + + m.fillFieldMap() + + return &m +} + +func (m *mytable) WithContext(ctx context.Context) *mytableDo { return m.mytableDo.WithContext(ctx) } + +func (m mytable) TableName() string { return m.mytableDo.TableName() } + +func (m *mytable) GetFieldByName(fieldName string) (field.Expr, bool) { + field, ok := m.fieldMap[fieldName] + return field, ok +} + +func (m *mytable) fillFieldMap() { + m.fieldMap = make(map[string]field.Expr, 4) + m.fieldMap["ID"] = m.ID + m.fieldMap["username"] = m.Username + m.fieldMap["age"] = m.Age + m.fieldMap["phone"] = m.Phone +} + +func (m mytable) clone(db *gorm.DB) mytable { + m.mytableDo.ReplaceDB(db) + return m +} + +type mytableDo struct{ gen.DO } + +func (m mytableDo) Debug() *mytableDo { + return m.withDO(m.DO.Debug()) +} + +func (m mytableDo) WithContext(ctx context.Context) *mytableDo { + return m.withDO(m.DO.WithContext(ctx)) +} + +func (m mytableDo) Clauses(conds ...clause.Expression) *mytableDo { + return m.withDO(m.DO.Clauses(conds...)) +} + +func (m mytableDo) Not(conds ...gen.Condition) *mytableDo { + return m.withDO(m.DO.Not(conds...)) +} + +func (m mytableDo) Or(conds ...gen.Condition) *mytableDo { + return m.withDO(m.DO.Or(conds...)) +} + +func (m mytableDo) Select(conds ...field.Expr) *mytableDo { + return m.withDO(m.DO.Select(conds...)) +} + +func (m mytableDo) Where(conds ...gen.Condition) *mytableDo { + return m.withDO(m.DO.Where(conds...)) +} + +func (m mytableDo) Order(conds ...field.Expr) *mytableDo { + return m.withDO(m.DO.Order(conds...)) +} + +func (m mytableDo) Distinct(cols ...field.Expr) *mytableDo { + return m.withDO(m.DO.Distinct(cols...)) +} + +func (m mytableDo) Omit(cols ...field.Expr) *mytableDo { + return m.withDO(m.DO.Omit(cols...)) +} + +func (m mytableDo) Join(table schema.Tabler, on ...field.Expr) *mytableDo { + return m.withDO(m.DO.Join(table, on...)) +} + +func (m mytableDo) LeftJoin(table schema.Tabler, on ...field.Expr) *mytableDo { + return m.withDO(m.DO.LeftJoin(table, on...)) +} + +func (m mytableDo) RightJoin(table schema.Tabler, on ...field.Expr) *mytableDo { + return m.withDO(m.DO.RightJoin(table, on...)) +} + +func (m mytableDo) Group(cols ...field.Expr) *mytableDo { + return m.withDO(m.DO.Group(cols...)) +} + +func (m mytableDo) Having(conds ...gen.Condition) *mytableDo { + return m.withDO(m.DO.Having(conds...)) +} + +func (m mytableDo) Limit(limit int) *mytableDo { + return m.withDO(m.DO.Limit(limit)) +} + +func (m mytableDo) Offset(offset int) *mytableDo { + return m.withDO(m.DO.Offset(offset)) +} + +func (m mytableDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *mytableDo { + return m.withDO(m.DO.Scopes(funcs...)) +} + +func (m mytableDo) Unscoped() *mytableDo { + return m.withDO(m.DO.Unscoped()) +} + +func (m mytableDo) Create(values ...*model.Mytable) error { + if len(values) == 0 { + return nil + } + return m.DO.Create(values) +} + +func (m mytableDo) CreateInBatches(values []*model.Mytable, batchSize int) error { + return m.DO.CreateInBatches(values, batchSize) +} + +// Save : !!! underlying implementation is different with GORM +// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values) +func (m mytableDo) Save(values ...*model.Mytable) error { + if len(values) == 0 { + return nil + } + return m.DO.Save(values) +} + +func (m mytableDo) First() (*model.Mytable, error) { + if result, err := m.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.Mytable), nil + } +} + +func (m mytableDo) Take() (*model.Mytable, error) { + if result, err := m.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.Mytable), nil + } +} + +func (m mytableDo) Last() (*model.Mytable, error) { + if result, err := m.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.Mytable), nil + } +} + +func (m mytableDo) Find() ([]*model.Mytable, error) { + result, err := m.DO.Find() + return result.([]*model.Mytable), err +} + +func (m mytableDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Mytable, err error) { + buf := make([]*model.Mytable, 0, batchSize) + err = m.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error { + defer func() { results = append(results, buf...) }() + return fc(tx, batch) + }) + return results, err +} + +func (m mytableDo) FindInBatches(result *[]*model.Mytable, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return m.DO.FindInBatches(result, batchSize, fc) +} + +func (m mytableDo) Attrs(attrs ...field.AssignExpr) *mytableDo { + return m.withDO(m.DO.Attrs(attrs...)) +} + +func (m mytableDo) Assign(attrs ...field.AssignExpr) *mytableDo { + return m.withDO(m.DO.Assign(attrs...)) +} + +func (m mytableDo) Joins(field field.RelationField) *mytableDo { + return m.withDO(m.DO.Joins(field)) +} + +func (m mytableDo) Preload(field field.RelationField) *mytableDo { + return m.withDO(m.DO.Preload(field)) +} + +func (m mytableDo) FirstOrInit() (*model.Mytable, error) { + if result, err := m.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.Mytable), nil + } +} + +func (m mytableDo) FirstOrCreate() (*model.Mytable, error) { + if result, err := m.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.Mytable), nil + } +} + +func (m mytableDo) FindByPage(offset int, limit int) (result []*model.Mytable, count int64, err error) { + count, err = m.Count() + if err != nil { + return + } + + result, err = m.Offset(offset).Limit(limit).Find() + return +} + +func (m mytableDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = m.Count() + if err != nil { + return + } + + err = m.Offset(offset).Limit(limit).Scan(result) + return +} + +func (m *mytableDo) withDO(do gen.Dao) *mytableDo { + m.DO = *do.(*gen.DO) + return m +} diff --git a/examples/generate.sh b/examples/generate.sh new file mode 100755 index 00000000..8ba522f5 --- /dev/null +++ b/examples/generate.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# TARGET_DIR="gen" +# TARGET_DIR="ultimate" +TARGET_DIR="sync_table" + +PROJECT_DIR=$(dirname "$0") +GENERATE_DIR="$PROJECT_DIR/cmd/$TARGET_DIR" + +cd "$GENERATE_DIR" || exit + +echo "Start Generating" +go run . diff --git a/examples/main.go b/examples/main.go new file mode 100644 index 00000000..36343d9b --- /dev/null +++ b/examples/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "context" + "fmt" + + "gorm.io/gen/examples/biz" + "gorm.io/gen/test/conf" + "gorm.io/gen/test/dal" +) + +func init() { + dal.DB = dal.ConnectDB(conf.MySQLDSN).Debug() +} + +func main() { + // start your project here + fmt.Println("hello world") + defer fmt.Println("bye~") + + biz.Query(context.Background()) +}