Golang:数据库ORM框架GoMybatis详解

zhuxiujia · · 2618 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

## 前言 学习过原生的Go链接Mysql的方法,使用Go自带的“database/sql”数据库链接api。</br> “github.com/go-sql-driver/mysql”mysql驱动,通过比较原生的方法去写sql和处理事务。</br> <p>目前开源界也有好多封装好的Orm操作框架:</p> Java系(Mybatis,MybatisPlus,hibernate ...)</br> Go系 (xorm,gorm,gomybatis ...)</br> 总所周知,在java/go系orm框架中,国外互联网公司程序员喜欢用hibernate/gorm(这是因为国外产品稳定,需求 清晰,代码素质普遍高),反观国内java系互联网公司则喜欢mybatis之流(需求频繁变动,水平参差不齐,sql优化方便)</br> * 我的观点</br> JPA/Hibernate.Gorm之流都比较复杂,团队中要有人Hold住它,否则后期及其容易踩坑;</br> Mybatis框架直观,但是基本crud功能应该简化一下,毕竟重复度极高(重点:而且简化的同时可以扩展逻辑)</br> ## GoMybatis 框架介绍: GoMybatis是一款总结了以上许多框架的优缺点的ORM框架,并且极力希望在 维护性/可读性/性能/易用性/之上做到均衡(正如go的思想一样,大道至简)</br> 项目地址 https://github.com/zhuxiujia/GoMybatis</br> GoMybatis并不是一夜之间想出来的,之前阅读过Hibernate、MybatisPlus ,xorm,gorm,gobatis(需要go generate生成中间代码)还有go标准库(database/sql)的部分源码,总结经验而来。</br> * GoMybatis 特性介绍: 整体基于标准库“database/sql”开发而来,干净无依赖,结构体+闭包+反射+代理实现 而不是使用go generate生成*.go等中间代码(利用反射函数闭包解决了gobatis框架免去生成代码的缺陷)</br> 吸收mybatis plus框架乐观锁,逻辑删除功能(当然你也可以手动在sql中加入,就是比较繁琐 好处是可读性高)</br> 实现了AST抽象语法树,能在#{}表达式中灵活使用公式</br> 增加了模板标签,减少普通CRUD操作的重复度(mybatis框架是没有的,意味着普通增删改查也得写sql)</br> * gomybatis+mysql数据库使用教程: ## 使用教程 > 教程源码 https://github.com/zhuxiujia/GoMybatis/tree/master/example 设置好GoPath,用go get 命令下载GoMybatis和对应的数据库驱动 ``` bash go get github.com/zhuxiujia/GoMybatis go get github.com/go-sql-driver/mysql ``` 实际使用mapper 定义xml内容,建议以*Mapper.xml文件存于项目目录中,在编辑xml时就可享受GoLand等IDE渲染和智能提示。生产环境可以使用statikFS把xml文件打包进程序里 ``` xml var xmlBytes = []byte(` <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://raw.githubusercontent.com/zhuxiujia/GoMybatis/master/mybatis-3-mapper.dtd"> <mapper> <select id="SelectAll"> select * from biz_activity where delete_flag=1 order by create_time desc </select> </mapper> `) ``` ``` go import ( "fmt" _ "github.com/go-sql-driver/mysql" //选择需要的数据库驱动导入 "github.com/zhuxiujia/GoMybatis" ) type ExampleActivityMapperImpl struct { SelectAll func() ([]Activity, error) } func main() { var engine = GoMybatis.GoMybatisEngine{}.New() //Mysql链接格式 用户名:密码@(数据库链接地址:端口)/数据库名称,如root:123456@(***.com:3306)/test err := engine.Open("mysql", "*?charset=utf8&parseTime=True&loc=Local") if err != nil { panic(err) } var exampleActivityMapperImpl ExampleActivityMapperImpl //加载xml实现逻辑到ExampleActivityMapperImpl engine.WriteMapperPtr(&exampleActivityMapperImpl, xmlBytes) //使用mapper result, err := exampleActivityMapperImpl.SelectAll(&result) if err != nil { panic(err) } fmt.Println(result) } ``` ## 功能:模板标签CRUD 简化(必须依赖一个resultMap 标签) ``` xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://raw.githubusercontent.com/zhuxiujia/GoMybatis/master/mybatis-3-mapper.dtd"> <mapper> <!--logic_enable 逻辑删除字段--> <!--logic_deleted 逻辑删除已删除字段--> <!--logic_undelete 逻辑删除 未删除字段--> <!--version_enable 乐观锁版本字段,支持int,int8,int16,int32,int64--> <resultMap id="BaseResultMap" tables="biz_activity"> <id column="id" property="id"/> <result column="name" property="name" langType="string"/> <result column="pc_link" property="pcLink" langType="string"/> <result column="h5_link" property="h5Link" langType="string"/> <result column="remark" property="remark" langType="string"/> <result column="version" property="version" langType="int" version_enable="true"/> <result column="create_time" property="createTime" langType="time.Time"/> <result column="delete_flag" property="deleteFlag" langType="int" logic_enable="true" logic_undelete="1" logic_deleted="0"/> </resultMap> <!--模板标签: columns wheres sets 支持逗号,分隔表达式,*?* 为判空表达式--> <!--插入模板:默认id="insertTemplete,test="field != null",where自动设置逻辑删除字段,支持批量插入" --> <insertTemplete/> <!--查询模板:默认id="selectTemplete,where自动设置逻辑删除字段--> <selectTemplete wheres="name?name = #{name}"/> <!--更新模板:默认id="updateTemplete,set自动设置乐观锁版本号--> <updateTemplete sets="name?name = #{name},remark?remark=#{remark}" wheres="id?id = #{id}"/> <!--删除模板:默认id="deleteTemplete,where自动设置逻辑删除字段--> <deleteTemplete wheres="name?name = #{name}"/> </mapper> ``` xml对应以下定义的Mapper结构体方法 ```go type Activity struct { Id string `json:"id"` Uuid string `json:"uuid"` Name string `json:"name"` PcLink string `json:"pcLink"` H5Link string `json:"h5Link"` Remark string `json:"remark"` Version int `json:"version"` CreateTime time.Time `json:"createTime"` DeleteFlag int `json:"deleteFlag"` } type ExampleActivityMapper struct { SelectTemplete func(name string) ([]Activity, error) `mapperParams:"name"` InsertTemplete func(arg Activity) (int64, error) InsertTempleteBatch func(args []Activity) (int64, error) `mapperParams:"args"` UpdateTemplete func(arg Activity) (int64, error) `mapperParams:"name"` DeleteTemplete func(name string) (int64, error) `mapperParams:"name"` } ``` ## 功能:动态数据源 ``` go //添加第二个mysql数据库,请把MysqlUri改成你的第二个数据源链接 GoMybatis.Open("mysql", MysqlUri) //动态数据源路由 var router = GoMybatis.GoMybatisDataSourceRouter{}.New(func(mapperName string) *string { //根据包名路由指向数据源 if strings.Contains(mapperName, "example.") { var url = MysqlUri//第二个mysql数据库,请把MysqlUri改成你的第二个数据源链接 fmt.Println(url) return &url } return nil }) ``` ## 功能:自定义日志输出 ``` go engine.SetLogEnable(true) engine.SetLog(&GoMybatis.LogStandard{ PrintlnFunc: func(messages []byte) { //do someting save messages }, }) ``` ## 功能:异步日志-基于消息队列日志 ![Image text](https://zhuxiujia.github.io/gomybatis.io/assets/log_system.png) ## 功能:嵌套事务-事务传播行为 <table> <thead> <tr><th>事务类型</th> <th>说明</th> </tr> </thead> <tbody><tr><td>PROPAGATION_REQUIRED</td><td>表示如果当前事务存在,则支持当前事务。否则,会启动一个新的事务。默认事务类型。</td></tr> <tr><td>PROPAGATION_SUPPORTS</td><td>表示如果当前事务存在,则支持当前事务,如果当前没有事务,就以非事务方式执行。</td></tr> <tr><td>PROPAGATION_MANDATORY</td><td>表示如果当前事务存在,则支持当前事务,如果当前没有事务,则返回事务嵌套错误。</td></tr> <tr><td>PROPAGATION_REQUIRES_NEW</td><td>表示新建一个全新Session开启一个全新事务,如果当前存在事务,则把当前事务挂起。</td></tr> <tr><td>PROPAGATION_NOT_SUPPORTED</td><td>表示以非事务方式执行操作,如果当前存在事务,则新建一个Session以非事务方式执行操作,把当前事务挂起。</td></tr> <tr><td>PROPAGATION_NEVER</td><td>表示以非事务方式执行操作,如果当前存在事务,则返回事务嵌套错误。</td></tr> <tr><td>PROPAGATION_NESTED</td><td>表示如果当前事务存在,则在嵌套事务内执行,如嵌套事务回滚,则只会在嵌套事务内回滚,不会影响当前事务。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。</td></tr> <tr><td>PROPAGATION_NOT_REQUIRED</td><td>表示如果当前没有事务,就新建一个事务,否则返回错误。</td></tr></tbody> </table> ``` go //嵌套事务的服务 type TestService struct { exampleActivityMapper *ExampleActivityMapper //服务包含一个mapper操作数据库,类似java spring mvc UpdateName func(id string, name string) error `tx:"" rollback:"error"` UpdateRemark func(id string, remark string) error `tx:"" rollback:"error"` } func main() { var testService TestService testService = TestService{ exampleActivityMapper: &exampleActivityMapper, UpdateRemark: func(id string, remark string) error { testService.exampleActivityMapper.SelectByIds([]string{id}) panic(errors.New("业务异常")) // panic 触发事务回滚策略 return nil // rollback:"error"指定了返回error类型 且不为nil 就会触发事务回滚策略 }, UpdateName: func(id string, name string) error { testService.exampleActivityMapper.SelectByIds([]string{id}) return nil }, } GoMybatis.AopProxyService(&testService, &engine)//必须使用AOP代理服务的func testService.UpdateRemark("1","remark") } ``` ## 功能:内置xml生成工具- 根据用户定义的struct结构体生成对应的 mapper.xml ``` go //step1 定义你的数据库模型,必须包含 json注解(默认为数据库字段), gm:""注解指定 值是否为 id,version乐观锁,logic逻辑软删除 type UserAddress struct { Id string `json:"id" gm:"id"` UserId string `json:"user_id"` RealName string `json:"real_name"` Phone string `json:"phone"` AddressDetail string `json:"address_detail"` Version int `json:"version" gm:"version"` CreateTime time.Time `json:"create_time"` DeleteFlag int `json:"delete_flag" gm:"logic"` } ``` * 第二步,在你项目main 目录下建立一个 XmlCreateTool.go 内容如下 ``` func main() { var bean = UserAddress{} //此处只是举例,应该替换为你自己的数据库模型 GoMybatis.OutPutXml(reflect.TypeOf(bean).Name()+"Mapper.xml", GoMybatis.CreateXml("biz_"+GoMybatis.StructToSnakeString(bean), bean)) } ``` * 第三步,执行命令,在当前目录下得到 UserAddressMapper.xml文件 ``` go go run XmlCreateTool.go ``` * 以下是自动生成的xml文件内容 ``` xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://raw.githubusercontent.com/zhuxiujia/GoMybatis/master/mybatis-3-mapper.dtd"> <mapper> <!--logic_enable 逻辑删除字段--> <!--logic_deleted 逻辑删除已删除字段--> <!--logic_undelete 逻辑删除 未删除字段--> <!--version_enable 乐观锁版本字段,支持int,int8,int16,int32,int64--> <resultMap id="BaseResultMap" tables="biz_user_address"> <id column="id" property="id"/> <result column="id" property="id" langType="string" /> <result column="user_id" property="user_id" langType="string" /> <result column="real_name" property="real_name" langType="string" /> <result column="phone" property="phone" langType="string" /> <result column="address_detail" property="address_detail" langType="string" /> <result column="version" property="version" langType="int" version_enable="true" /> <result column="create_time" property="create_time" langType="Time" /> <result column="delete_flag" property="delete_flag" langType="int" logic_enable="true" logic_undelete="1" logic_deleted="0" /> </resultMap> </mapper> ``` ## 配套生态(RPC,JSONRPC,Consul)-搭配GoMybatis * https://github.com/zhuxiujia/easy_mvc //mvc,极大简化开发流程 * https://github.com/zhuxiujia/easyrpc //easyrpc(基于标准库的RPC)吸收GoMybatis的概念,类似标准库的api,定义服务没有标准库的要求那么严格(可选不传参数,或者只有一个参数,只有一个返回值) * https://github.com/zhuxiujia/easyrpc_discovery //基于easyrpc定制微服务发现,支持动态代理,支持GoMybatis事务,AOP代理,事务嵌套,tag定义事务,自带负载均衡算法(随机,加权轮询,源地址哈希法) ![Image text](https://zhuxiujia.github.io/gomybatis.io/assets/easy_consul.png)

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2618 次点击  ∙  1 赞  
加入收藏 微博
1 回复  |  直到 2022-10-10 12:50:57
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传