zorm

package module
v1.5.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2022 License: Apache-2.0 Imports: 16 Imported by: 0

README

介绍

go(golang)轻量级ORM,零依赖,零侵入分布式事务,支持达梦(dm),金仓(kingbase),神通(shentong),南大通用(gbase),mysql,postgresql,oracle,mssql,sqlite,clickhouse数据库.
源码地址:https://gitee.com/chunanyong/zorm

作者博客:https://www.jiagou.com

交流QQ群:727723736 添加进入社区群聊,问题交流,技术探讨 社区微信: LAUV927

go get gitee.com/chunanyong/zorm 
  • 基于原生sql语句编写,是springrain的精简和优化.
  • 代码生成器
  • 代码精简,主体2500行,零依赖4000行,注释详细,方便定制修改.
  • 支持事务传播,这是zorm诞生的主要原因
  • 支持mysql,postgresql,oracle,mssql,sqlite,clickhouse,dm(达梦),kingbase(金仓),shentong(神通),gbase(南通),clickhouse数据库
  • 支持多库和读写分离
  • 更新性能zorm,gorm,xorm相当. 读取性能zorm比gorm,xorm快50%
  • 不支持联合主键,变通认为无主键,业务控制实现(艰难取舍)
  • 集成seata-golang,支持全局托管,不修改业务代码,零侵入分布式事务
  • 支持clickhouse,更新,删除语句使用SQL92标准语法.clickhouse-go官方驱动不支持批量insert语法,建议使用https://github.com/mailru/go-clickhouse

zorm生产环境使用参考: UserStructService.go

源码仓库说明

我主导的开源项目主库都在gitee,github上留有项目说明,引导跳转到gitee,这样也造成了项目star增长缓慢,毕竟github社区更加强大.
开源没有国界,开发者却有自己的祖国.
严格意义上,github是受美国法律管辖的 https://www.infoq.cn/article/SA72SsSeZBpUSH_ZH8XB
尽我所能,支持国内开源社区,不喜勿喷,谢谢!

支持国产数据库

达梦(dm)

配置zorm.DataSourceConfig的 DriverName:dm ,DBType:dm
达梦数据库驱动: https://gitee.com/chunanyong/dm
达梦的text类型会映射为dm.DmClob,string不能接收,需要实现zorm.CustomDriverValueConver接口,自定义扩展处理

人大金仓(kingbase)

配置zorm.DataSourceConfig的 DriverName:kingbase ,DBType:kingbase
金仓驱动说明: https://help.kingbase.com.cn/doc-view-8108.html
金仓kingbase 8核心是基于postgresql 9.6,可以使用 https://github.com/lib/pq 进行测试,生产环境建议使用官方驱动.
注意修改 data/kingbase.conf中 ora_input_emptystr_isnull = false,因为golang没有null值,一般数据库都是not null,golang的string默认是'',如果这个设置为true,数据库就会把值设置为null,和字段属性not null 冲突,因此报错.

神舟通用(shentong)

建议使用官方驱动,配置zorm.DataSourceConfig的 DriverName:aci ,DBType:shentong

南大通用(gbase)

暂时还未找到官方golang驱动,配置zorm.DataSourceConfig的 DriverName:gbase ,DBType:gbase
暂时先使用odbc驱动,DriverName:odbc ,DBType:gbase

测试用例

https://gitee.com/chunanyong/readygo/blob/master/test/testzorm/BaseDao_test.go

// zorm 使用原生的sql语句,没有对sql语法做限制.语句使用Finder作为载体
// 占位符统一使用?,zorm会根据数据库类型,自动替换占位符,例如postgresql数据库把?替换成$1,$2...
// zorm使用 ctx context.Context 参数实现事务传播,ctx从web层传递进来即可,例如gin的c.Request.Context()
// zorm的事务操作需要显示使用zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {})开启

数据库脚本和实体类

https://gitee.com/chunanyong/readygo/blob/master/test/testzorm/demoStruct.go

生成实体类或手动编写,建议使用代码生成器 https://gitee.com/zhou-a-xing/wsgt


package testzorm

import (
	"time"

	"gitee.com/chunanyong/zorm"
)

//建表语句

/*

DROP TABLE IF EXISTS `t_demo`;
CREATE TABLE `t_demo`  (
  `id` varchar(50)  NOT NULL COMMENT '主键',
  `userName` varchar(30)  NOT NULL COMMENT '姓名',
  `password` varchar(50)  NOT NULL COMMENT '密码',
  `createTime` 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:"userName"`

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

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

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

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

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


}

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

//GetPKColumnName 获取数据库表的主键字段名称.因为要兼容Map,只能是数据库的字段名称
//不支持联合主键,变通认为无主键,业务控制实现(艰难取舍)
//如果没有主键,也需要实现这个方法, return "" 即可
//IEntityStruct 接口的方法,实体类需要实现!!!
func (entity *demoStruct) GetPKColumnName() string {
	//如果没有主键
	//return ""
	return "id"
}

//newDemoStruct 创建一个默认对象
func newDemoStruct() demoStruct {
	demo := demoStruct{
		//如果Id=="",保存时zorm会调用zorm.FuncGenerateStringID(),默认时间戳+随机数,也可以自己定义实现方式,例如 zorm.FuncGenerateStringID=funcmyId
		Id:         zorm.FuncGenerateStringID(),
		UserName:   "defaultUserName",
		Password:   "defaultPassword",
		Active:     1,
		CreateTime: time.Now(),
	}
	return demo
}


测试用例即文档


// testzorm 使用原生的sql语句,没有对sql语法做限制.语句使用Finder作为载体
// 占位符统一使用?,zorm会根据数据库类型,自动替换占位符,例如postgresql数据库把?替换成$1,$2...
// zorm使用 ctx context.Context 参数实现事务传播,ctx从web层传递进来即可,例如gin的c.Request.Context()
// zorm的事务操作需要显示使用zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {})开启
package testzorm

import (
	"context"
	"fmt"
	"testing"
	"time"

	"gitee.com/chunanyong/zorm"

	//00.引入数据库驱动
	_ "github.com/go-sql-driver/mysql"
)

//dbDao 代表一个数据库,如果有多个数据库,就对应声明多个DBDao
var dbDao *zorm.DBDao

// ctx默认应该有 web层传入,例如gin的c.Request.Context().这里只是模拟
var ctx = context.Background()

//01.初始化DBDao
func init() {

	//自定义zorm日志输出
	//zorm.LogCallDepth = 4 //日志调用的层级
	//zorm.FuncLogError = myFuncLogError //记录异常日志的函数
	//zorm.FuncLogPanic = myFuncLogPanic //记录panic日志,默认使用defaultLogError实现
	//zorm.FuncPrintSQL = myFuncPrintSQL //打印sql的函数

	//自定义日志输出格式,把FuncPrintSQL函数重新赋值
	//log.SetFlags(log.LstdFlags)
	//zorm.FuncPrintSQL = zorm.FuncPrintSQL

	//dbDaoConfig 数据库的配置.这里只是模拟,生产应该是读取配置配置文件,构造DataSourceConfig
	dbDaoConfig := zorm.DataSourceConfig{
		//DSN 数据库的连接字符串
		DSN: "root:root@tcp(127.0.0.1:3306)/readygo?charset=utf8&parseTime=true",
		//数据库驱动名称:mysql,postgres,oci8,sqlserver,sqlite3,clickhouse,dm,kingbase,aci 和DBType对应,处理数据库有多个驱动
		DriverName: "mysql",
		//数据库类型(方言判断依据):mysql,postgresql,oracle,mssql,sqlite,clickhouse,dm,kingbase,shentong 和 DriverName 对应,处理数据库有多个驱动
		DBType: "mysql",
		//MaxOpenConns 数据库最大连接数 默认50
		MaxOpenConns: 50,
		//MaxIdleConns 数据库最大空闲连接数 默认50
		MaxIdleConns: 50,
		//ConnMaxLifetimeSecond 连接存活秒时间. 默认600(10分钟)后连接被销毁重建.避免数据库主动断开连接,造成死连接.MySQL默认wait_timeout 28800秒(8小时)
		ConnMaxLifetimeSecond: 600,
		//PrintSQL 打印SQL.会使用FuncPrintSQL记录SQL
		PrintSQL: true,
		//DefaultTxOptions 事务隔离级别的默认配置,默认为nil
		//DefaultTxOptions: nil,
		//如果是使用seata-golang分布式事务,建议使用默认配置
		//DefaultTxOptions: &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false},

		//FuncSeataGlobalTransaction seata-golang分布式的适配函数,返回ISeataGlobalTransaction接口的实现
	    //FuncSeataGlobalTransaction : MyFuncSeataGlobalTransaction,
	}

	// 根据dbDaoConfig创建dbDao, 一个数据库只执行一次,第一个执行的数据库为 defaultDao,后续zorm.xxx方法,默认使用的就是defaultDao
	dbDao, _ = zorm.NewDBDao(&dbDaoConfig)
}

//TestInsert 02.测试保存Struct对象
func TestInsert(t *testing.T) {

	//需要手动开启事务,匿名函数返回的error如果不是nil,事务就会回滚
	//如果全局DefaultTxOptions配置不满足需求,可以在zorm.Transaction事务方法前设置事务的隔离级别,
	//例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions为nil,使用全局DefaultTxOptions
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		//创建一个demo对象
		demo := newDemoStruct()

		//保存对象,参数是对象指针.如果主键是自增,会赋值到对象的主键属性
		_, err := zorm.Insert(ctx, &demo)

		//如果返回的err不是nil,事务就会回滚
		return nil, err
	})
	//标记测试失败
	if err != nil {
		t.Errorf("错误:%v", err)
	}
}

//TestInsertSlice 03.测试批量保存Struct对象的Slice
//如果是自增主键,无法对Struct对象里的主键属性赋值
func TestInsertSlice(t *testing.T) {

	//需要手动开启事务,匿名函数返回的error如果不是nil,事务就会回滚
	//如果全局DefaultTxOptions配置不满足需求,可以在zorm.Transaction事务方法前设置事务的隔离级别,
	//例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions为nil,使用全局DefaultTxOptions
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {

		//slice存放的类型是zorm.IEntityStruct!!!,golang目前没有泛型,使用IEntityStruct接口,兼容Struct实体类
		demoSlice := make([]zorm.IEntityStruct, 0)

		//创建对象1
		demo1 := newDemoStruct()
		demo1.UserName = "demo1"
		//创建对象2
		demo2 := newDemoStruct()
		demo2.UserName = "demo2"

		demoSlice = append(demoSlice, &demo1, &demo2)

		//批量保存对象,如果主键是自增,无法保存自增的ID到对象里.
		_, err := zorm.InsertSlice(ctx, demoSlice)

		//如果返回的err不是nil,事务就会回滚
		return nil, err
	})
	//标记测试失败
	if err != nil {
		t.Errorf("错误:%v", err)
	}
}

//TestInsertEntityMap 04.测试保存EntityMap对象,用于不方便使用struct的场景,使用Map作为载体
func TestInsertEntityMap(t *testing.T) {

	//需要手动开启事务,匿名函数返回的error如果不是nil,事务就会回滚
	//如果全局DefaultTxOptions配置不满足需求,可以在zorm.Transaction事务方法前设置事务的隔离级别,
	//例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions为nil,使用全局DefaultTxOptions
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		//创建一个EntityMap,需要传入表名
		entityMap := zorm.NewEntityMap(demoStructTableName)
		//设置主键名称
		entityMap.PkColumnName = "id"
		//如果是自增序列,设置序列的值
		//entityMap.PkSequence = "mySequence"

		//Set 设置数据库的字段值
		//如果主键是自增或者序列,不要entityMap.Set主键的值
		entityMap.Set("id", zorm.FuncGenerateStringID())
		entityMap.Set("userName", "entityMap-userName")
		entityMap.Set("password", "entityMap-password")
		entityMap.Set("createTime", time.Now())
		entityMap.Set("active", 1)

		//执行
		_, err := zorm.InsertEntityMap(ctx, entityMap)

		//如果返回的err不是nil,事务就会回滚
		return nil, err
	})
	//标记测试失败
	if err != nil {
		t.Errorf("错误:%v", err)
	}
}

//TestQueryRow 05.测试查询一个struct对象
func TestQueryRow(t *testing.T) {

	//声明一个对象的指针,用于承载返回的数据
	demo := &demoStruct{}

	//构造查询用的finder
	finder := zorm.NewSelectFinder(demoStructTableName) // select * from t_demo
	//finder = zorm.NewSelectFinder(demoStructTableName, "id,userName") // select id,userName from t_demo
	//finder = zorm.NewFinder().Append("SELECT * FROM " + demoStructTableName) // select * from t_demo
	//finder默认启用了sql注入检查,禁止语句中拼接 ' 单引号,可以设置 finder.InjectionCheck = false 解开限制

	//finder.Append 第一个参数是语句,后面的参数是对应的值,值的顺序要正确.语句统一使用?,zorm会处理数据库的差异
	finder.Append("WHERE id=? and active in(?)", "20210630163227149563000042432429", []int{0, 1})

	//执行查询
	_, err := zorm.QueryRow(ctx, finder, demo)

	if err != nil { //标记测试失败
		t.Errorf("错误:%v", err)
	}
	//打印结果
	fmt.Println(demo)
}

//TestQueryRowMap 06.测试查询map接收结果,用于不太适合struct的场景,比较灵活
func TestQueryRowMap(t *testing.T) {

	//构造查询用的finder
	finder := zorm.NewSelectFinder(demoStructTableName) // select * from t_demo
	//finder.Append 第一个参数是语句,后面的参数是对应的值,值的顺序要正确.语句统一使用?,zorm会处理数据库的差异
	finder.Append("WHERE id=? and active in(?)", "20210630163227149563000042432429", []int{0, 1})
	//执行查询
	resultMap, err := zorm.QueryRowMap(ctx, finder)

	if err != nil { //标记测试失败
		t.Errorf("错误:%v", err)
	}
	//打印结果
	fmt.Println(resultMap)
}

//TestQuery 07.测试查询对象列表
func TestQuery(t *testing.T) {
	//创建用于接收结果的slice
	list := make([]*demoStruct, 0)

	//构造查询用的finder
	finder := zorm.NewSelectFinder(demoStructTableName) // select * from t_demo
	//创建分页对象,查询完成后,page对象可以直接给前端分页组件使用
	page := zorm.NewPage()
	page.PageNo = 2   //查询第1页,默认是1
	page.PageSize = 2 //每页20条,默认是20

	//执行查询
	err := zorm.Query(ctx, finder, &list, page)
	if err != nil { //标记测试失败
		t.Errorf("错误:%v", err)
	}
	//打印结果
	fmt.Println("总条数:", page.TotalCount, "  列表:", list)
}

//TestQueryMap 08.测试查询map列表,用于不方便使用struct的场景,一条记录是一个map对象
func TestQueryMap(t *testing.T) {
	//构造查询用的finder
	finder := zorm.NewSelectFinder(demoStructTableName) // select * from t_demo

	//创建分页对象,查询完成后,page对象可以直接给前端分页组件使用
	page := zorm.NewPage()
	page.PageNo = 1   //查询第1页,默认是1
	page.PageSize = 2 //每页20条,默认是20
	//执行查询
	listMap, err := zorm.QueryMap(ctx, finder, page)
	if err != nil { //标记测试失败
		t.Errorf("错误:%v", err)
	}
	//打印结果
	fmt.Println("总条数:", page.TotalCount, "  列表:", listMap)
}

//TestUpdateNotZeroValue 09.更新struct对象,只更新不为零值的字段.主键必须有值
func TestUpdateNotZeroValue(t *testing.T) {

	//需要手动开启事务,匿名函数返回的error如果不是nil,事务就会回滚
	//如果全局DefaultTxOptions配置不满足需求,可以在zorm.Transaction事务方法前设置事务的隔离级别,
	//例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions为nil,使用全局DefaultTxOptions
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		//声明一个对象的指针,用于更新数据
		demo := &demoStruct{}
		demo.Id = "20210630163227149563000042432429"
		demo.UserName = "UpdateNotZeroValue"

		//更新 "sql":"UPDATE t_demo SET userName=? WHERE id=?","args":["UpdateNotZeroValue","41b2aa4f-379a-4319-8af9-08472b6e514e"]
		_, err := zorm.UpdateNotZeroValue(ctx, demo)

		//如果返回的err不是nil,事务就会回滚
		return nil, err
	})
	if err != nil { //标记测试失败
		t.Errorf("错误:%v", err)
	}

}

//TestUpdate 10.更新struct对象,更新所有字段.主键必须有值
func TestUpdate(t *testing.T) {

	//需要手动开启事务,匿名函数返回的error如果不是nil,事务就会回滚
	//如果全局DefaultTxOptions配置不满足需求,可以在zorm.Transaction事务方法前设置事务的隔离级别,
	//例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions为nil,使用全局DefaultTxOptions
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {

		//声明一个对象的指针,用于更新数据
		demo := &demoStruct{}
		demo.Id = "20210630163227149563000042432429"
		demo.UserName = "TestUpdate"

		_, err := zorm.Update(ctx, demo)

		//如果返回的err不是nil,事务就会回滚
		return nil, err
	})
	if err != nil { //标记测试失败
		t.Errorf("错误:%v", err)
	}
}

//TestUpdateFinder 11.通过finder更新,zorm最灵活的方式,可以编写任何更新语句,甚至手动编写insert语句
func TestUpdateFinder(t *testing.T) {
	//需要手动开启事务,匿名函数返回的error如果不是nil,事务就会回滚
	//如果全局DefaultTxOptions配置不满足需求,可以在zorm.Transaction事务方法前设置事务的隔离级别,
	//例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions为nil,使用全局DefaultTxOptions
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		finder := zorm.NewUpdateFinder(demoStructTableName) // UPDATE t_demo SET
		//finder = zorm.NewDeleteFinder(demoStructTableName)  // DELETE FROM t_demo
		//finder = zorm.NewFinder().Append("UPDATE").Append(demoStructTableName).Append("SET") // UPDATE t_demo SET
		finder.Append("userName=?,active=?", "TestUpdateFinder", 1).Append("WHERE id=?", "20210630163227149563000042432429")

		//更新 "sql":"UPDATE t_demo SET  userName=?,active=? WHERE id=?","args":["TestUpdateFinder",1,"41b2aa4f-379a-4319-8af9-08472b6e514e"]
		_, err := zorm.UpdateFinder(ctx, finder)

		//如果返回的err不是nil,事务就会回滚
		return nil, err
	})
	if err != nil { //标记测试失败
		t.Errorf("错误:%v", err)
	}

}

//TestUpdateEntityMap 12.更新一个EntityMap,主键必须有值
func TestUpdateEntityMap(t *testing.T) {
	//需要手动开启事务,匿名函数返回的error如果不是nil,事务就会回滚
	//如果全局DefaultTxOptions配置不满足需求,可以在zorm.Transaction事务方法前设置事务的隔离级别,
	//例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions为nil,使用全局DefaultTxOptions
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		//创建一个EntityMap,需要传入表名
		entityMap := zorm.NewEntityMap(demoStructTableName)
		//设置主键名称
		entityMap.PkColumnName = "id"
		//Set 设置数据库的字段值,主键必须有值
		entityMap.Set("id", "20210630163227149563000042432429")
		entityMap.Set("userName", "TestUpdateEntityMap")
		//更新 "sql":"UPDATE t_demo SET userName=? WHERE id=?","args":["TestUpdateEntityMap","41b2aa4f-379a-4319-8af9-08472b6e514e"]
		_, err := zorm.UpdateEntityMap(ctx, entityMap)

		//如果返回的err不是nil,事务就会回滚
		return nil, err
	})
	if err != nil { //标记测试失败
		t.Errorf("错误:%v", err)
	}

}

//TestDelete 13.删除一个struct对象,主键必须有值
func TestDelete(t *testing.T) {
	//需要手动开启事务,匿名函数返回的error如果不是nil,事务就会回滚
	//如果全局DefaultTxOptions配置不满足需求,可以在zorm.Transaction事务方法前设置事务的隔离级别,
	//例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions为nil,使用全局DefaultTxOptions
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		demo := &demoStruct{}
		demo.Id = "20210630163227149563000042432429"

		//删除 "sql":"DELETE FROM t_demo WHERE id=?","args":["ae9987ac-0467-4fe2-a260-516c89292684"]
		_, err := zorm.Delete(ctx, demo)

		//如果返回的err不是nil,事务就会回滚
		return nil, err
	})
	if err != nil { //标记测试失败
		t.Errorf("错误:%v", err)
	}

}

//TestProc 14.测试调用存储过程
func TestProc(t *testing.T) {
	demo := &demoStruct{}
	finder := zorm.NewFinder().Append("call testproc(?) ", "u_10001")
	zorm.QueryRow(ctx, finder, demo)
	fmt.Println(demo)
}

//TestFunc 15.测试调用自定义函数
func TestFunc(t *testing.T) {
	userName := ""
	finder := zorm.NewFinder().Append("select testfunc(?) ", "u_10001")
	zorm.QueryRow(ctx, finder, &userName)
	fmt.Println(userName)
}

//TestOther 16.其他的一些说明.非常感谢您能看到这一行
func TestOther(t *testing.T) {

	//场景1.多个数据库.通过对应数据库的dbDao,调用BindContextDBConnection函数,把这个数据库的连接绑定到返回的ctx上,然后把ctx传递到zorm的函数即可.
	newCtx, err := dbDao.BindContextDBConnection(ctx)
	if err != nil { //标记测试失败
		t.Errorf("错误:%v", err)
	}

	finder := zorm.NewSelectFinder(demoStructTableName)
	//把新产生的newCtx传递到zorm的函数
	list, _ := zorm.QueryMap(newCtx, finder, nil)
	fmt.Println(list)

	//场景2.单个数据库的读写分离.设置读写分离的策略函数.
	zorm.FuncReadWriteStrategy = myReadWriteStrategy

	//场景3.如果是多个数据库,每个数据库还读写分离,按照 场景1 处理

}

//单个数据库的读写分离的策略 rwType=0 read,rwType=1 write
func myReadWriteStrategy(rwType int) *zorm.DBDao {
	//根据自己的业务场景,返回需要的读写dao,每次需要数据库的连接的时候,会调用这个函数
	return dbDao
}

//---------------------------------//

//实现CustomDriverValueConver接口,扩展自定义类型,例如 达梦数据库text类型,映射出来的是dm.DmClob类型,无法使用string类型直接接收
type CustomDMText struct{}
//GetDriverValue 根据数据库列类型,实体类属性类型,Finder对象,返回driver.Value的实例
//如果无法获取到structFieldType,例如Map查询,会传入nil
//如果返回值为nil,接口扩展逻辑无效,使用原生的方式接收数据库字段值
func (dmtext CustomDMText) GetDriverValue(columnType *sql.ColumnType, structFieldType *reflect.Type, finder *zorm.Finder) (driver.Value, error) {
	return &dm.DmClob{}, nil
}
//ConverDriverValue 数据库列类型,实体类属性类型,GetDriverValue返回的driver.Value的临时接收值,Finder对象
//如果无法获取到structFieldType,例如Map查询,会传入nil
//返回符合接收类型值的指针,指针,指针!!!!
func (dmtext CustomDMText) ConverDriverValue(columnType *sql.ColumnType, structFieldType *reflect.Type, tempDriverValue driver.Value, finder *zorm.Finder) (interface{}, error) {
	//类型转换
	dmClob, isok := tempDriverValue.(*dm.DmClob)
	if !isok {
		return tempDriverValue, errors.New("转换至*dm.DmClob类型失败")
	}

	//获取长度
	dmlen, errLength := dmClob.GetLength()
	if errLength != nil {
		return dmClob, errLength
	}

	//int64转成int类型
	strInt64 := strconv.FormatInt(dmlen, 10)
	dmlenInt, errAtoi := strconv.Atoi(strInt64)
	if errAtoi != nil {
		return dmClob, errAtoi
	}

	//读取字符串
	str, errReadString := dmClob.ReadString(1, dmlenInt)
	return &str, errReadString
}
//CustomDriverValueMap 用于配置driver.Value和对应的处理关系,key是 drier.Value 的字符串,例如 *dm.DmClob
//一般是放到init方法里进行添加
zorm.CustomDriverValueMap["*dm.DmClob"] = CustomDMText{}

分布式事务

基于seata-golang实现分布式事务.

proxy模式

//DataSourceConfig 配置  DefaultTxOptions
//DefaultTxOptions: &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false},

// 引入V1版本的依赖包,V2的参考官方例子
import (
	"github.com/opentrx/mysql"
	"github.com/transaction-wg/seata-golang/pkg/client"
	"github.com/transaction-wg/seata-golang/pkg/client/config"
	"github.com/transaction-wg/seata-golang/pkg/client/rm"
	"github.com/transaction-wg/seata-golang/pkg/client/tm"
	seataContext "github.com/transaction-wg/seata-golang/pkg/client/context"
)

//配置文件路径
var configPath = "./conf/client.yml"

func main() {

	//初始化配置
	conf := config.InitConf(configPath)
	//初始化RPC客户端
	client.NewRpcClient()
	//注册mysql驱动
	mysql.InitDataResourceManager()
	mysql.RegisterResource(config.GetATConfig().DSN)
	//sqlDB, err := sql.Open("mysql", config.GetATConfig().DSN)


	//后续正常初始化zorm,一定要放到seata mysql 初始化后面!!!

	//................//
	//tm注册事务服务,参照官方例子.(全局托管主要是去掉proxy,对业务零侵入)
	tm.Implement(svc.ProxySvc)
	//................//


	//获取seata的rootContext
	//rootContext := seataContext.NewRootContext(ctx)
	//rootContext := ctx.(*seataContext.RootContext)

	//创建seata事务
	//seataTx := tm.GetCurrentOrCreate(rootContext)

	//开始事务
	//seataTx.BeginWithTimeoutAndName(int32(6000), "事务名称", rootContext)

	//事务开启之后获取XID.可以通过gin的header传递,或者其他方式传递
	//xid:=rootContext.GetXID()

	// 如果使用的gin框架,获取到ctx
	// ctx := c.Request.Context()

	// 接受传递过来的XID,绑定到本地ctx
	//ctx =context.WithValue(ctx,mysql.XID,xid)


}

全局托管模式


//不使用proxy代理模式,全局托管,不修改业务代码,零侵入实现分布式事务
//tm.Implement(svc.ProxySvc)


// 分布式事务示例代码
_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {

    // 获取当前分布式事务的XID.不用考虑怎么来的,如果是分布式事务环境,会自动设置值
    // xid := ctx.Value("XID").(string)

	// 把xid传递到第三方应用
	// req.Header.Set("XID", xid)

	// 如果返回的err不是nil,本地事务和分布式事务就会回滚
	return nil, err
})

///----------第三方应用-------///

// 第三方应用开启事务前,ctx需要绑定XID,例如使用了gin框架

// 接受传递过来的XID,绑定到本地ctx
// xid:=c.Request.Header.Get("XID")
// 获取到ctx
// ctx := c.Request.Context()
// ctx = context.WithValue(ctx,"XID",xid)

// ctx绑定XID之后,调用业务事务
_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {

    // 业务代码......

	// 如果返回的err不是nil,本地事务和分布式事务就会回滚
	return nil, err
})



// 建议以下代码放到单独的文件里
//................//

// ZormSeataGlobalTransaction 包装seata的*tm.DefaultGlobalTransaction,实现zorm.ISeataGlobalTransaction接口
type ZormSeataGlobalTransaction struct {
	*tm.DefaultGlobalTransaction
}

// MyFuncSeataGlobalTransaction zorm适配seata分布式事务的函数
// 重要!!!!需要配置zorm.DataSourceConfig.FuncSeataGlobalTransaction=MyFuncSeataGlobalTransaction 重要!!!
func MyFuncSeataGlobalTransaction(ctx context.Context) (zorm.ISeataGlobalTransaction, context.Context, error) {
	//获取seata的rootContext
	rootContext := seataContext.NewRootContext(ctx)
	//创建seata事务
	seataTx := tm.GetCurrentOrCreate(rootContext)
	//使用zorm.ISeataGlobalTransaction接口对象包装seata事务,隔离seata-golang依赖
	seataGlobalTransaction := ZormSeataGlobalTransaction{seataTx}

	return seataGlobalTransaction, rootContext, nil
}

//实现zorm.ISeataGlobalTransaction接口
func (gtx ZormSeataGlobalTransaction) SeataBegin(ctx context.Context) error {
	rootContext := ctx.(*seataContext.RootContext)
	return gtx.BeginWithTimeout(int32(6000), rootContext)
}

func (gtx ZormSeataGlobalTransaction) SeataCommit(ctx context.Context) error {
	rootContext := ctx.(*seataContext.RootContext)
	return gtx.Commit(rootContext)
}

func (gtx ZormSeataGlobalTransaction) SeataRollback(ctx context.Context) error {
	rootContext := ctx.(*seataContext.RootContext)
	//如果是Participant角色,修改为Launcher角色,允许分支事务提交全局事务.
	if gtx.Role != tm.Launcher {
		gtx.Role = tm.Launcher
	}
	return gtx.Rollback(rootContext)
}

func (gtx ZormSeataGlobalTransaction) GetSeataXID(ctx context.Context) string {
	rootContext := ctx.(*seataContext.RootContext)
	return rootContext.GetXID()
}

//................//

性能压测

测试代码:https://github.com/springrain/goormbenchmark
zorm 1.2.x 版本实现了基础功能,读性能比gorm和xorm快一倍.随着功能持续增加,造成性能下降,目前读性能只快了50%.
zorm会持续优化改进性能.

Documentation

Overview

Package zorm 使用原生的sql语句,没有对sql语法做限制.语句使用Finder作为载体 占位符统一使用?,zorm会根据数据库类型,语句执行前会自动替换占位符,postgresql 把?替换成$1,$2...;mssql替换成@P1,@p2...;orace替换成:1,:2... zorm使用 ctx context.Context 参数实现事务传播,ctx从web层传递进来即可,例如gin的c.Request.Context() zorm的事务操作需要显示使用zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {})开启 "package zorm" Use native SQL statements, no restrictions on SQL syntax. Statements use Finder as a carrier Use placeholders uniformly "?" "zorm" automatically replaces placeholders before statements are executed,depending on the database type. Replaced with $1, $2... ; Replace MSSQL with @p1,@p2... ; Orace is replaced by :1,:2..., "zorm" uses the "ctx context.Context" parameter to achieve transaction propagation,and ctx can be passed in from the web layer, such as "gin's c.Request.Context()", "zorm" Transaction operations need to be displayed using "zorm.transaction" (ctx, func(ctx context.context) (interface{}, error) {})

Index

Constants

This section is empty.

Variables

View Source
var CustomDriverValueMap = make(map[string]CustomDriverValueConver)

CustomDriverValueMap 用于配置driver.Value和对应的处理关系,key是 drier.Value 的字符串,例如 *dm.DmClob 一般是放到init方法里进行添加

View Source
var FuncGenerateStringID func() string = generateStringID

FuncGenerateStringID 默认生成字符串ID的函数.方便自定义扩展 FuncGenerateStringID Function to generate string ID by default. Convenient for custom extension

View Source
var FuncLogError func(err error) = defaultLogError

FuncLogError 记录error日志 FuncLogError Record error log

View Source
var FuncLogPanic func(err error) = defaultLogPanic

FuncLogPanic 记录panic日志,默认使用"defaultLogError"实现 FuncLogPanic Record panic log, using "defaultLogError" by default

View Source
var FuncPrintSQL func(sqlstr string, args []interface{}) = defaultPrintSQL

FuncPrintSQL 打印sql语句和参数 FuncPrintSQL Print sql statement and parameters

View Source
var FuncReadWriteStrategy func(rwType int) *DBDao = getDefaultDao

FuncReadWriteStrategy 单个数据库的读写分离的策略,用于外部复写实现自定义的逻辑,rwType=0 read,rwType=1 write 不能归属到DBDao里,BindContextDBConnection已经是指定数据库的连接了,和这个函数会冲突.就作为单数据库读写分离的处理方式 即便是放到DBDao里,因为是多库,BindContextDBConnection函数调用少不了,业务包装一个方法,指定一下读写获取一个DBDao效果是一样的,唯一就是需要根据业务指定一下读写,其实更灵活了 FuncReadWriteStrategy Single database read and write separation strategy,used for external replication to implement custom logic, rwType=0 read, rwType=1 write. "BindContextDBConnection" is already a connection to the specified database and will conflict with this function. As a single database read and write separation of processing

View Source
var LogCallDepth = 4

LogCallDepth 记录日志调用层级,用于定位到业务层代码 Log Call Depth Record the log call level, used to locate the business layer code

Functions

func Delete

func Delete(ctx context.Context, entity IEntityStruct) (int, error)

Delete 根据主键删除一个对象.必须是IEntityStruct类型 ctx不能为nil,参照使用zorm.Transaction方法传入ctx.也不要自己构建DBConnection affected影响的行数,如果异常或者驱动不支持,返回-1

func Insert

func Insert(ctx context.Context, entity IEntityStruct) (int, error)

Insert 保存Struct对象,必须是IEntityStruct类型 ctx不能为nil,参照使用zorm.Transaction方法传入ctx.也不要自己构建DBConnection affected影响的行数,如果异常或者驱动不支持,返回-1 Insert saves the Struct object, which must be of type IEntityStruct ctx cannot be nil, refer to zorm.Transaction method to pass in ctx. Don't build dbConnection yourself The number of rows affected by affected, if it is abnormal or the driver does not support it, return -1

func InsertEntityMap

func InsertEntityMap(ctx context.Context, entity IEntityMap) (int, error)

InsertEntityMap 保存*IEntityMap对象.使用Map保存数据,用于不方便使用struct的场景,如果主键是自增或者序列,不要entityMap.Set主键的值 ctx不能为nil,参照使用zorm.Transaction方法传入ctx.也不要自己构建DBConnection affected影响的行数,如果异常或者驱动不支持,返回-1

func InsertSlice

func InsertSlice(ctx context.Context, entityStructSlice []IEntityStruct) (int, error)

InsertSlice 批量保存Struct Slice 数组对象,必须是[]IEntityStruct类型,golang目前没有泛型,使用IEntityStruct接口,兼容Struct实体类 如果是自增主键,无法对Struct对象里的主键属性赋值 ctx不能为nil,参照使用zorm.Transaction方法传入ctx.也不要自己构建DBConnection affected影响的行数,如果异常或者驱动不支持,返回-1

func Query

func Query(ctx context.Context, finder *Finder, rowsSlicePtr interface{}, page *Page) error

Query 不要偷懒调用QueryMap,需要处理sql驱动支持的sql.Nullxxx的数据类型,也挺麻烦的 根据Finder和封装为指定的entity类型,entity必须是*[]struct类型,已经初始化好的数组,此方法只Append元素,这样调用方就不需要强制类型转换了 context必须传入,不能为空.如果想不分页,查询所有数据,page传入nil Query:Don't be lazy to call QueryMap, you need to deal with the sql,Nullxxx data type supported by the sql driver, which is also very troublesome According to the Finder and encapsulation for the specified entity type, the entity must be of the *[]struct type, which has been initialized,This method only Append elements, so the caller does not need to force type conversion context must be passed in and cannot be empty

func QueryMap

func QueryMap(ctx context.Context, finder *Finder, page *Page) ([]map[string]interface{}, error)

QueryMap 根据Finder查询,封装Map数组 根据数据库字段的类型,完成从[]byte到golang类型的映射,理论上其他查询方法都可以调用此方法,但是需要处理sql.Nullxxx等驱动支持的类型 context必须传入,不能为空 QueryMap According to Finder query, encapsulate Map array According to the type of database field, the mapping from []byte to golang type is completed. In theory,other query methods can call this method, but need to deal with types supported by drivers such as sql.Nullxxx context must be passed in and cannot be empty

func QueryMapAndColumnTypes

func QueryMapAndColumnTypes(ctx context.Context, finder *Finder, page *Page) ([]*sql.ColumnType, []map[string]interface{}, error)

QueryMap 根据Finder查询,封装Map数组 根据数据库字段的类型,完成从[]byte到golang类型的映射,理论上其他查询方法都可以调用此方法,但是需要处理sql.Nullxxx等驱动支持的类型 context必须传入,不能为空 QueryMap According to Finder query, encapsulate Map array According to the type of database field, the mapping from []byte to golang type is completed. In theory,other query methods can call this method, but need to deal with types supported by drivers such as sql.Nullxxx context must be passed in and cannot be empty

func QueryRow

func QueryRow(ctx context.Context, finder *Finder, entity interface{}) (bool, error)

QueryRow 不要偷懒调用Query返回第一条,问题1.需要构建一个slice,问题2.调用方传递的对象其他值会被抛弃或者覆盖. 根据Finder和封装为指定的entity类型,entity必须是*struct类型或者基础类型的指针.把查询的数据赋值给entity,所以要求指针类型 context必须传入,不能为空 如果数据库是null,基本类型不支持,会返回异常,不做默认值处理,Query因为是列表,会设置为默认值 QueryRow Don't be lazy to call Query to return the first one Question 1. A selice needs to be constructed, and question 2. Other values ​​of the object passed by the caller will be discarded or overwritten context must be passed in and cannot be empty

func QueryRowMap

func QueryRowMap(ctx context.Context, finder *Finder) (map[string]interface{}, error)

QueryRowMap 根据Finder查询,封装Map context必须传入,不能为空 QueryRowMap encapsulates Map according to Finder query context must be passed in and cannot be empty

func Transaction

func Transaction(ctx context.Context, doTransaction func(ctx context.Context) (interface{}, error)) (interface{}, error)

Transaction 的示例代码

  //匿名函数return的error如果不为nil,事务就会回滚
  zorm.Transaction(ctx context.Context,func(ctx context.Context) (interface{}, error) {

	  //业务代码

	  //return的error如果不为nil,事务就会回滚
      return nil, nil
  })

事务方法,隔离dbConnection相关的API.必须通过这个方法进行事务处理,统一事务方式 如果入参ctx中没有dbConnection,使用defaultDao开启事务并最后提交 如果入参ctx有dbConnection且没有事务,调用dbConnection.begin()开启事务并最后提交 如果入参ctx有dbConnection且有事务,只使用不提交,有开启方提交事务 但是如果遇到错误或者异常,虽然不是事务的开启方,也会回滚事务,让事务尽早回滚 在多库的场景,手动获取dbConnection,然后绑定到一个新的context,传入进来 不要去掉匿名函数的context参数,因为如果Transaction的context中没有dbConnection,会新建一个context并放入dbConnection,此时的context指针已经变化,不能直接使用Transaction的context参数 bug(springrain)如果有大神修改了匿名函数内的参数名,例如改为ctx2,这样业务代码实际使用的是Transaction的context参数,如果为没有dbConnection,会抛异常,如果有dbConnection,实际就是一个对象.影响有限.也可以把匿名函数抽到外部 如果全局DefaultTxOptions配置不满足需求,可以在zorm.Transaction事务方法前设置事务的隔离级别,例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault}),如果txOptions为nil,使用全局DefaultTxOptions return的error如果不为nil,事务就会回滚 如果使用了分布式事务,需要设置分布式事务函数zorm.DataSourceConfig.FuncSeataGlobalTransaction,实现ISeataGlobalTransaction接口 如果是分布式事务开启方,需要在本地事务前开启分布事务,开启之后获取XID,设值到ctx的XID和TX_XID.XID是seata MySQL驱动需要,TX_XID是seataContext.NewRootContext需要 分布式事务需要传递XID,接收方context.WithValue(ctx, "XID", XID)绑定到ctx 如果分支事务出现异常或者回滚,会立即回滚分布式事务 Transaction method, isolate db Connection related API. This method must be used for transaction processing and unified transaction mode If there is no db Connection in the input ctx, use default Dao to start the transaction and submit it finally If the input ctx has db Connection and no transaction, call db Connection.begin() to start the transaction and finally commit If the input ctx has a db Connection and a transaction, only use non-commit, and the open party submits the transaction If you encounter an error or exception, although it is not the initiator of the transaction, the transaction will be rolled back, so that the transaction can be rolled back as soon as possible In a multi-database scenario, manually obtain db Connection, then bind it to a new context and pass in Do not drop the anonymous function's context parameter, because if the Transaction context does not have a DBConnection, then a new context will be created and placed in the DBConnection The context pointer has changed and the Transaction context parameters cannot be used directly "bug (springrain)" If a great god changes the parameter name in the anonymous function, for example, change it to ctx 2, so that the business code actually uses the context parameter of Transaction. If there is no db Connection, an exception will be thrown. If there is a db Connection, the actual It is an object The impact is limited. Anonymous functions can also be extracted outside If the return error is not nil, the transaction will be rolled back

func Update

func Update(ctx context.Context, entity IEntityStruct) (int, error)

Update 更新struct所有属性,必须是IEntityStruct类型 ctx不能为nil,参照使用zorm.Transaction方法传入ctx.也不要自己构建DBConnection

func UpdateEntityMap

func UpdateEntityMap(ctx context.Context, entity IEntityMap) (int, error)

UpdateEntityMap 更新IEntityMap对象.用于不方便使用struct的场景,主键必须有值 ctx不能为nil,参照使用zorm.Transaction方法传入ctx.也不要自己构建DBConnection affected影响的行数,如果异常或者驱动不支持,返回-1 UpdateEntityMap Update IEntityMap object. Used in scenarios where struct is not convenient, the primary key must have a value ctx cannot be nil, refer to zorm.Transaction method to pass in ctx. Don't build DB Connection yourself The number of rows affected by "affected", if it is abnormal or the driver does not support it, return -1

func UpdateFinder

func UpdateFinder(ctx context.Context, finder *Finder) (int, error)

UpdateFinder 更新Finder语句 ctx不能为nil,参照使用zorm.Transaction方法传入ctx.也不要自己构建DBConnection affected影响的行数,如果异常或者驱动不支持,返回-1 UpdateFinder Update Finder statement ctx cannot be nil, refer to zorm.Transaction method to pass in ctx. Don't build DB Connection yourself The number of rows affected by affected, if it is abnormal or the driver does not support it, return-1

func UpdateNotZeroValue

func UpdateNotZeroValue(ctx context.Context, entity IEntityStruct) (int, error)

UpdateNotZeroValue 更新struct不为默认零值的属性,必须是IEntityStruct类型,主键必须有值 ctx不能为nil,参照使用zorm.Transaction方法传入ctx.也不要自己构建DBConnection

func WrapPageSQL

func WrapPageSQL(dbType string, sqlstr string, page *Page) (string, error)

Types

type CustomDriverValueConver

type CustomDriverValueConver interface {
	//GetDriverValue 根据数据库列类型,实体类属性类型,Finder对象,返回driver.Value的实例
	//如果无法获取到structFieldType,例如Map查询,会传入nil
	//如果返回值为nil,接口扩展逻辑无效,使用原生的方式接收数据库字段值
	GetDriverValue(columnType *sql.ColumnType, structFieldType *reflect.Type, finder *Finder) (driver.Value, error)

	//ConverDriverValue 数据库列类型,实体类属性类型,GetDriverValue返回的driver.Value的临时接收值,Finder对象
	//如果无法获取到structFieldType,例如Map查询,会传入nil
	//返回符合接收类型值的指针,指针,指针!!!!
	ConverDriverValue(columnType *sql.ColumnType, structFieldType *reflect.Type, tempDriverValue driver.Value, finder *Finder) (interface{}, error)
}

CustomDriverValueConver 自定义类型转化接口,用于解决 类似达梦 text --> dm.DmClob --> string类型接收的问题

type DBDao

type DBDao struct {
	// contains filtered or unexported fields
}

DBDao 数据库操作基类,隔离原生操作数据库API入口,所有数据库操作必须通过DBDao进行 DBDao Database operation base class, isolate the native operation database API entry,all database operations must be performed through DB Dao

func NewDBDao

func NewDBDao(config *DataSourceConfig) (*DBDao, error)

NewDBDao 创建dbDao,一个数据库要只执行一次,业务自行控制 第一个执行的数据库为 defaultDao,后续zorm.xxx方法,默认使用的就是defaultDao NewDBDao Creates dbDao, a database must be executed only once, and the business is controlled by itself The first database to be executed is defaultDao, and the subsequent zorm.xxx method is defaultDao by default

func (*DBDao) BindContextDBConnection

func (dbDao *DBDao) BindContextDBConnection(parent context.Context) (context.Context, error)

BindContextDBConnection 多库的时候,通过dbDao创建DBConnection绑定到子context,返回的context就有了DBConnection. parent 不能为空 BindContextDBConnection In the case of multiple databases, create a DB Connection through db Dao and bind it to a sub-context,and the returned context will have a DB Connection. parent is not nil

func (*DBDao) BindContextTxOptions

func (dbDao *DBDao) BindContextTxOptions(parent context.Context, txOptions *sql.TxOptions) (context.Context, error)

BindContextTxOptions 绑定事务的隔离级别,参考sql.IsolationLevel,如果txOptions为nil,使用默认的事务隔离级别.parent不能为空 需要在事务开启前调用,也就是zorm.Transaction方法前,不然事务开启之后再调用就无效了

func (*DBDao) CloseDB

func (dbDao *DBDao) CloseDB() error

CloseDB 关闭所有数据库连接 请谨慎调用这个方法,会关闭所有数据库连接,用于处理特殊场景,正常使用无需手动关闭数据库连接

type DataSourceConfig

type DataSourceConfig struct {
	//DSN dataSourceName 连接字符串
	//DSN DataSourceName Database connection string
	DSN string
	//数据库驱动名称:mysql,postgres,oci8,sqlserver,sqlite3,clickhouse,dm,kingbase 和DBType对应,处理数据库有多个驱动
	//Database diver name:mysql,dm,postgres,opi8,sqlserver,sqlite3,clickhouse,kingbase corresponds to DBType,A database may have multiple drivers
	DriverName string
	//数据库类型(方言判断依据):mysql,postgresql,oracle,mssql,sqlite,clickhouse,dm,kingbase 和 DriverName 对应,处理数据库有多个驱动
	//Database Type:mysql,postgresql,oracle,mssql,sqlite,clickhouse,dm,kingbase corresponds to DriverName,A database may have multiple drivers
	DBType string
	//PrintSQL 是否打印SQL语句.使用zorm.PrintSQL记录SQL
	//PrintSQL Whether to print SQL, use zorm.PrintSQL record sql
	PrintSQL bool
	//MaxOpenConns 数据库最大连接数,默认50
	//MaxOpenConns Maximum number of database connections, Default 50
	MaxOpenConns int
	//MaxIdleConns 数据库最大空闲连接数,默认50
	//MaxIdleConns The maximum number of free connections to the database default 50
	MaxIdleConns int
	//ConnMaxLifetimeSecond 连接存活秒时间. 默认600(10分钟)后连接被销毁重建.避免数据库主动断开连接,造成死连接.MySQL默认wait_timeout 28800秒(8小时)
	//ConnMaxLifetimeSecond: (Connection survival time in seconds)Destroy and rebuild the connection after the default 600 seconds (10 minutes)
	//Prevent the database from actively disconnecting and causing dead connections. MySQL Default wait_timeout 28800 seconds
	ConnMaxLifetimeSecond int

	//事务隔离级别的默认配置,默认为nil
	DefaultTxOptions *sql.TxOptions

	//FuncSeataGlobalTransaction seata-golang分布式的适配函数,返回ISeataGlobalTransaction接口的实现
	FuncSeataGlobalTransaction func(ctx context.Context) (ISeataGlobalTransaction, context.Context, error)
}

DataSourceConfig 数据库连接池的配置 DateSourceConfig Database connection pool configuration

type EntityMap

type EntityMap struct {

	//主键列名
	PkColumnName string
	//主键序列,需要兼容多种数据库的序列,使用map,key是DBType,value是序列的值,例如oracle的TESTSEQ.NEXTVAL,如果有值,优先级最高
	PkSequence map[string]string
	// contains filtered or unexported fields
}

EntityMap IEntityMap的基础实现,可以直接使用或者匿名注入

func NewEntityMap

func NewEntityMap(tbName string) *EntityMap

NewEntityMap 初始化Map,必须传入表名称

func (*EntityMap) GetDBFieldMap

func (entity *EntityMap) GetDBFieldMap() map[string]interface{}

GetDBFieldMap 针对Map类型,记录数据库字段 GetDBFieldMap For Map type, record database fields

func (*EntityMap) GetPKColumnName

func (entity *EntityMap) GetPKColumnName() string

GetPKColumnName 获取数据库表的主键字段名称.因为要兼容Map,只能是数据库的字段名称

func (*EntityMap) GetPkSequence

func (entity *EntityMap) GetPkSequence() map[string]string

GetPkSequence 主键序列,因为需要兼容多种数据库的序列,所以使用map key是DBType,value是序列的值,例如oracle的TESTSEQ.NEXTVAL,如果有值,优先级最高 如果key对应的value是 "",则代表是触发器触发的序列,兼容自增关键字,例如 ["oracle"]"" GetPkSequence Primary key sequence, because it needs to be compatible with multiple database sequences, map is used The key is the DB Type, and the value is the value of the sequence, such as Oracle's TESTSEQ.NEXTVAL. If there is a value, the priority is the highest If the value corresponding to the key is "", it means the sequence triggered by the trigger Compatible with auto-increment keywords, such as ["oracle"]""

func (*EntityMap) GetTableName

func (entity *EntityMap) GetTableName() string

GetTableName 获取表名称

func (*EntityMap) Set

func (entity *EntityMap) Set(key string, value interface{}) map[string]interface{}

Set 设置数据库字段 Set Set database fields

type EntityStruct

type EntityStruct struct {
}

EntityStruct "IBaseEntity" 的基础实现,所有的实体类都匿名注入.这样就类似实现继承了,如果接口增加方法,调整这个默认实现即可 EntityStruct The basic implementation of "IBaseEntity", all entity classes are injected anonymously This is similar to implementation inheritance. If the interface adds methods, adjust the default implementation

func (*EntityStruct) GetPKColumnName

func (entity *EntityStruct) GetPKColumnName() string

GetPKColumnName 获取数据库表的主键字段名称.因为要兼容Map,只能是数据库的字段名称 GetPKColumnName Get the primary key field name of the database table Because it is compatible with Map, it can only be the field name of the database

func (*EntityStruct) GetPkSequence

func (entity *EntityStruct) GetPkSequence() map[string]string

GetPkSequence 主键序列,需要兼容多种数据库的序列,使用map,key是DBType,value是序列的值,例如oracle的TESTSEQ.NEXTVAL,如果有值,优先级最高 如果key对应的value是 "",则代表是触发器触发的序列,兼容自增关键字,例如 ["oracle"]""

type Finder

type Finder struct {

	//注入检查,默认true 不允许SQL注入的 ' 单引号
	//Injection check, default true does not allow SQL injection  single quote
	InjectionCheck bool
	//CountFinder 自定义的查询总条数'Finder',使用指针默认为nil.主要是为了在'group by'等复杂情况下,为了性能,手动编写总条数语句
	//CountFinder The total number of custom queries is'Finder', and the pointer is nil by default. It is mainly used to manually write the total number of statements for performance in complex situations such as'group by'
	CountFinder *Finder
	//是否自动查询总条数,默认true.同时需要Page不为nil,才查询总条数
	//Whether to automatically query the total number of entries, the default is true. At the same time, the Page is not nil to query the total number of entries
	SelectTotalCount bool
	// contains filtered or unexported fields
}

Finder 查询数据库的载体,所有的sql语句都要通过Finder执行. Finder To query the database carrier, all SQL statements must be executed through Finder

func NewDeleteFinder

func NewDeleteFinder(tableName string) *Finder

NewDeleteFinder 根据表名初始化删除的'Finder', DELETE FROM tableName NewDeleteFinder Finder for initial deletion based on table name. DELETE FROM tableName

func NewFinder

func NewFinder() *Finder

NewFinder 初始化一个Finder,生成一个空的Finder NewFinder Initialize a Finder and generate an empty Finder

func NewSelectFinder

func NewSelectFinder(tableName string, strs ...string) *Finder

NewSelectFinder 根据表名初始化查询的Finder | Finder that initializes the query based on the table name NewSelectFinder("tableName") SELECT * FROM tableName NewSelectFinder("tableName", "id,name") SELECT id,name FROM tableName

func NewUpdateFinder

func NewUpdateFinder(tableName string) *Finder

NewUpdateFinder 根据表名初始化更新的Finder, UPDATE tableName SET NewUpdateFinder Initialize the updated Finder according to the table name, UPDATE tableName SET

func (*Finder) Append

func (finder *Finder) Append(s string, values ...interface{}) *Finder

Append 添加SQL和参数的值,第一个参数是语句,后面的参数[可选]是参数的值,顺序要正确 例如: finder.Append(" and id=? and name=? ",23123,"abc") 只拼接SQL,例如: finder.Append(" and name=123 ") Append:Add SQL and parameter values, the first parameter is the statement, and the following parameter (optional) is the value of the parameter, in the correct order E.g: finder.Append(" and id=? and name=? ",23123,"abc") Only splice SQL, E.g: finder.Append(" and name=123 ")

func (*Finder) AppendFinder

func (finder *Finder) AppendFinder(f *Finder) (*Finder, error)

AppendFinder 添加另一个Finder finder.AppendFinder(f) AppendFinder Add another Finder . finder.AppendFinder(f)

func (*Finder) GetSQL

func (finder *Finder) GetSQL() (string, error)

GetSQL 返回Finder封装的SQL语句 GetSQL Return the SQL statement encapsulated by the Finder

type IEntityMap

type IEntityMap interface {
	//获取表名称
	//Get the table name
	GetTableName() string

	//获取数据库表的主键字段名称.因为要兼容Map,只能是数据库的字段名称.
	//Get the primary key field name of the database table. Because it is compatible with Map, it can only be the field name of the database.
	GetPKColumnName() string

	//GetPkSequence 主键序列,因为需要兼容多种数据库的序列,所以使用map
	//key是DBType,value是序列的值,例如oracle的TESTSEQ.NEXTVAL,如果有值,优先级最高
	//如果key对应的value是 "",则代表是触发器触发的序列,兼容自增关键字,例如 ["oracle"]""
	//GetPkSequence Primary key sequence, because it needs to be compatible with multiple database sequences, map is used
	//The key is the DB Type, and the value is the value of the sequence,
	//such as Oracle's TESTSEQ.NEXTVAL. If there is a value, the priority is the highest
	//If the value corresponding to the key is "", it means the sequence triggered by the trigger
	//Compatible with auto-increment keywords, such as ["oracle"]""
	GetPkSequence() map[string]string

	//针对Map类型,记录数据库字段
	//For Map type, record database fields.
	GetDBFieldMap() map[string]interface{}
	//设置数据库字段的值
	//Set the value of a database field.
	Set(key string, value interface{}) map[string]interface{}
}

IEntityMap 使用Map保存数据,用于不方便使用struct的场景,如果主键是自增或者序列,不要"entityMap.Set"主键的值 IEntityMap Use Map to save data for scenarios where it is not convenient to use struct If the primary key is auto-increment or sequence, do not "entity Map.Set" the value of the primary key

type IEntityStruct

type IEntityStruct interface {
	//获取表名称
	//Get the table name.
	GetTableName() string

	//获取数据库表的主键字段名称.因为要兼容Map,只能是数据库的字段名称
	//Get the primary key field name of the database table. Because it is compatible with Map, it can only be the field name of the database
	GetPKColumnName() string

	//GetPkSequence 主键序列,因为需要兼容多种数据库的序列,所以使用map
	//key是DBType,value是序列的值,例如oracle的TESTSEQ.NEXTVAL,如果有值,优先级最高
	//如果key对应的value是 "",则代表是触发器触发的序列,兼容自增关键字,例如 ["oracle"]""
	//GetPkSequence Primary key sequence, because it needs to be compatible with multiple database sequences, map is used
	//The key is the DB Type, and the value is the value of the sequence,
	//such as Oracle's TESTSEQ.NEXTVAL. If there is a value, the priority is the highest
	//If the value corresponding to the key is "", it means the sequence triggered by the trigger
	//Compatible with auto-increment keywords, such as ["oracle"]""
	GetPkSequence() map[string]string
}

IEntityStruct "struct"实体类的接口,所有的struct实体类都要实现这个接口 IEntityStruct The interface of the "struct" entity class, all struct entity classes must implement this interface

type ISeataGlobalTransaction

type ISeataGlobalTransaction interface {
	//开启seata全局事务
	SeataBegin(ctx context.Context) error

	//提交seata全局事务
	SeataCommit(ctx context.Context) error

	//回滚seata全局事务
	SeataRollback(ctx context.Context) error

	//获取seata事务的XID
	GetSeataXID(ctx context.Context) string
}

type Page

type Page struct {
	//当前页码,从1开始
	//Current page number, starting from 1
	PageNo int

	//每页多少条,默认20条
	//How many items per page, 20 items by default
	PageSize int

	//数据总条数
	//Total number of data
	TotalCount int

	//共多少页
	//How many pages
	PageCount int

	//是否是第一页
	//Is it the first page
	FirstPage bool

	//是否有上一页
	//Whether there is a previous page
	HasPrev bool

	//是否有下一页
	//Is there a next page
	HasNext bool

	//是否是最后一页
	//Is it the last page
	LastPage bool
}

Page 分页对象 Page Pagination object

func NewPage

func NewPage() *Page

NewPage 创建Page对象 NewPage Create Page object

Directories

Path Synopsis
Package decimal implements an arbitrary precision fixed-point decimal.
Package decimal implements an arbitrary precision fixed-point decimal.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL