1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        zormGo 輕量 ORM

        聯(lián)合創(chuàng)作 · 2023-09-30 01:40

        Go輕量ORM,零依賴(lài),支持達(dá)夢(mèng)(dm)、金倉(cāng)(kingbase) 、神通(shentong) 、南通(gbase)、TDenginemysql、postgresql、oraclemssql、sqlite、db2 、clickhouse...

        源碼: https://gitee.com/chunanyong/zorm
        官網(wǎng): https://zorm.cn
        測(cè)試用例: https://gitee.com/wuxiangege/zorm-examples/

        • 基于原生sql語(yǔ)句編寫(xiě),學(xué)習(xí)成本更低.
        • 代碼生成器
        • 代碼精簡(jiǎn),主體3000行,零依賴(lài)5000行,注釋詳細(xì),方便定制修改.
        • 支持事務(wù)傳播,這是zorm誕生的主要原因
        • 支持mysql,postgresql,oracle,mssql,sqlite,db2,dm(達(dá)夢(mèng)),kingbase(人大金倉(cāng)),shentong(神通),gbase(南通),TDengine,clickhouse
        • 支持多庫(kù)和讀寫(xiě)分離
        • 不支持聯(lián)合主鍵,變通認(rèn)為無(wú)主鍵,業(yè)務(wù)控制實(shí)現(xiàn)(艱難取舍)
        • 支持seata,hptx,dbpack分布式事務(wù),支持全局事務(wù)托管,不修改業(yè)務(wù)代碼,零侵入分布式事務(wù)
        • 支持clickhouse,更新,刪除語(yǔ)句使用SQL92標(biāo)準(zhǔn)語(yǔ)法

        支持國(guó)產(chǎn)數(shù)據(jù)庫(kù)

        達(dá)夢(mèng)(dm)

        配置zorm.DataSourceConfig的 DriverName:dm ,Dialect:dm
        達(dá)夢(mèng)數(shù)據(jù)庫(kù)驅(qū)動(dòng): gitee.com/chunanyong/dm
        達(dá)夢(mèng)的text類(lèi)型會(huì)映射為dm.DmClob,string不能接收,需要實(shí)現(xiàn)zorm.CustomDriverValueConver接口,自定義擴(kuò)展處理

        import (
        	// 00.引入數(shù)據(jù)庫(kù)驅(qū)動(dòng)
        	"gitee.com/chunanyong/dm"
        	"io"
        )
        
        // CustomDMText 實(shí)現(xiàn)ICustomDriverValueConver接口,擴(kuò)展自定義類(lèi)型,例如 達(dá)夢(mèng)數(shù)據(jù)庫(kù)TEXT類(lèi)型,映射出來(lái)的是dm.DmClob類(lèi)型,無(wú)法使用string類(lèi)型直接接收
        type CustomDMText struct{}
        
        // GetDriverValue 根據(jù)數(shù)據(jù)庫(kù)列類(lèi)型,返回driver.Value的實(shí)例,struct屬性類(lèi)型
        // map接收或者字段不存在,無(wú)法獲取到structFieldType,會(huì)傳入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 數(shù)據(jù)庫(kù)列類(lèi)型,GetDriverValue返回的driver.Value的臨時(shí)接收值,struct屬性類(lèi)型
        // map接收或者字段不存在,無(wú)法獲取到structFieldType,會(huì)傳入nil
        // 返回符合接收類(lèi)型值的指針,指針,指針!!!!
        func (dmtext CustomDMText) ConverDriverValue(ctx context.Context, columnType *sql.ColumnType, tempDriverValue driver.Value, structFieldType *reflect.Type) (interface{}, error) {
        	// 如果需要使用structFieldType,需要先判斷是否為nil
        	// if structFieldType != nil {
        	// }
        
        	// 類(lèi)型轉(zhuǎn)換
        	dmClob, isok := tempDriverValue.(*dm.DmClob)
        	if !isok {
        		return tempDriverValue, errors.New("->ConverDriverValue-->轉(zhuǎn)換至*dm.DmClob類(lèi)型失敗")
        	}
        	if dmClob == nil || !dmClob.Valid {
        		return new(string), nil
        	}
        	// 獲取長(zhǎng)度
        	dmlen, errLength := dmClob.GetLength()
        	if errLength != nil {
        		return dmClob, errLength
        	}
        
        	// int64轉(zhuǎn)成int類(lèi)型
        	strInt64 := strconv.FormatInt(dmlen, 10)
        	dmlenInt, errAtoi := strconv.Atoi(strInt64)
        	if errAtoi != nil {
        		return dmClob, errAtoi
        	}
        
        	// 讀取字符串
        	str, errReadString := dmClob.ReadString(1, dmlenInt)
        
        	// 處理空字符串或NULL造成的EOF錯(cuò)誤
        	if errReadString == io.EOF {
        		return new(string), nil
        	}
        
        	return &str, errReadString
        }
        // RegisterCustomDriverValueConver 注冊(cè)自定義的字段處理邏輯,用于驅(qū)動(dòng)無(wú)法直接轉(zhuǎn)換的場(chǎng)景,例如達(dá)夢(mèng)的 TEXT 無(wú)法直接轉(zhuǎn)化成 string
        // 一般是放到init方法里進(jìn)行注冊(cè)
        func init() {
        	// dialectColumnType 值是 Dialect.字段類(lèi)型 ,例如 dm.TEXT
        	zorm.RegisterCustomDriverValueConver("dm.TEXT", CustomDMText{})
        }

        金倉(cāng)(kingbase)

        配置zorm.DataSourceConfig的 DriverName:kingbase ,Dialect:kingbase
        金倉(cāng)驅(qū)動(dòng)說(shuō)明: https://help.kingbase.com.cn/doc-view-8108.html
        金倉(cāng)kingbase 8核心是基于postgresql 9.6,可以使用 https://github.com/lib/pq 進(jìn)行測(cè)試,生產(chǎn)環(huán)境建議使用官方驅(qū)動(dòng).
        注意修改 data/kingbase.conf中 ora_input_emptystr_isnull = false,因?yàn)間olang沒(méi)有null值,一般數(shù)據(jù)庫(kù)都是not null,golang的string默認(rèn)是'',如果這個(gè)設(shè)置為true,數(shù)據(jù)庫(kù)就會(huì)把值設(shè)置為null,和字段屬性not null 沖突,因此報(bào)錯(cuò).

        神通(shentong)

        建議使用官方驅(qū)動(dòng),配置zorm.DataSourceConfig的 DriverName:aci ,Dialect:shentong

        南通(gbase)

        暫時(shí)還未找到官方golang驅(qū)動(dòng),配置zorm.DataSourceConfig的 DriverName:gbase ,Dialect:gbase
        暫時(shí)先使用odbc驅(qū)動(dòng),DriverName:odbc ,Dialect:gbase

        TDengine

        • 因TDengine驅(qū)動(dòng)不支持事務(wù),需要設(shè)置DisableTransaction=true
        • 配置zorm.DataSourceConfig的 DriverName:taosSql或者taosRestful, Dialect:tdengine
        • zorm.DataSourceConfig的TDengineInsertsColumnName TDengine批量insert語(yǔ)句中是否有列名.默認(rèn)false沒(méi)有列名,插入值和數(shù)據(jù)庫(kù)列順序保持一致,減少語(yǔ)句長(zhǎng)度
        • 測(cè)試用例: https://www.yuque.com/u27016943/nrgi00/dnru3f
        • TDengine已收錄: https://github.com/taosdata/awesome-tdengine/#orm

        數(shù)據(jù)庫(kù)腳本和實(shí)體類(lèi)

        生成實(shí)體類(lèi)或手動(dòng)編寫(xiě),建議使用代碼生成器 https://gitee.com/zhou-a-xing/zorm-generate-struct

        
        package testzorm
        
        import (
        	"time"
        
        	"gitee.com/chunanyong/zorm"
        )
        
        // 建表語(yǔ)句
        
        /*
        
        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 表名常量,方便直接調(diào)用
        const demoStructTableName = "t_demo"
        
        // demoStruct 例子
        type demoStruct struct {
        	// 引入默認(rèn)的struct,隔離IEntityStruct的方法改動(dòng)
        	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"`
        
        	// ------------------數(shù)據(jù)庫(kù)字段結(jié)束,自定義字段寫(xiě)在下面---------------// 
        	// 如果查詢(xún)的字段在column tag中沒(méi)有找到,就會(huì)根據(jù)名稱(chēng)(不區(qū)分大小寫(xiě),支持 _ 下劃線(xiàn)轉(zhuǎn)駝峰)映射到struct的屬性上
        
        	// 模擬自定義的字段Active
        	Active int
        }
        
        // GetTableName 獲取表名稱(chēng)
        // IEntityStruct 接口的方法,實(shí)體類(lèi)需要實(shí)現(xiàn)!!!
        func (entity *demoStruct) GetTableName() string {
        	return demoStructTableName
        }
        
        // GetPKColumnName 獲取數(shù)據(jù)庫(kù)表的主鍵字段名稱(chēng).因?yàn)橐嫒軲ap,只能是數(shù)據(jù)庫(kù)的字段名稱(chēng)
        // 不支持聯(lián)合主鍵,變通認(rèn)為無(wú)主鍵,業(yè)務(wù)控制實(shí)現(xiàn)(艱難取舍)
        // 如果沒(méi)有主鍵,也需要實(shí)現(xiàn)這個(gè)方法, return "" 即可
        // IEntityStruct 接口的方法,實(shí)體類(lèi)需要實(shí)現(xiàn)!!!
        func (entity *demoStruct) GetPKColumnName() string {
        	// 如果沒(méi)有主鍵
        	// return ""
        	return "id"
        }
        
        
        // GetDefaultValue 獲取列的默認(rèn)值Map,用于Insert和Update Struct對(duì)象,返回map的key是Struct屬性名,value是默認(rèn)值,value可以是nil.不能是類(lèi)型的默認(rèn)值,比如int類(lèi)型設(shè)置默認(rèn)值為0
        // BindContextDefaultValue 優(yōu)先級(jí)高于 GetDefaultValue
        
        //func (entity *EntityStruct) GetDefaultValue() map[string]interface{} {
        //	return map[string]interface{}{"CreateTime": time.Now(),"Active":nil}
        //}
        
        // newDemoStruct 創(chuàng)建一個(gè)默認(rèn)對(duì)象
        func newDemoStruct() demoStruct {
        	demo := demoStruct{
        		// 如果Id=="",保存時(shí)zorm會(huì)調(diào)用zorm.FuncGenerateStringID(ctx),默認(rèn)時(shí)間戳+隨機(jī)數(shù),也可以自己定義實(shí)現(xiàn)方式,例如 zorm.FuncGenerateStringID=funcmyId
        		Id:		 zorm.FuncGenerateStringID(ctx),
        		UserName:   "defaultUserName",
        		Password:   "defaultPassword",
        		Active:	 1,
        		CreateTime: time.Now(),
        	}
        	return demo
        }
        

        測(cè)試用例即文檔

        測(cè)試用例: https://gitee.com/wuxiangege/zorm-examples

        
        // testzorm 使用原生的sql語(yǔ)句,沒(méi)有對(duì)sql語(yǔ)法做限制.語(yǔ)句使用Finder作為載體
        // 占位符統(tǒng)一使用?,zorm會(huì)根據(jù)數(shù)據(jù)庫(kù)類(lèi)型,自動(dòng)替換占位符,例如postgresql數(shù)據(jù)庫(kù)把?替換成$1,$2...
        // zorm使用 ctx context.Context 參數(shù)實(shí)現(xiàn)事務(wù)傳播,ctx從web層傳遞進(jìn)來(lái)即可,例如gin的c.Request.Context()
        // zorm的事務(wù)操作需要顯式使用zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {})開(kāi)啟
        package testzorm
        
        import (
        	"context"
        	"fmt"
        	"testing"
        	"time"
        
        	"gitee.com/chunanyong/zorm"
        
        	// 00.引入數(shù)據(jù)庫(kù)驅(qū)動(dòng)
        	_ "github.com/go-sql-driver/mysql"
        )
        
        // dbDao 代表一個(gè)數(shù)據(jù)庫(kù),如果有多個(gè)數(shù)據(jù)庫(kù),就對(duì)應(yīng)聲明多個(gè)DBDao
        var dbDao *zorm.DBDao
        
        // 01.初始化DBDao
        func init() {
        
        	// 自定義zorm日志輸出
        	// zorm.LogCallDepth = 4 // 日志調(diào)用的層級(jí)
        	// zorm.FuncLogError = myFuncLogError // 記錄異常日志的函數(shù)
        	// zorm.FuncLogPanic = myFuncLogPanic // 記錄panic日志,默認(rèn)使用defaultLogError實(shí)現(xiàn)
        	// zorm.FuncPrintSQL = myFuncPrintSQL // 打印sql的函數(shù)
        
        	// 自定義日志輸出格式,把FuncPrintSQL函數(shù)重新賦值
        	// log.SetFlags(log.LstdFlags)
        	// zorm.FuncPrintSQL = zorm.FuncPrintSQL
        
        	// 自定義主鍵生成
        	// zorm.FuncGenerateStringID=funcmyId
        
        	// 自定義Tag列名
        	// zorm.FuncWrapFieldTagName=funcmyTagName
        
        	// 自定義decimal類(lèi)型實(shí)現(xiàn),例如github.com/shopspring/decimal
        	// zorm.FuncDecimalValue=funcmyDecimal
        
        	// Go數(shù)據(jù)庫(kù)驅(qū)動(dòng)列表:https://github.com/golang/go/wiki/SQLDrivers
        
        	// dbDaoConfig 數(shù)據(jù)庫(kù)的配置.這里只是模擬,生產(chǎn)應(yīng)該是讀取配置配置文件,構(gòu)造DataSourceConfig
        	dbDaoConfig := zorm.DataSourceConfig{
        		// DSN 數(shù)據(jù)庫(kù)的連接字符串,parseTime=true會(huì)自動(dòng)轉(zhuǎn)換為time格式,默認(rèn)查詢(xún)出來(lái)的是[]byte數(shù)組.&loc=Local用于設(shè)置時(shí)區(qū)
        		DSN: "root:root@tcp(127.0.0.1:3306)/zorm?charset=utf8&parseTime=true&loc=Local",
        		// DriverName 數(shù)據(jù)庫(kù)驅(qū)動(dòng)名稱(chēng):mysql,postgres,oracle(go-ora),sqlserver,sqlite3,go_ibm_db,clickhouse,dm,kingbase,aci,taosSql|taosRestful 和Dialect對(duì)應(yīng)
        		// sql.Open(DriverName,DSN) DriverName就是驅(qū)動(dòng)的sql.Open第一個(gè)字符串參數(shù),根據(jù)驅(qū)動(dòng)實(shí)際情況獲取
        		DriverName: "mysql",
        		// Dialect 數(shù)據(jù)庫(kù)方言:mysql,postgresql,oracle,mssql,sqlite,db2,clickhouse,dm,kingbase,shentong,tdengine 和 DriverName 對(duì)應(yīng)
        		Dialect: "mysql",
        		// MaxOpenConns 數(shù)據(jù)庫(kù)最大連接數(shù) 默認(rèn)50
        		MaxOpenConns: 50,
        		// MaxIdleConns 數(shù)據(jù)庫(kù)最大空閑連接數(shù) 默認(rèn)50
        		MaxIdleConns: 50,
        		// ConnMaxLifetimeSecond 連接存活秒時(shí)間. 默認(rèn)600(10分鐘)后連接被銷(xiāo)毀重建.避免數(shù)據(jù)庫(kù)主動(dòng)斷開(kāi)連接,造成死連接.MySQL默認(rèn)wait_timeout 28800秒(8小時(shí))
        		ConnMaxLifetimeSecond: 600,
        		// SlowSQLMillis 慢sql的時(shí)間閾值,單位毫秒.小于0是禁用SQL語(yǔ)句輸出;等于0是只輸出SQL語(yǔ)句,不計(jì)算執(zhí)行時(shí)間;大于0是計(jì)算SQL執(zhí)行時(shí)間,并且>=SlowSQLMillis值
        		SlowSQLMillis: 0,
        		// DefaultTxOptions 事務(wù)隔離級(jí)別的默認(rèn)配置,默認(rèn)為nil
        		// DefaultTxOptions: nil,
        		// 如果是使用分布式事務(wù),建議使用默認(rèn)配置
        		// DefaultTxOptions: &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false},
        
        		// FuncGlobalTransaction seata/hptx全局分布式事務(wù)的適配函數(shù),返回IGlobalTransaction接口的實(shí)現(xiàn)
        		// 業(yè)務(wù)必須調(diào)用 ctx,_=zorm.BindContextEnableGlobalTransaction(ctx) 開(kāi)啟全局分布事務(wù)
        		// FuncGlobalTransaction : MyFuncGlobalTransaction,
        
        		// SQLDB 使用現(xiàn)有的數(shù)據(jù)庫(kù)連接,優(yōu)先級(jí)高于DSN
        		// SQLDB : nil,
        
        		// DisableTransaction 禁用事務(wù),默認(rèn)false,如果設(shè)置了DisableTransaction=true,Transaction方法失效,不再要求有事務(wù),為了處理某些數(shù)據(jù)庫(kù)不支持事務(wù),比如TDengine
        		// 禁用事務(wù)應(yīng)該有驅(qū)動(dòng)偽造事務(wù)API,不應(yīng)該有orm實(shí)現(xiàn),clickhouse的驅(qū)動(dòng)就是這樣做的
        		// DisableTransaction :false,
        
        		// TDengineInsertsColumnName TDengine批量insert語(yǔ)句中是否有列名.默認(rèn)false沒(méi)有列名,插入值和數(shù)據(jù)庫(kù)列順序保持一致,減少語(yǔ)句長(zhǎng)度
        		// TDengineInsertsColumnName :false,
        	}
        
        	// 根據(jù)dbDaoConfig創(chuàng)建dbDao, 一個(gè)數(shù)據(jù)庫(kù)只執(zhí)行一次,第一個(gè)執(zhí)行的數(shù)據(jù)庫(kù)為 defaultDao,后續(xù)zorm.xxx方法,默認(rèn)使用的就是defaultDao
        	dbDao, _ = zorm.NewDBDao(&dbDaoConfig)
        }
        
        // TestInsert 02.測(cè)試保存Struct對(duì)象
        func TestInsert(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	// 需要手動(dòng)開(kāi)啟事務(wù),匿名函數(shù)返回的error如果不是nil,事務(wù)就會(huì)回滾.如果設(shè)置了DisableTransaction=true,Transaction方法失效,不再要求有事務(wù)
        	// 如果zorm.DataSourceConfig.DefaultTxOptions配置不滿(mǎn)足需求,可以在zorm.Transaction事務(wù)方法前設(shè)置事務(wù)的隔離級(jí)別
        	// 例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions為nil,使用zorm.DataSourceConfig.DefaultTxOptions
        	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
        		// 創(chuàng)建一個(gè)demo對(duì)象
        		demo := newDemoStruct()
        
        		// 保存對(duì)象,參數(shù)是對(duì)象指針.如果主鍵是自增,會(huì)賦值到對(duì)象的主鍵屬性
        		_, err := zorm.Insert(ctx, &demo)
        
        		// 如果返回的err不是nil,事務(wù)就會(huì)回滾
        		return nil, err
        	})
        	// 標(biāo)記測(cè)試失敗
        	if err != nil {
        		t.Errorf("錯(cuò)誤:%v", err)
        	}
        }
        
        // TestInsertSlice 03.測(cè)試批量保存Struct對(duì)象的Slice
        // 如果是自增主鍵,無(wú)法對(duì)Struct對(duì)象里的主鍵屬性賦值
        func TestInsertSlice(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	// 需要手動(dòng)開(kāi)啟事務(wù),匿名函數(shù)返回的error如果不是nil,事務(wù)就會(huì)回滾.如果設(shè)置了DisableTransaction=true,Transaction方法失效,不再要求有事務(wù)
        	// 如果zorm.DataSourceConfig.DefaultTxOptions配置不滿(mǎn)足需求,可以在zorm.Transaction事務(wù)方法前設(shè)置事務(wù)的隔離級(jí)別
        	// 例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions為nil,使用zorm.DataSourceConfig.DefaultTxOptions
        	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
        
        		// slice存放的類(lèi)型是zorm.IEntityStruct!!!使用IEntityStruct接口,兼容Struct實(shí)體類(lèi)
        		demoSlice := make([]zorm.IEntityStruct, 0)
        
        		// 創(chuàng)建對(duì)象1
        		demo1 := newDemoStruct()
        		demo1.UserName = "demo1"
        		// 創(chuàng)建對(duì)象2
        		demo2 := newDemoStruct()
        		demo2.UserName = "demo2"
        
        		demoSlice = append(demoSlice, &demo1, &demo2)
        
        		// 批量保存對(duì)象,如果主鍵是自增,無(wú)法保存自增的ID到對(duì)象里.
        		_, err := zorm.InsertSlice(ctx, demoSlice)
        
        		// 如果返回的err不是nil,事務(wù)就會(huì)回滾
        		return nil, err
        	})
        	// 標(biāo)記測(cè)試失敗
        	if err != nil {
        		t.Errorf("錯(cuò)誤:%v", err)
        	}
        }
        
        // TestInsertEntityMap 04.測(cè)試保存EntityMap對(duì)象,用于不方便使用struct的場(chǎng)景,使用Map作為載體
        func TestInsertEntityMap(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	// 需要手動(dòng)開(kāi)啟事務(wù),匿名函數(shù)返回的error如果不是nil,事務(wù)就會(huì)回滾.如果設(shè)置了DisableTransaction=true,Transaction方法失效,不再要求有事務(wù)
        	// 如果zorm.DataSourceConfig.DefaultTxOptions配置不滿(mǎn)足需求,可以在zorm.Transaction事務(wù)方法前設(shè)置事務(wù)的隔離級(jí)別
        	// 例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions為nil,使用zorm.DataSourceConfig.DefaultTxOptions
        	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
        		// 創(chuàng)建一個(gè)EntityMap,需要傳入表名
        		entityMap := zorm.NewEntityMap(demoStructTableName)
        		// 設(shè)置主鍵名稱(chēng)
        		entityMap.PkColumnName = "id"
        		// 如果是自增序列,設(shè)置序列的值
        		// entityMap.PkSequence = "mySequence"
        
        		// Set 設(shè)置數(shù)據(jù)庫(kù)的字段值
        		// 如果主鍵是自增或者序列,不要entityMap.Set主鍵的值
        		entityMap.Set("id", zorm.FuncGenerateStringID(ctx))
        		entityMap.Set("userName", "entityMap-userName")
        		entityMap.Set("password", "entityMap-password")
        		entityMap.Set("createTime", time.Now())
        		entityMap.Set("active", 1)
        
        		// 執(zhí)行
        		_, err := zorm.InsertEntityMap(ctx, entityMap)
        
        		// 如果返回的err不是nil,事務(wù)就會(huì)回滾
        		return nil, err
        	})
        	// 標(biāo)記測(cè)試失敗
        	if err != nil {
        		t.Errorf("錯(cuò)誤:%v", err)
        	}
        }
        
        
        // TestInsertEntityMapSlice 05.測(cè)試批量保存[]IEntityMap,用于不方便使用struct的場(chǎng)景,使用Map作為載體
        func TestInsertEntityMapSlice(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	_, err := Transaction(ctx, func(ctx context.Context) (interface{}, error) {
        		entityMapSlice := make([]IEntityMap, 0)
        		entityMap1 := NewEntityMap(demoStructTableName)
        		entityMap1.PkColumnName = "id"
        		entityMap1.Set("id", zorm.FuncGenerateStringID(ctx))
        		entityMap1.Set("userName", "entityMap-userName1")
        		entityMap1.Set("password", "entityMap-password1")
        		entityMap1.Set("createTime", time.Now())
        		entityMap1.Set("active", 1)
        
        		entityMap2 := NewEntityMap(demoStructTableName)
        		entityMap2.PkColumnName = "id"
        		entityMap2.Set("id", zorm.FuncGenerateStringID(ctx))
        		entityMap2.Set("userName", "entityMap-userName2")
        		entityMap2.Set("password", "entityMap-password2")
        		entityMap2.Set("createTime", time.Now())
        		entityMap2.Set("active", 2)
        
        		entityMapSlice = append(entityMapSlice, entityMap1 ,entityMap2)
        
        		// 執(zhí)行
        		_, err := zorm.InsertEntityMapSlice(ctx, entityMapSlice)
        
        		// 如果返回的err不是nil,事務(wù)就會(huì)回滾
        		return nil, err
        	})
        	// 標(biāo)記測(cè)試失敗
        	if err != nil {
        		t.Errorf("錯(cuò)誤:%v", err)
        	}
        }
        
        // TestQueryRow 06.測(cè)試查詢(xún)一個(gè)struct對(duì)象
        func TestQueryRow(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	// 聲明一個(gè)對(duì)象的指針,用于承載返回的數(shù)據(jù)
        	demo := demoStruct{}
        
        	// 構(gòu)造查詢(xún)用的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默認(rèn)啟用了sql注入檢查,禁止語(yǔ)句中拼接 ' 單引號(hào),可以設(shè)置 finder.InjectionCheck = false 解開(kāi)限制
        
        	// finder.Append 第一個(gè)參數(shù)是語(yǔ)句,后面的參數(shù)是對(duì)應(yīng)的值,值的順序要正確.語(yǔ)句統(tǒng)一使用?,zorm會(huì)處理數(shù)據(jù)庫(kù)的差異
        	// in (?) 參數(shù)必須有()括號(hào),不能 in ?
        	finder.Append("WHERE id=? and active in(?)", "20210630163227149563000042432429", []int{0, 1})
        
        	// 如何使用like
        	// finder.Append("WHERE id like ? ", "20210630163227149563000042432429%")
        
        	// 執(zhí)行查詢(xún),has為true表示數(shù)據(jù)庫(kù)有數(shù)據(jù)
        	has, err := zorm.QueryRow(ctx, finder, &demo)
        
        	if err != nil { // 標(biāo)記測(cè)試失敗
        		t.Errorf("錯(cuò)誤:%v", err)
        	}
        	// 打印結(jié)果
        	fmt.Println(demo)
        }
        
        // TestQueryRowMap 07.測(cè)試查詢(xún)map接收結(jié)果,用于不太適合struct的場(chǎng)景,比較靈活
        func TestQueryRowMap(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	// 構(gòu)造查詢(xún)用的finder
        	// finder := zorm.NewSelectFinder(demoStructTableName) // select * from t_demo
        	finder := zorm.NewFinder().Append("SELECT * FROM " + demoStructTableName) // select * from t_demo
        	// finder.Append 第一個(gè)參數(shù)是語(yǔ)句,后面的參數(shù)是對(duì)應(yīng)的值,值的順序要正確.語(yǔ)句統(tǒng)一使用?,zorm會(huì)處理數(shù)據(jù)庫(kù)的差異
        	// in (?) 參數(shù)必須有()括號(hào),不能 in ?
        	finder.Append("WHERE id=? and active in(?)", "20210630163227149563000042432429", []int{0, 1})
        	// 執(zhí)行查詢(xún)
        	resultMap, err := zorm.QueryRowMap(ctx, finder)
        
        	if err != nil { // 標(biāo)記測(cè)試失敗
        		t.Errorf("錯(cuò)誤:%v", err)
        	}
        	// 打印結(jié)果
        	fmt.Println(resultMap)
        }
        
        // TestQuery 08.測(cè)試查詢(xún)對(duì)象列表
        func TestQuery(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	// 創(chuàng)建用于接收結(jié)果的slice
        	list := make([]demoStruct, 0)
        
        	// 構(gòu)造查詢(xún)用的finder
        	// finder := zorm.NewSelectFinder(demoStructTableName) // select * from t_demo
        	finder := zorm.NewFinder().Append("SELECT id FROM " + demoStructTableName) // select * from t_demo
        	// 創(chuàng)建分頁(yè)對(duì)象,查詢(xún)完成后,page對(duì)象可以直接給前端分頁(yè)組件使用
        	page := zorm.NewPage()
        	page.PageNo = 1   // 查詢(xún)第1頁(yè),默認(rèn)是1
        	page.PageSize = 20 // 每頁(yè)20條,默認(rèn)是20
        
        	// 不查詢(xún)總條數(shù)
        	// finder.SelectTotalCount = false
        
        	// 如果是特別復(fù)雜的語(yǔ)句,造成count語(yǔ)句構(gòu)造失敗,可以手動(dòng)指定count語(yǔ)句
        	// countFinder := zorm.NewFinder().Append("select count(*) from (")
        	// countFinder.AppendFinder(finder)
        	// countFinder.Append(") tempcountfinder")
        	// finder.CountFinder = countFinder
        
        	// 執(zhí)行查詢(xún)
        	err := zorm.Query(ctx, finder, &list, page)
        	if err != nil { // 標(biāo)記測(cè)試失敗
        		t.Errorf("錯(cuò)誤:%v", err)
        	}
        	// 打印結(jié)果
        	fmt.Println("總條數(shù):", page.TotalCount, "  列表:", list)
        }
        
        // TestQueryMap 09.測(cè)試查詢(xún)map列表,用于不方便使用struct的場(chǎng)景,一條記錄是一個(gè)map對(duì)象
        func TestQueryMap(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	// 構(gòu)造查詢(xún)用的finder
        	// finder := zorm.NewSelectFinder(demoStructTableName) // select * from t_demo
        	finder := zorm.NewFinder().Append("SELECT * FROM " + demoStructTableName) // select * from t_demo
        	// 創(chuàng)建分頁(yè)對(duì)象,查詢(xún)完成后,page對(duì)象可以直接給前端分頁(yè)組件使用
        	page := zorm.NewPage()
        	page.PageNo = 1   // 查詢(xún)第1頁(yè),默認(rèn)是1
        	page.PageSize = 20 // 每頁(yè)20條,默認(rèn)是20
        
        	// 不查詢(xún)總條數(shù)
        	// finder.SelectTotalCount = false
        
        	// 如果是特別復(fù)雜的語(yǔ)句,造成count語(yǔ)句構(gòu)造失敗,可以手動(dòng)指定count語(yǔ)句
        	// countFinder := zorm.NewFinder().Append("select count(*) from (")
        	// countFinder.AppendFinder(finder)
        	// countFinder.Append(") tempcountfinder")
        	// finder.CountFinder = countFinder
        
        	// 執(zhí)行查詢(xún)
        	listMap, err := zorm.QueryMap(ctx, finder, page)
        	if err != nil { // 標(biāo)記測(cè)試失敗
        		t.Errorf("錯(cuò)誤:%v", err)
        	}
        	// 打印結(jié)果
        	fmt.Println("總條數(shù):", page.TotalCount, "  列表:", listMap)
        }
        
        // TestUpdateNotZeroValue 10.更新struct對(duì)象,只更新不為零值的字段.主鍵必須有值
        func TestUpdateNotZeroValue(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	// 需要手動(dòng)開(kāi)啟事務(wù),匿名函數(shù)返回的error如果不是nil,事務(wù)就會(huì)回滾.如果設(shè)置了DisableTransaction=true,Transaction方法失效,不再要求有事務(wù)
        	// 如果zorm.DataSourceConfig.DefaultTxOptions配置不滿(mǎn)足需求,可以在zorm.Transaction事務(wù)方法前設(shè)置事務(wù)的隔離級(jí)別
        	// 例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions為nil,使用zorm.DataSourceConfig.DefaultTxOptions
        	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
        		// 聲明一個(gè)對(duì)象的指針,用于更新數(shù)據(jù)
        		demo := demoStruct{}
        		demo.Id = "20210630163227149563000042432429"
        		demo.UserName = "UpdateNotZeroValue"
        
                // 指定必須更新的數(shù)據(jù)庫(kù)字段,只對(duì)UpdateNotZeroValue方法有效.cols是數(shù)據(jù)庫(kù)列名切片
        		// ctx里bind的值z(mì)orm不會(huì)清空,使用時(shí)不要覆蓋原始的ctx或者不要傳給多個(gè)UpdateNotZeroValue方法.
        		// newCtx, _ := zorm.BindContextMustUpdateCols(ctx, []string{"active"})
        		// _, err := zorm.UpdateNotZeroValue(newCtx, &demo)
        
        		// 更新 "sql":"UPDATE t_demo SET userName=? WHERE id=?","args":["UpdateNotZeroValue","20210630163227149563000042432429"]
        		_, err := zorm.UpdateNotZeroValue(ctx, &demo)
        
        		// 如果返回的err不是nil,事務(wù)就會(huì)回滾
        		return nil, err
        	})
        	if err != nil { // 標(biāo)記測(cè)試失敗
        		t.Errorf("錯(cuò)誤:%v", err)
        	}
        
        }
        
        // TestUpdate 11.更新struct對(duì)象,更新所有字段.主鍵必須有值
        func TestUpdate(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	// BindContextOnlyUpdateCols 指定僅更新的數(shù)據(jù)庫(kù)字段,只對(duì)Update方法有效.cols是數(shù)據(jù)庫(kù)列名切片
            // ctx里bind的值z(mì)orm不會(huì)清空,使用時(shí)不要覆蓋原始的ctx或者不要傳給多個(gè)Update方法.
        	// ctx, _ = zorm.BindContextOnlyUpdateCols(ctx, []string{"userName", "active"})
        
        
        	// 需要手動(dòng)開(kāi)啟事務(wù),匿名函數(shù)返回的error如果不是nil,事務(wù)就會(huì)回滾.如果設(shè)置了DisableTransaction=true,Transaction方法失效,不再要求有事務(wù)
        	// 如果zorm.DataSourceConfig.DefaultTxOptions配置不滿(mǎn)足需求,可以在zorm.Transaction事務(wù)方法前設(shè)置事務(wù)的隔離級(jí)別
        	// 例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions為nil,使用zorm.DataSourceConfig.DefaultTxOptions
        	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
        
        		// 聲明一個(gè)對(duì)象的指針,用于更新數(shù)據(jù)
        		demo := demoStruct{}
        		demo.Id = "20210630163227149563000042432429"
        		demo.UserName = "TestUpdate"
        
        		_, err := zorm.Update(ctx, &demo)
        
        		// 如果返回的err不是nil,事務(wù)就會(huì)回滾
        		return nil, err
        	})
        	if err != nil { // 標(biāo)記測(cè)試失敗
        		t.Errorf("錯(cuò)誤:%v", err)
        	}
        }
        
        // TestUpdateFinder 12.通過(guò)finder更新,zorm最靈活的方式,可以編寫(xiě)任何更新語(yǔ)句,甚至手動(dòng)編寫(xiě)insert語(yǔ)句
        func TestUpdateFinder(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	// 需要手動(dòng)開(kāi)啟事務(wù),匿名函數(shù)返回的error如果不是nil,事務(wù)就會(huì)回滾.如果設(shè)置了DisableTransaction=true,Transaction方法失效,不再要求有事務(wù)
        	// 如果zorm.DataSourceConfig.DefaultTxOptions配置不滿(mǎn)足需求,可以在zorm.Transaction事務(wù)方法前設(shè)置事務(wù)的隔離級(jí)別
        	// 例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions為nil,使用zorm.DataSourceConfig.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,"20210630163227149563000042432429"]
        		_, err := zorm.UpdateFinder(ctx, finder)
        
        		// 如果返回的err不是nil,事務(wù)就會(huì)回滾
        		return nil, err
        	})
        	if err != nil { // 標(biāo)記測(cè)試失敗
        		t.Errorf("錯(cuò)誤:%v", err)
        	}
        
        }
        
        // TestUpdateEntityMap 13.更新一個(gè)EntityMap,主鍵必須有值
        func TestUpdateEntityMap(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	// 需要手動(dòng)開(kāi)啟事務(wù),匿名函數(shù)返回的error如果不是nil,事務(wù)就會(huì)回滾.如果設(shè)置了DisableTransaction=true,Transaction方法失效,不再要求有事務(wù)
        	// 如果zorm.DataSourceConfig.DefaultTxOptions配置不滿(mǎn)足需求,可以在zorm.Transaction事務(wù)方法前設(shè)置事務(wù)的隔離級(jí)別
        	// 例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions為nil,使用zorm.DataSourceConfig.DefaultTxOptions
        	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
        		// 創(chuàng)建一個(gè)EntityMap,需要傳入表名
        		entityMap := zorm.NewEntityMap(demoStructTableName)
        		// 設(shè)置主鍵名稱(chēng)
        		entityMap.PkColumnName = "id"
        		// Set 設(shè)置數(shù)據(jù)庫(kù)的字段值,主鍵必須有值
        		entityMap.Set("id", "20210630163227149563000042432429")
        		entityMap.Set("userName", "TestUpdateEntityMap")
        		// 更新 "sql":"UPDATE t_demo SET userName=? WHERE id=?","args":["TestUpdateEntityMap","20210630163227149563000042432429"]
        		_, err := zorm.UpdateEntityMap(ctx, entityMap)
        
        		// 如果返回的err不是nil,事務(wù)就會(huì)回滾
        		return nil, err
        	})
        	if err != nil { // 標(biāo)記測(cè)試失敗
        		t.Errorf("錯(cuò)誤:%v", err)
        	}
        
        }
        
        // TestDelete 14.刪除一個(gè)struct對(duì)象,主鍵必須有值
        func TestDelete(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	// 需要手動(dòng)開(kāi)啟事務(wù),匿名函數(shù)返回的error如果不是nil,事務(wù)就會(huì)回滾.如果設(shè)置了DisableTransaction=true,Transaction方法失效,不再要求有事務(wù)
        	// 如果zorm.DataSourceConfig.DefaultTxOptions配置不滿(mǎn)足需求,可以在zorm.Transaction事務(wù)方法前設(shè)置事務(wù)的隔離級(jí)別
        	// 例如 ctx, _ := dbDao.BindContextTxOptions(ctx, &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false}),如果txOptions為nil,使用zorm.DataSourceConfig.DefaultTxOptions
        	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
        		demo := demoStruct{}
        		demo.Id = "20210630163227149563000042432429"
        
        		// 刪除 "sql":"DELETE FROM t_demo WHERE id=?","args":["20210630163227149563000042432429"]
        		_, err := zorm.Delete(ctx, &demo)
        
        		// 如果返回的err不是nil,事務(wù)就會(huì)回滾
        		return nil, err
        	})
        	if err != nil { // 標(biāo)記測(cè)試失敗
        		t.Errorf("錯(cuò)誤:%v", err)
        	}
        
        }
        
        // TestProc 15.測(cè)試調(diào)用存儲(chǔ)過(guò)程
        func TestProc(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	demo := demoStruct{}
        	finder := zorm.NewFinder().Append("call testproc(?) ", "u_10001")
        	zorm.QueryRow(ctx, finder, &demo)
        	fmt.Println(demo)
        }
        
        // TestFunc 16.測(cè)試調(diào)用自定義函數(shù)
        func TestFunc(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	userName := ""
        	finder := zorm.NewFinder().Append("select testfunc(?) ", "u_10001")
        	zorm.QueryRow(ctx, finder, &userName)
        	fmt.Println(userName)
        }
        
        // TestOther 17.其他的一些說(shuō)明.非常感謝您能看到這一行
        func TestOther(t *testing.T) {
        	// ctx 一般一個(gè)請(qǐng)求一個(gè)ctx,正常應(yīng)該有web層傳入,例如gin的c.Request.Context().這里只是模擬
        	var ctx = context.Background()
        
        	// 場(chǎng)景1.多個(gè)數(shù)據(jù)庫(kù).通過(guò)對(duì)應(yīng)數(shù)據(jù)庫(kù)的dbDao,調(diào)用BindContextDBConnection函數(shù),把這個(gè)數(shù)據(jù)庫(kù)的連接綁定到返回的ctx上,然后把ctx傳遞到zorm的函數(shù)即可
        	// 也可以重寫(xiě)FuncReadWriteStrategy函數(shù),通過(guò)ctx設(shè)置不同的key,返回指定數(shù)據(jù)庫(kù)的DBDao
        	newCtx, err := dbDao.BindContextDBConnection(ctx)
        	if err != nil { // 標(biāo)記測(cè)試失敗
        		t.Errorf("錯(cuò)誤:%v", err)
        	}
        
        	finder := zorm.NewFinder().Append("SELECT * FROM " + demoStructTableName) // select * from t_demo
        	// 把新產(chǎn)生的newCtx傳遞到zorm的函數(shù)
        	list, _ := zorm.QueryMap(newCtx, finder, nil)
        	fmt.Println(list)
        
        	// 場(chǎng)景2.單個(gè)數(shù)據(jù)庫(kù)的讀寫(xiě)分離.設(shè)置讀寫(xiě)分離的策略函數(shù).
        	zorm.FuncReadWriteStrategy = myReadWriteStrategy
        
        	// 場(chǎng)景3.如果是多個(gè)數(shù)據(jù)庫(kù),每個(gè)數(shù)據(jù)庫(kù)還讀寫(xiě)分離,按照 場(chǎng)景1 處理. 
        	// 也可以重寫(xiě)FuncReadWriteStrategy函數(shù),通過(guò)ctx設(shè)置不同的key,返回指定數(shù)據(jù)庫(kù)的DBDao
        
        }
        
        // myReadWriteStrategy 數(shù)據(jù)庫(kù)的讀寫(xiě)分離的策略 rwType=0 read,rwType=1 write
        // 也可以通過(guò)ctx設(shè)置不同的key,返回指定數(shù)據(jù)庫(kù)的DBDao
        func myReadWriteStrategy(ctx context.Context, rwType int) (*zorm.DBDao, error) {
        	// 根據(jù)自己的業(yè)務(wù)場(chǎng)景,返回需要的讀寫(xiě)dao,每次需要數(shù)據(jù)庫(kù)的連接的時(shí)候,會(huì)調(diào)用這個(gè)函數(shù)
        	// if rwType == 0 {
        	// 	return dbReadDao
        	// }
        	// return dbWriteDao
        
        	return DbDao, nil
        }
        
        // --------------------------------------------
        // ICustomDriverValueConver接口,參見(jiàn)達(dá)夢(mèng)的例子
        
        // --------------------------------------------
        // OverrideFunc 重寫(xiě)ZORM的函數(shù),當(dāng)你使用這個(gè)函數(shù)時(shí),你必須知道自己在做什么
        
        //oldInsertFunc 默認(rèn)的Insert實(shí)現(xiàn)
        var oldInsertFunc func(ctx context.Context, entity IEntityStruct) (int, error)
        
        //newInsertFunc 新的Insert實(shí)現(xiàn)
        var newInsertFunc = func(ctx context.Context, entity IEntityStruct) (int, error) {
        	fmt.Println("Insert前")
        	i, err := oldInsertFunc(ctx, entity)
        	fmt.Println("Insert后")
        	return i, err
        }
        
        // 在init函數(shù)中注冊(cè)覆蓋老的函數(shù)
        func init() {
        	ok, oldFunc, err := zorm.OverrideFunc("Insert", newInsertFunc)
        	if ok && err == nil {
        		oldInsertFunc = oldFunc.(func(ctx context.Context, entity IEntityStruct) (int, error))
        	}
        }
        瀏覽 38
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        編輯 分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        編輯 分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            日本xxxx在线观看 | ppypp电影频道 | 夜夜撸网站 | 五月天综合网 | 快播一区二区 | 啊啊啊用力插视频 | 美女操逼逼 | 国产精品久久久久久久久久免 | 香蕉尹人网 | 91色色影院 |