首頁 > 軟體

Go ORM的封裝解決方式詳解

2023-01-14 14:01:51

背景

去年慢慢開始接觸了Go語言,也在公司寫了幾個Go的生產專案。我是從Java轉過來的。(其實也不算轉,公司用啥,我用啥)在這個過程中,老是想用Java的思維寫Go,在開始的一兩個月,那是邊寫邊吐槽。

醜陋的錯誤處理,沒有流式處理,還竟然沒有泛型,框架生態鏈不成熟,沒有一家獨大的類似Spring的框架。(其實現在寫了快一年的Go,Go還是挺香的,哈哈)

今天,我來聊一下,我在我在寫Go過程中用的最多orm框架gorm。

Java的orm

寫過Java的基本都知道Mybatis,Mybatis-plus。

在Mybatis-plus中操作單表非常方便,通過QueryWrapper,對於單表的操作非常的絲滑,沒有任何的思維負擔。

類似下面這樣:

        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(User::getUsername,"zhangsan").eq(User::getAge,18);
        userMapper.selectList(queryWrapper);

Go的orm

這裡的條件查詢都需要開發手動來拼接字串,如果專案比較大的話,就會看到漫天飛的SQL欄位,維護起來非常麻煩。

  var users []*User
  sqlResult := db.Where("username = ? and age = ?", "zhangsan", 18).Find(&users)

解決方式

寫了一段時間的Go之後,實在不想每次都寫這些字串拼接了,於是我給gorm封裝了一個gorm-plus。

這裡我使用到了go 1.18的泛型。泛型出了這麼久,也該使用上了。

其實就是把Mybatis-plus的那套語法借鑑了一下。(好吧,就是抄他的)

Mybatis-plus對於單表操作提供了非常多的CRUD操作。

我給gorm-plus 也提供了類似的操作

下面是具體用法

下載:

go get github.com/acmestack/gorm-plus

初始化sql

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users`  (
                          `id` int(0) NOT NULL AUTO_INCREMENT,
                          `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
                          `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
                          `age` int(0) NULL DEFAULT NULL,
                          `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
                          `score` int(0) NULL DEFAULT NULL,
                          `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
                          `dept` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
                          `created_at` datetime(0) NULL DEFAULT NULL,
                          `updated_at` datetime(0) NULL DEFAULT NULL,
                          PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;

連線資料庫

var GormDb *gorm.DB
func init() {
  dsn := "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
  var err error
  GormDb, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
    Logger: logger.Default.LogMode(logger.Info),
  })
  if err != nil {
    log.Fatalln(err)
  }
  gplus.Init(GormDb)
}

插入語句

  user := &User{Username: "zhangsan", Password: "123456", Age: 18, Score: 100, Dept: "A部門"}
  result := gplus.Insert(user)
  fmt.Println(result.RowsAffected)

查詢語句

根據id查詢:

注意這裡需要傳入泛型User

  user, resultDb := gplus.SelectById[User](1)
  fmt.Println(user, resultDb.RowsAffected)

根據ids查詢:

  var ids = []int{1,2}
  users, resultDb := gplus.SelectByIds[User](ids)
  fmt.Println(users, resultDb.RowsAffected)

條件查詢:

  q := gplus.NewQuery[User]()
  q.Eq("username", "zhangsan").Eq("age",18)
  users, resultDb := gplus.SelectList(q)
  fmt.Println(users,resultDb.RowsAffected)

對比一下Mybatis-plus寫法基本一致了。

        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(User::getUsername,"zhangsan").eq(User::getAge,18);
        userMapper.selectList(queryWrapper);

更多操作請檢視:

github.com/acmestack/g…

gplus工具

其實上面的寫法還是需要寫資料庫的欄位名,如果資料庫的欄位名很多,我們很容易寫錯,導致不必要的bug 產生。

一旦名稱長,非常容易誤寫,而且如果有欄位名稱修改的話,還需要全域性搜尋一個個地修改,比較麻煩。

Go沒有提供類似Java的lambad表示式或者C#中 nameof 方式直接獲取某個物件的欄位名稱的操作,但是我們可以通過生成程式碼的方式生成欄位名。

所有就有了gplus,它作用就是自動識別結構體,把結構體的欄位名生成出來。

下載使用:

go install github.com/acmestack/gorm-plus/cmd/gplus@latest

通過 gplus gen paths=路徑,gplus 會自動識別帶有// +gplus:column=true註釋的結構體,給這個結構體生成欄位。

gplus 會在輸入的路徑下面生成 zz_gen.column.go檔案。

例如:

在example目錄下建立了了一個users.go 目錄,執行 gplus gen paths=./eample

users.go

// +gplus:column=true
type User struct {
  ID        int64
  Username  string `gorm:"column:username"`
  Password  string
  Address   string
  Age       int
  Phone     string
  Score     int
  Dept      string
  CreatedAt time.Time
  UpdatedAt time.Time
}

zz_gen.column.go (自動生成的)

var UserColumn = struct {
  ID        string
  Username  string
  Password  string
  Address   string
  Age       string
  Phone     string
  Score     string
  Dept      string
  CreatedAt string
  UpdatedAt string
}{
  ID:        "id",
  Username:  "username",
  Password:  "password",
  Address:   "address",
  Age:       "age",
  Phone:     "phone",
  Score:     "score",
  Dept:      "dept",
  CreatedAt: "created_at",
  UpdatedAt: "updated_at",
}

其實你自己也可以手寫這個檔案,只不過通過工具生成更加方便而已。

有了這個檔案,我們的查詢就變成這樣:

  q := gplus.NewQuery[User]()
  q.Eq(UserColumn.Username, "zhangsan").Eq(UserColumn.Age,18)
  users, resultDb := gplus.SelectList(q)
  fmt.Println(users,resultDb.RowsAffected)

這樣維護起來就非常方便了。

最後

更多的用法請檢視github地址:https://github.com/acmestack/gorm-plus

以上就是Go ORM的封裝解決方式詳解的詳細內容,更多關於Go ORM封裝的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com