Golang高效ORM框架GDAO:提升开发效率与性能的轻量级解决方案

Golang高效ORM框架GDAO:提升开发效率与性能的轻量级解决方案

GDAO —— Go 持久化框架

一个简化的 Go 持久化框架,具有更高的生产力和性能
为 Go 应用结合 Hibernate 的抽象性与 MyBatis 的灵活性

简介

Gdao 是一个创新的持久层解决方案,旨在减少编码工作量、提高生产力、增强性能、支持多数据源集成以及促进数据读写分离。通过利用 Gdao,开发者可以将持久层所需的代码量减少 30% 到 50%,建立统一的编码标准,减少错误,并确保更易于维护和扩展。

Gdao 对于 Go 而言,相当于 Hibernate + MyBatis 对于 Java。Gdao 框架结合了 Hibernate 的抽象性和 MyBatis 的灵活性,并解决了它们各自在 ORM 框架上长期使用存在的痛点。关于 Hibernate 和 MyBatis 之间的痛点,请参考 jdao 使用文档

  • GDAO 的设计结构既简洁又严谨,所有接口和函数的命名都具有描述性,其用途一目了然。
  • 即使您是 GDAO 的新手,也能快速理解其代码和相关的数据行为。
  • GDAO 的简洁性确保您能在几分钟内掌握其用法。

Github

官方网站

文档

演示

主要特性

  1. 代码生成:使用 Gdao 代码生成工具为数据库表创建标准化的实体类,类似于 Thrift/Protobuf。
  2. 高效序列化:表的标准化实体类实现了高效的序列化和反序列化,性能更高,数据量更小。
  3. 支持数据读写分离:Gdao 支持绑定多个数据源和数据读写分离,允许将数据源绑定到表、类、映射接口和其他属性。
  4. 支持数据缓存:Gdao 支持数据缓存,并详细控制缓存数据的生命周期和回收功能。
  5. 广泛的兼容性:Gdao 理论上支持所有实现了 Go 数据库驱动接口的数据库。
  6. 高级功能:支持事务、存储过程、批处理和其他数据库操作。
  7. 支持 SQL 与程序分离:类似于 mybatis,gdao 支持通过 xml 文件编写 sql 映射调用。这是少数支持 SQL 与程序分离的 ORM 特性之一,但功能非常强大。

GDAO 的创新性 ORM 解决方案

Gdao 是 Jdao 的兄弟框架,共享其设计模式。

  1. 用于单表 CRUD 操作的标准化映射实体类:超过 90% 的单表操作可以通过实体类执行。这些 CRUD 操作通常不涉及复杂的 SQL 优化,可以通过实体类生成,以减少错误率并简化维护。 通过利用缓存和读写分离等机制,持久层变得更加高效和便捷。标准化实体类的数据操作格式不仅仅是对象函数的拼接,更类似于 SQL 操作,更容易理解。
  2. 复杂 SQL 的执行:复杂的 SQL,特别是多表连接,通常需要根据表结构和索引属性进行优化。使用对象来拼接复杂的 SQL 会增加理解的难度,并可能导致开发者不知道最终执行的 SQL,从而增加风险和维护难度。 因此,Gdao 建议使用 Gdao 的 CRUD 接口来处理复杂的 SQL 问题。Gdao 提供了灵活的数据转换和高效的对象映射实现,避免了过度使用反射等耗时操作。
  3. SQL 映射文件:对于复杂的 SQL 操作,Gdao 提供了相应的 CRUD 接口。它还支持通过 XML 配置映射 SQL 以进行接口调用,类似于 Java 的 MyBatis ORM 框架。然而,与 MyBatis 需要映射所有 SQL 操作不同,Gdao 提供了完整的 SQL 映射接口,但建议仅映射复杂的 SQL 或标准化实体类无法完成的操作。 Gdao 的 SQL 配置文件参考了 MyBatis 的配置文件格式,实现了一个新的解析器,允许更高的灵活性和对配置参数类型的容忍度(请参阅文档)。

图片


核心组件

1. gdao

主要核心入口点,提供以下功能:

  • 设置数据源
  • SQL CRUD 函数

2. gdaoCache

缓存入口点,支持以下功能:

  • 绑定或解绑包、类和其他属性以启用或禁用查询缓存

3. gdaoSlave

读写分离操作入口点,支持以下功能:

  • 绑定或解绑包、类和其他属性以启用或禁用读写分离

4. gdaoMapper

通过调用 Gdao 的 CRUD 接口直接执行 SQL,或使用 XML 文件映射并通过 Mapper Id 通过 gdaoMapper 调用


快速入门

1. 安装

# 导入 gdao
go get github.com/donnie4w/gdao

2. 配置数据源

gdao.Init(mysqlDB, gdao.MYSQL)
// gdao.MYSQL 是数据库类型

3. 生成表实体类

使用 Gdao 代码生成工具为数据库表生成标准化的实体类。

4. 实体类操作

// 设置数据源
gdao.Init(mysqlDB, gdao.MYSQL)

// 读取
hs := dao.NewHstest()
hs.Where(hs.Id.EQ(10))
h, _ := hs.Select(hs.Id, hs.Value, hs.Rowname)
logger.Debug(h)
//[DEBUG][SELECT ONE][ select id, value, rowname from hstest where id=?][10]

// 更新
hs := dao.NewHstest()
hs.SetRowname("hello10")
hs.Where(hs.Id.EQ(10))
hs.Update()
//[DEBUG][UPDATE][update hstest set rowname=? where id=?][hello10 10]

// 删除
hs := dao.NewHstest()
hs.Where(hs.Id.EQ(10))
hs.Delete()
//[DEBUG][DELETE][delete from hstest where id=?][10]

// 插入
hs := dao.NewHstest()
hs.SetValue("hello123")
hs.SetLevel(12345)
hs.SetBody([]byte("hello"))
hs.SetRowname("hello1234")
hs.SetUpdatetime(time.Now())
hs.SetFloa(123456)
hs.SetAge(123)
hs.Insert()
//[DEBUG][INSERT][insert into hstest(floa, age, value, level, body, rowname, updatetime) values(?,?,?,?,?,?,?)][123456 123 hello123 12345 [104 101 108 108 111] hello1234 2024-07-17 19:36:44]

5. gdao api

CRUD 操作
// 查询,返回单条记录
bean, _ := gdao.ExecuteQueryBean("select id, value, rowname from hstest where id=?", 10)
logger.Debug(bean)

// 插入
i := gdao.ExecuteUpdate("insert into hstest2(rowname, value) values(?,?)", "helloWorld", "123456789")

// 更新
i := gdao.ExecuteUpdate("update hstest set value=? where id=1", "hello")

// 删除
i := gdao.ExecuteUpdate("delete from hstest where id = ?", 1)

6. gdaoCache

配置缓存
// 绑定 Hstest 以启用缓存,缓存过期时间设置为 300 秒
gdaoCache.BindClass[dao.Hstest]()

// 首次查询 Hstest 并根据条件设置数据缓存
hs := dao.NewHstest()
hs.Where((hs.Id.Between(0, 2)).Or(hs.Id.Between(10, 15)))
hs.Limit(3)
hs.Selects()

// 对于相同的条件,数据直接从缓存中获取
hs = dao.NewHstest()
hs.Where((hs.Id.Between(0, 2)).Or(hs.Id.Between(10, 15)))
hs.Limit(3)
hs.Selects()
执行结果
[DEBUG][SELECT LIST][ select id, age, rowname, value, updatetime, body, floa, level from hstest where id between ? and ? or (id between ? and ?) LIMIT ? ][0 2 10 15 3]
[DEBUG][SET CACHE][ select id, age, rowname, value, updatetime, body, floa, level from hstest where id between ? and ? or (id between ? and ?) LIMIT ? ][0 2 10 15 3]
[DEBUG][SELECT LIST][ select id, age, rowname, value, updatetime, body, floa, level from hstest where id between ? and ? or (id between ? and ?) LIMIT ? ][0 2 10 15 3]
[DEBUG][GET CACHE][ select id, age, rowname, value, updatetime, body, floa, level from hstest where id between ? and ? or (id between ? and ?) LIMIT ? ][0 2 10 15 3]

7. gdaoSlave

读写分离
// 设置从数据源:mysql
mysqlDB, _ := getDataSource("mysql.json")
gdaoSlave.BindClass[dao.Hstest](mysqlDB, gdao.MYSQL)
// 这里主数据库是 sqlite,从数据库是 mysql。Hstest 从 mysql 读取数据
hs := dao.NewHstest()
hs.Where(hs.Id.Between(0, 5))
hs.OrderBy(hs.Id.Desc())
hs.Limit(3)
hs.Selects()

8. gdaoMapper

使用 XML 映射 SQL
<!-- MyBatis 风格的 XML 配置文件 -->
<mapper namespace="user">
   <select id="selectHstest1" parameterType="int64" resultType="hstest1">
      SELECT * FROM hstest1 ORDER BY id DESC LIMIT #{limit}
   </select>
</mapper>
// 设置数据源
if db, err := getDataSource("sqlite.json"); err == nil {
   gdao.Init(db, gdao.SQLITE)  
   gdao.SetLogger(true)
}

// 读取并解析 XML 配置
hs1, _ := gdaoMapper.Select[dao.Hstest1]("user.selectHstest1", 1)
fmt.Println(hs1)

id, _ := gdaoMapper.Select[int64]("user.selectHstest1", 1)
fmt.Println(*id)
执行结果
[DEBUG][Mapper Id] user.selectHstest1 
SELECTONE SQL[SELECT * FROM hstest1 ORDER BY id DESC LIMIT ?]ARGS[1]
Id:52, Rowname:rowname>>>123456789, Value:[104 101 108 108 111 32 103 100 97 111], Goto:[49 50 51 52 53]
[DEBUG][Mapper Id] user.selectHstest1 
SELECTONE SQL[SELECT * FROM hstest1 ORDER BY id DESC LIMIT ?]ARGS[1]
52

更多关于Golang高效ORM框架GDAO:提升开发效率与性能的轻量级解决方案的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

gdaodemo

这是一个用于 Gdao 的测试演示程序,包含一个打包好的 SQLite 测试数据库文件 hstest.db,其中已生成数据。该演示程序可以直接运行。除了测试读写分离或多数据源操作外,其他测试默认操作 hstest.db 数据库中的数据,可以直接运行以查看数据操作结果。

更多关于Golang高效ORM框架GDAO:提升开发效率与性能的轻量级解决方案的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


GDAO框架确实为Go语言开发者提供了一个兼具Hibernate抽象性和MyBatis灵活性的持久层解决方案。以下通过具体示例展示其核心功能:

1. 数据源配置与实体类生成

// 多数据源配置示例
func initDataSources() {
    // 主数据源(MySQL)
    masterDB, _ := sql.Open("mysql", "user:pass@tcp(127.0.0.1:3306)/db")
    gdao.Init(masterDB, gdao.MYSQL)
    
    // 从数据源(PostgreSQL)
    slaveDB, _ := sql.Open("postgres", "host=localhost port=5432 user=postgres password=secret dbname=test sslmode=disable")
    gdaoSlave.BindClass[dao.User](slaveDB, gdao.POSTGRESQL)
}

// 生成的实体类使用示例
user := dao.NewUser()
user.SetUsername("john_doe")
user.SetEmail("john@example.com")
user.SetCreatedAt(time.Now())

2. 链式查询与条件组合

// 复杂查询条件构建
func queryUsers() {
    user := dao.NewUser()
    
    // 多条件组合查询
    user.Where(
        user.Age.GT(18).
        And(user.Status.EQ(1)).
        Or(user.Email.Like("%@gmail.com"))
    )
    
    // 排序和分页
    user.OrderBy(user.CreatedAt.Desc())
    user.Limit(10).Offset(0)
    
    // 选择特定字段
    results, err := user.Selects(user.ID, user.Username, user.Email)
    if err != nil {
        log.Fatal(err)
    }
    
    for _, u := range results {
        fmt.Printf("User: %+v\n", u)
    }
}

3. 事务处理示例

func transferFunds(fromID, toID int64, amount float64) error {
    // 开启事务
    tx, err := gdao.Begin()
    if err != nil {
        return err
    }
    defer func() {
        if err != nil {
            tx.Rollback()
        }
    }()
    
    // 扣款操作
    accountFrom := dao.NewAccount()
    accountFrom.SetBalance(accountFrom.Balance.Sub(amount))
    accountFrom.Where(accountFrom.ID.EQ(fromID))
    if _, err := accountFrom.Update(); err != nil {
        return err
    }
    
    // 存款操作
    accountTo := dao.NewAccount()
    accountTo.SetBalance(accountTo.Balance.Add(amount))
    accountTo.Where(accountTo.ID.EQ(toID))
    if _, err := accountTo.Update(); err != nil {
        return err
    }
    
    // 提交事务
    return tx.Commit()
}

4. 批量操作优化

func batchInsertUsers(users []*dao.User) {
    // 开启批量模式
    gdao.StartBatch()
    defer gdao.EndBatch()
    
    for _, user := range users {
        user.Insert()
    }
    
    // 执行批量提交
    if err := gdao.ExecuteBatch(); err != nil {
        log.Printf("Batch insert failed: %v", err)
    }
}

5. 自定义SQL映射与动态查询

<!-- user_mapper.xml -->
<mapper namespace="user">
    <select id="searchUsers" parameterType="map" resultType="user">
        SELECT * FROM users 
        WHERE 1=1
        <if test="username != null">
            AND username LIKE CONCAT('%', #{username}, '%')
        </if>
        <if test="minAge != null">
            AND age >= #{minAge}
        </if>
        <if test="maxAge != null">
            AND age <= #{maxAge}
        </if>
        ORDER BY ${orderBy} ${orderDir}
        LIMIT #{limit} OFFSET #{offset}
    </select>
</mapper>
// Go代码调用
func searchUsers(params map[string]interface{}) {
    results, err := gdaoMapper.Select[[]dao.User](
        "user.searchUsers", 
        params,
    )
    if err != nil {
        log.Fatal(err)
    }
    
    for _, user := range results {
        fmt.Printf("Found user: %s\n", user.GetUsername())
    }
}

6. 缓存策略配置

func setupCache() {
    // 类级别缓存,过期时间300秒
    gdaoCache.BindClass[dao.Product](300)
    
    // 方法级别缓存
    gdaoCache.BindMethod("GetHotProducts", 60)
    
    // 自定义缓存键生成
    gdaoCache.SetKeyGenerator(func(sql string, args ...interface{}) string {
        hash := sha256.Sum256([]byte(fmt.Sprintf("%s%v", sql, args)))
        return hex.EncodeToString(hash[:])
    })
}

7. 读写分离实战

func handleUserRequest() {
    user := dao.NewUser()
    
    // 写操作走主库
    user.SetUsername("new_user")
    user.Insert()  // 自动使用主库
    
    // 读操作走从库
    user.Where(user.ID.EQ(1))
    user.SelectOne()  // 自动使用从库
    
    // 强制使用主库读取
    gdaoSlave.UnbindClass[dao.User]()
    user.SelectOne()  // 使用主库
    gdaoSlave.BindClass[dao.User](slaveDB, gdao.MYSQL)
}

8. 存储过程调用

func callStoredProcedure() {
    // 调用存储过程并获取结果集
    results, err := gdao.ExecuteQuery(
        "CALL GetUserStatistics(?, ?)", 
        "2024-01-01", 
        "2024-12-31",
    )
    if err != nil {
        log.Fatal(err)
    }
    
    // 处理结果
    for results.Next() {
        var stat struct {
            Month    string
            NewUsers int
            Active   int
        }
        results.Scan(&stat.Month, &stat.NewUsers, &stat.Active)
        fmt.Printf("%s: %d new users, %d active\n", 
            stat.Month, stat.NewUsers, stat.Active)
    }
}

9. 性能监控集成

func withMetrics() {
    // 添加SQL执行监控
    gdao.SetInterceptor(func(sql string, args []interface{}, duration time.Duration) {
        metrics.RecordQuery(sql, duration)
        
        if duration > 100*time.Millisecond {
            log.Printf("Slow query detected: %s (took %v)", sql, duration)
        }
    })
    
    // 执行查询
    product := dao.NewProduct()
    product.Where(product.Price.GT(100))
    product.Selects()
}

这些示例展示了GDAO框架在实际生产环境中的应用方式。框架通过代码生成减少重复工作,通过缓存和读写分离提升性能,同时保持SQL的透明性和灵活性。实体类的链式API设计使得查询构建直观,而XML映射支持则解决了复杂SQL的管理问题。

回到顶部