开放代码社区 › zorm

zorm

开发工具Go

Go轻量ORM,零依赖,零侵入分布式事务,支持达梦(dm),金仓(kingbase),神通(shentong),南通(gbase),高斯(openGauss),TDengine,mysql,postgresql,oracle,mssql,sqlite,db2,clickhouse... 社区微信: ZORM927 基于原生sql语句,学习成本更低 代码精简,主体3000行,零依赖5000行,注释详细,方便定制修改 支持事务传播,这是zorm诞生的主要原因 支持dm(达梦),kingbase(金仓),shentong(神通),gbase(南通),高斯(openGauss),TDengine,my

AI/机器学习Web后端前端工具数据库
作者:用户6868语言:Go更新:2026-11-03
仓库地址
https://gitee.com/chunanyong/zorm.git

前往原仓库

README

## 介绍
![zorm logo](https://web-code-img.oss-cn-shanghai.aliyuncs.com/readme/fbb2049a49f68a06dfbc3f57.png)  
Go轻量ORM,零依赖,零侵入分布式事务,支持达梦(dm),金仓(kingbase),神通(shentong),南通(gbase),高斯(openGauss),TDengine,mysql,postgresql,oracle,mssql,sqlite,db2,clickhouse... 

官网: https://zorm.cn    
源码地址: https://gitee.com/chunanyong/zorm || https://gitcode.com/springrain/zorm  
测试用例: https://gitee.com/wuxiangege/zorm-examples/  	  
视频教程: https://www.bilibili.com/video/BV1L24y1976U/     
 
社区微信: [ZORM927]()   

``` 
go get gitee.com/chunanyong/zorm 
```  
* 基于原生sql语句,学习成本更低  
* [代码生成器](https://gitee.com/zhou-a-xing/zorm-generate-struct)	
* 代码精简,主体3000行,零依赖5000行,注释详细,方便定制修改   
* <font color=red>支持事务传播,这是zorm诞生的主要原因</font>
* 支持dm(达梦),kingbase(金仓),shentong(神通),gbase(南通),高斯(openGauss),TDengine,mysql,postgresql,oracle,mssql,sqlite,db2,clickhouse...
* 支持多库和读写分离
* 不支持联合主键,变通认为无主键,业务控制实现(艰难取舍)  
* 支持seata,hptx,dbpack分布式事务,支持全局事务托管,不修改业务代码,零侵入分布式事务
* 支持clickhouse,更新和删除语句使用SQL92标准语法

## 事务传播
事务传播是zorm的核心功能,也是zorm所有方法都有ctx入参的主要原因.	
zorm的事务操作需要显式使用```zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {})```开启,在执行闭包函数前检查事务,如果ctx里有事务就加入事务,如果ctx里没事务就创建新的事务,所以只需要传递同一个ctx对象,就可以实现事务传播.特殊场景如果不想事务同步,就可以声明一个新的ctx对象,做事务隔离.

## 源码仓库说明
我主导的开源项目主库都在gitee,github上留有项目说明,引导跳转到gitee,这样也造成了项目star增长缓慢,毕竟github用户多些.  
尽我所能,支持国内开源社区,不喜勿喷,谢谢!

## 支持国产数据库  
zorm对国产数据库的适配不遗余力,遇到没有适配或者有问题的国产数据库,请反馈到社区,携手共建国产软件生态.		
### 达梦(dm)  
- 配置zorm.DataSourceConfig的 ```DriverName:dm ,Dialect:dm```  
- 达梦数据库驱动: gitee.com/chunanyong/dm	
- ~~达梦的TEXT类型会映射为dm.DmClob,string不能接收,需要实现zorm.ICustomDriverValueConver接口,自定义扩展处理~~  
- 达梦最新驱动已经支持TEXT类型使用string接收,不需要自定义扩展了  
```go
import (
	// 00.引入数据库驱动
	"gitee.com/chunanyong/dm"
	"io"
)

// CustomDMText 实现ICustomDriverValueConver接口,扩展自定义类型,例如 达梦数据库TEXT类型,映射出来的是dm.DmClob类型,无法使用string类型直接接收
type CustomDMText struct{}

// GetDriverValue 根据数据库列类型,返回driver.Value的实例,struct属性类型
// map接收或者字段不存在,无法获取到structFieldType,会传入nil
func (dmtext CustomDMText) GetDriverValue(ctx context.Context, columnType *sql.ColumnType, structFieldType *reflect.Type) (driver.Value, error) {
	// 如果需要使用structFieldType,需要先判断是否为nil
	// if structFieldType != nil {
	// }

	return &dm.DmClob{}, nil
}

// ConverDriverValue 数据库列类型,GetDriverValue返回的driver.Value的临时接收值,struct属性类型
// map接收或者字段不存在,无法获取到structFieldType,会传入nil
// 返回符合接收类型值的指针,指针,指针!!!!
func (dmtext CustomDMText) ConverDriverValue(ctx context.Context, columnType *sql.ColumnType, tempDriverValue driver.Value, structFieldType *reflect.Type) (interface{}, error) {
	// 如果需要使用structFieldType,需要先判断是否为nil
	// if structFieldType != nil {
	// }

	// 类型转换
	dmClob, isok := tempDriverValue.(*dm.DmClob)
	if !isok {
		return tempDriverValue, errors.New("->ConverDriverValue-->转换至*dm.DmClob类型失败")
	}
	if dmClob == nil || !dmClob.Valid {
		return new(string), nil
	}
	// 获取长度
	dmlen, errLength := dmClob.GetLength()
	if errLength != nil {
		return dmClob, errLength
	}

	// int64转成int类型
	dmlenInt, errConvert := zorm.typeConvertInt64toInt(dmlen)
	if errConvert != nil {
		return dmClob, errConvert
	}

	// 读取字符串
	str, errReadString := dmClob.ReadString(1, dmlenInt)

	// 处理空字符串或NULL造成的EOF错误
	if errReadString == io.EOF {
		return new(string), nil
	}

	return &str, errReadString
}
// RegisterCustomDriverValueConver 注册自定义的字段处理逻辑,用于驱动无法直接转换的场景.
// 一般是放到init方法里进行注册
func init() {
	// dialectColumnType 值是 Dialect.字段类型(大写) ,例如 dm.TEXT
	zorm.RegisterCustomDriverValueConver("dm.TEXT", CustomDMText{})
}
```

### 金仓(kingbase)  
- 配置zorm.DataSourceConfig的 ```DriverName:kingbase ,Dialect:kingbase```	
- 金仓官方驱动: https://www.kingbase.com.cn/qd/index.htm   https://bbs.kingbase.com.cn/thread-14457-1-1.html?_dsign=87f12756	  
- 金仓kingbase 8核心是基于postgresql 9.6,可以使用 https://github.com/lib/pq 进行测试,生产环境建议使用官方驱动.	
- 注意修改数据库的 data/kingbase.conf中 ora_input_emptystr_isnull = false 或者是ora_input_emptystr_isnull = on (根据版本进行区分),因为golang没有null值,一般数据库都是not null,golang的string默认是'',如果这个设置为true,数据库就会把值设置为null,和字段属性not null 冲突,因此报错.配置文件修改后,进行数据库的重启.

### 神通(shentong)  
建议使用官方驱动,配置zorm.DataSourceConfig的 ```DriverName:aci ,Dialect:shentong```  

### 南通(gbase)
~~暂时还未找到官方Go驱动,配置zorm.DataSourceConfig的 DriverName:gbase ,Dialect:gbase~~  
暂时先使用odbc驱动,```DriverName:odbc ,Dialect:gbase```

### 高斯(openGauss)  
官方驱动:https://gitee.com/opengauss/openGauss-connector-go-pq    
配置zorm.DataSourceConfig的 ```DriverName:opengauss ,Dialect:postgresql```  

### TDengine  
- 因TDengine驱动不支持事务,需要设置```DisableTransaction=true```
- 配置zorm.DataSourceConfig的 ```DriverName:taosSql或者taosRestful, Dialect:tdengine``` 
- zorm.DataSourceConfig的```InsertSQLNoColumn ```insert语句中是否没有列名.true没有列名,插入值和数据库列顺序保持一致,减少语句长度
- 测试用例: https://zorm.cn/docs/zorm_tdengine_3_test   
- TDengine已收录: https://github.com/taosdata/awesome-tdengine/#orm   

## 数据库脚本和实体类
生成实体类或手动编写,建议使用代码生成器 https://gitee.com/zhou-a-xing/zorm-generate-struct
```go 

package testzorm

import (
	"time"

	"gitee.com/chunanyong/zorm"
)

// 建表语句

/*

DROP TABLE IF EXISTS `t_demo`;
CREATE TABLE `t_demo`  (
  `id` varchar(50)  NOT NULL COMMENT '主键',
  `user_name` varchar(30)  NOT NULL COMMENT '姓名',
  `password` varchar(50)  NOT NULL COMMENT '密码',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
  `active` int  COMMENT '是否有效(0否,1是)',
  PRIMARY KEY (`id`)
) ENGINE = InnoDB CHARACTER SET = utf8mb4  COMMENT = '例子' ;

*/

// demoStructTableName 表名常量,方便直接调用
const demoStructTableName = "t_demo"

// demoStruct 例子
type demoStruct struct {
	// 引入默认的struct,隔离IEntityStruct的方法改动
	zorm.EntityStruct

	// Id 主键
	Id string `column:"id"`

	// UserName 姓名
	UserName string `column:"user_name"`

	// Password 密码
	Password string `column:"password"`

	// CreateTime <no value>
	CreateTime time.Time `column:"create_time"`

	// Active 是否有效(0否,1是)
	// Active int `column:"active"`

	// ------------------数据库字段结束,自定义字段写在下面---------------// 
	// 如果查询的字段在column tag中没有找到,就会根据名称(不区分大小写,支持 _ 下划线转驼峰)映射到struct的属性上

	// 模拟自定义的字段Active
	Active int
}

// GetTableName 获取表名称
// IEntityStruct 接口的方法,实体类需要实现!!!
func (entity *demoStruct) GetTableName() string {