Gorm-gen入门一文就够

Gorm-gen入门一文就够

1. 前言

官网:gorm-gen

和mybatis类似,不过注解@变成了注释//

下面让我们来介绍一下gorm-gen

2. 代码生成

2.1. 建表

首先我们需要建一张表test:t_user

create table t_user
(
    user_id       int auto_increment primary key,
    user_name     varchar(40) default ''       null,
    user_password varchar(64) default '' null
);

2.2. 配置项

  1. OutPath:./dal/query
  2. db:root:@(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local
  3. g.ApplyBasic(g.GenerateModel("t_user")),t_user就是对应的表名
  4. g.ApplyInterface(func(method model2.TUserMethod) {}, g.GenerateModel("t_user")),TUserMethod是自定义的方法
  5. 如果上面的表述你还是不清楚,那么就继续往下看看吧~
func main() {
    // specify the output directory (default: "./query")
    // ### if you want to query without context constrain, set mode gen.WithoutContext ###
    g := gen.NewGenerator(gen.Config{
      OutPath: "./dal/query",
      /* Mode: gen.WithoutContext,*/
      //if you want the nullable field generation property to be pointer type, set FieldNullable true
      /* FieldNullable: true,*/
    })

    // reuse the database connection in Project or create a connection here
    // if you want to use GenerateModel/GenerateModelAs, UseDB is necessray or it will panic
    db, _ := gorm.Open(mysql.Open("root:@(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"))
    g.UseDB(db)

    // apply basic crud api on structs or table models which is specified by table name with function
    // GenerateModel/GenerateModelAs. And generator will generate table models' code when calling Excute.
    g.ApplyBasic(
      g.GenerateModel("t_user"),
    )

    // apply diy interfaces on structs or table models
    g.ApplyInterface(
      func(method model2.TUserMethod) {},
      g.GenerateModel("t_user"),
    )

    // execute the action of code generation
    g.Execute()
}

2.3. 代码示例

image-20211005135955370

// gorm-gen通过注释生成DIY的代码,所以每次做修改之后都需要重新生成代码
type TUserMethod interface {
	// where("user_name=@name")
	SimpleFindByName(name string) (gen.T, error)
}

// -----------------生成的代码----------------------

// 在dal/query/t_user.gen.go里面
//where("user_name=@name")
func (t tUserDo) SimpleFindByName(name string) (result *model.TUser, err error) {
	params := map[string]interface{}{
		"name": name,
	}

	var generateSQL string
	generateSQL += "user_name=@name"

	err = t.UnderlyingDB().Where(generateSQL, params).Take(&result).Error
	return
}

自测代码

type UserService struct{}

func NewUserService() *UserService {
	return &UserService{}
}

func (this *UserService) Login(ctx context.Context) (*model.TUser, error) {
	u := query.Use(config.TestDB).TUser

	return u.WithContext(ctx).SimpleFindByName("ch")
}
func TestUserService_Login(t *testing.T) {
	config.InitDB()
	ctx := context.Background()
	user, err := NewUserService().Login(ctx)
	t.Log(user, err)
}
// user_service_test.go:13: &{1 ch 123} <nil>

3. CURD

和往常的差不多,所以也就不多说什么了

func (this *UserService) AddUnSelectedUser(ctx context.Context, oneUser *model.TUser) error {
	u := query.Use(config.TestDB).TUser
	return u.WithContext(ctx).Debug().Create(oneUser)
}

func (this *UserService) RemoveUser(ctx context.Context, oneUser *model.TUser) error {
	u := query.Use(config.TestDB).TUser
	info, err := u.WithContext(ctx).Where(u.UserName.Eq(oneUser.UserName)).Delete()
	if err != nil {
		return err
	}
	if info.RowsAffected == 0 {
		return info.Error
	}
	return nil
}

func (this *UserService) ModifyUser(ctx context.Context, oneUser *model.TUser) error {
	u := query.Use(config.TestDB).TUser
	info, err := u.WithContext(ctx).Where(u.UserName.Eq(oneUser.UserName)).Updates(map[string]interface{}{
		"user_password": oneUser.UserPassword,
	})
	if err != nil {
		return err
	}
	if info.RowsAffected == 0 {
		return info.Error
	}
	return nil
}

func (this *UserService) QueryUserByName(ctx context.Context, name string) (*model.TUser, error) {
	u := query.Use(config.TestDB).TUser
	return u.WithContext(ctx).Where(u.UserName.Eq(name)).First()
}

4. DIY Method

4.1. 如何自定义方法

在前面生成代码的时候我们看到,我可以通过DIY方法完成相应dto的操作

「见2. 代码生成」

  1. 简单的查询,可以直接使用where
  2. sql语句可以之间使用sql(),但不是必须的
type Method interface {
    // where("name=@name and age=@age")
    SimpleFindByNameAndAge(name string, age int) (gen.T, error)
    
    // sql(select * from users where id=@id)
    FindUserToMap(id int) (gen.M, error)
    
    // insert into users (name,age) values (@name,@age)
    InsertValue(age int, name string) error
}

4.2. 占位符

  • gen.T represents specified struct or table
  • gen.M represents map[string]interface
  • @@table represents table's name (if method's parameter doesn't contains variable table, GEN will generate table from model struct)
  • @@<columnName> represents column's name or table's name
  • @<name> represents normal query variable

4.3. 逻辑语句

Logical operations must be wrapped in {{}},and end must used {{end}}, All templates support nesting

  • if/else if/else the condition accept a bool parameter or operation expression which conforms to Golang syntax.
  • where The where clause will be inserted only if the child elements return something. The key word and or or in front of clause will be removed. And and will be added automatically when there is no junction keyword between query condition clause.
  • Set The set clause will be inserted only if the child elements return something. The , in front of columns array will be removed.And , will be added automatically when there is no junction keyword between query coulmns.

简单来说,就是{{if/where/set}}开头,{}结尾

下面是来源于官网的一个例子,可以作为学习的例子写出他们对应的sql语句

type Method interface {
    // Where("name=@name and age=@age")
    SimpleFindByNameAndAge(name string, age int) (gen.T, error)
    
    // select * from users where id=@id
    FindUserToMap(id int) (gen.M, error)
    
    // sql(insert into @@table (name,age) values (@name,@age) )
    InsertValue(age int, name string) error
    
    // select name from @@table where id=@id
    FindNameByID(id int) string
    
    // select * from @@table
    //  {{where}}
    //      id>0
    //      {{if cond}}id=@id {{end}}
    //      {{if key!="" && value != ""}} or @@key=@value{{end}}
    //  {{end}}
    FindByIDOrCustom(cond bool, id int, key, value string) ([]gen.T, error)
    
    // update @@table
    //  {{set}}
    //      update_time=now()
    //      {{if name != ""}}
    //          name=@name
    //      {{end}}
    //  {{end}}
    //  {{where}}
    //      id=@id
    //  {{end}}
    UpdateName(name string, id int) error
}

4.4. 举个例子

有人可能会问逻辑语句解决了什么问题?

我们试想一个场景,where 里面的语句有两种情况,分别是全量查询和部分查询,全量查的话不需要where

我们一般的做法是在sql语句外写if,总的来说还是两句sql去兼容这两种情况

但如果使用逻辑语句的话就可以在框架层完美解决这个问题。。。。

type TUserMethod interface {
	// where("user_name=@name")
	SimpleFindByName(name string) (gen.T, error)

	// select * from @@table {{where}} {{if 1!=1}}user_name=@userName{{end}} {{end}}
	DebugFindUsers(userName string) ([]gen.T, error)
}

func (this *UserService) DebugFindUsers(ctx context.Context, name string) ([]*model.TUser, error) {
	u := query.Use(config.TestDB).TUser
	return u.WithContext(ctx).Debug().DebugFindUsers(name)
}

func TestUserService_DebugFindUsers(t *testing.T) {
	ctx := context.Background()
	users, err := NewUserService().DebugFindUsers(ctx, "ch")
	assert.Nil(t, err)
	for _, v := range users {
		t.Log(v)
	}
}

// select * from t_user

我们重点看debug打出的sql日志,是不是where就消失了!

我们只需要补充{}里面的cond就可以了,用一条sql兼容了两种情况,是不是很棒!

5. 参考文献

gorm-gen

6. 推荐阅读

MyBatis-Plus(偷懒神器)
SpringBoot+SpringMVC+Mybatis(Web服务端笔记)

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×