{"id":17570576,"url":"https://github.com/springrain/zorm","last_synced_at":"2026-01-17T04:03:58.809Z","repository":{"id":56804004,"uuid":"244607242","full_name":"springrain/zorm","owner":"springrain","description":"Go轻量ORM,支持达梦(dm),金仓(kingbase),神通(shentong),南大通用(gbase),TDengine,mysql,postgresql,oracle,mssql,sqlite,db2,clickhouse...","archived":false,"fork":false,"pushed_at":"2026-01-14T04:29:20.000Z","size":1481,"stargazers_count":76,"open_issues_count":0,"forks_count":6,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-14T07:23:50.415Z","etag":null,"topics":["clickhouse","database","dm","gbase","go","go-orm","golang","golang-orm","kingbase","mysql","orm","shentong","sql","sqlx","tdengine"],"latest_commit_sha":null,"homepage":"https://zorm.cn","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/springrain.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-03-03T10:30:09.000Z","updated_at":"2026-01-14T04:29:23.000Z","dependencies_parsed_at":"2023-02-19T05:55:18.904Z","dependency_job_id":"3ffc0d3e-bbc5-45aa-b0c2-b44e3f47d487","html_url":"https://github.com/springrain/zorm","commit_stats":{"total_commits":976,"total_committers":14,"mean_commits":69.71428571428571,"dds":0.03176229508196726,"last_synced_commit":"a50e8629cdef8bd2bc5b71c81d3039d463ab6440"},"previous_names":[],"tags_count":52,"template":false,"template_full_name":null,"purl":"pkg:github/springrain/zorm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/springrain%2Fzorm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/springrain%2Fzorm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/springrain%2Fzorm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/springrain%2Fzorm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/springrain","download_url":"https://codeload.github.com/springrain/zorm/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/springrain%2Fzorm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28494183,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T02:39:23.645Z","status":"ssl_error","status_checked_at":"2026-01-17T02:34:19.649Z","response_time":85,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clickhouse","database","dm","gbase","go","go-orm","golang","golang-orm","kingbase","mysql","orm","shentong","sql","sqlx","tdengine"],"created_at":"2024-10-21T18:01:15.078Z","updated_at":"2026-01-17T04:03:58.781Z","avatar_url":"https://github.com/springrain.png","language":"Go","readme":"## Introduction\n![zorm logo](zorm-logo.png)  \nThis is a lightweight ORM,zero dependency, that supports DM,Kingbase,shentong,TDengine,mysql,postgresql,oracle,mssql,sqlite,db2,clickhouse...\n\nOfficial website:https://zorm.cn  \nsource code address:https://gitee.com/chunanyong/zorm  || https://gitcode.com/springrain/zorm   \ntest case: https://gitee.com/wuxiangege/zorm-examples/  \nVideo tutorial: https://www.bilibili.com/video/BV1L24y1976U/\n\n\n``` \ngo get gitee.com/chunanyong/zorm \n```  \n\n* Based on native SQL statements, the learning cost is lower  \n* [Code generator](https://gitee.com/zhou-a-xing/zorm-generate-struct)\t\n* The code is concise, the main body is 3000 lines, zero dependency 5000 lines, detailed comments, easy to customize and modify\n* \u003cfont color=red\u003eSupport for transaction propagation, which was the main reason for the birth of ZORM\u003c/font\u003e\t\t\n* Support dm (dameng), kingbase (jincang), shentong (Shentong), gbase (Nantong), TDengine, mysql, postgresql, oracle, mssql, sqlite, db2, clickhouse...\n* Supports multi-database and read/write splitting  \n* Joint primary keys are not supported, workarounds are assumed to be no primary keys, and business control is implemented (difficult trade-offs)\n* Support seata, HPTX, dbpack distributed transactions, support global transaction hosting, do not modify business code, zero intrusion distributed transactions  \n* Support clickhouse, update, delete statements using SQL92 standard syntax\n\n## Transaction propagation  \nTransaction propagation is the core function of ZORM and the main reason why all methods of ZORM have ctx parameters.  \nZORM transaction operations need ```zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {})``` to be explicitly enabled, check transactions before executing closure functions, join transactions if there are transactions in ctx, and create new transactions if there are no transactions in ctx, so you only need to pass the same ctx object to achieve transaction propagation. In special scenarios, if you do not want transaction synchronization, you can declare a new ctx object to do transaction isolation. \n\n## Description of the source repository\nThe main libraries of the open source projects I led are all in GitHub, and there are project descriptions on GitHub to guide the jump to GitHub, which also causes the slow growth of the project, after all, there are more GitHub users.  \n**Open source has no borders, but developers have their own homeland.**  \nStrictly speaking, GitHub is governed by US law https://www.infoq.cn/article/SA72SsSeZBpUSH_ZH8XB\ndo my best to support the domestic open source community, don't like it, don't spray, thank you!\n\n## Support domestic database  \nZORM spares no effort in adapting to domestic databases, and if you encounter domestic databases that are not adapted or have problems, please feedback to the community and work together to build a domestic software ecosystem\n\n### Da Meng (DM)\n- Configure zorm.DataSourceConfig ```DriverName:dm ,Dialect:dm```\n- Damon Database Driver: gitee.com/chunanyong/dm\n- The TEXT type of Damon will be mapped to ```dm.DmClob```, string cannot be received, and zorm needs to be implemented ```ICustomDriverValueConver``` interface, custom extension processing\n```go\nimport (\n\t// 00. Introduce the database driver\n\t\"gitee.com/chunanyong/dm\"\n\t\"io\"\n)\n\n// CustomDMText implements ICustomDriverValueConver interface to extend custom types. For example, the TEXT type is mapped to dm.DmClob and cannot be directly received using string\ntype CustomDMText struct{}\n\n// GetDriverValue Returns an instance of driver.Value, the struct attribute type, based on the database column type\n// The structFieldType is passed nil because the map received or field does not exist\nfunc (dmtext CustomDMText) GetDriverValue(ctx context.Context, columnType *sql.ColumnType, structFieldType *reflect.Type) (driver.Value, error) {\n\t// If you want to use the structFieldType, you need to determine if it is nil\n\t// if structFieldType != nil {\n\t// }\n\n\treturn \u0026dm.DmClob{}, nil\n}\n\n// ConverDriverValue database column type, temporary received Value of driver. value returned by GetDriverValue,struct attribute type\n// The structFieldType is passed nil because the map received or field does not exist\n// Returns a pointer, pointer, pointer that matches the received type value!!!!\nfunc (dmtext CustomDMText) ConverDriverValue(ctx context.Context, columnType *sql.ColumnType, tempDriverValue driver.Value, structFieldType *reflect.Type) (interface{}, error) {\n\t// If you want to use the structFieldType, you need to determine if it is nil\n\t// if structFieldType != nil {\n\t// }\n\n\t// Type conversion\n\tdmClob, isok := tempDriverValue.(*dm.DmClob)\n\tif !isok {\n\t\treturn tempDriverValue, errors.New(\"-\u003eConverDriverValue--\u003e Failed to convert to *dm.DmClob\")\n\t}\n\tif dmClob == nil || !dmClob.Valid {\n\t\treturn new(string), nil\n\t}\n\t// Get the length\n\tdmlen, errLength := dmClob.GetLength()\n\tif errLength != nil {\n\t\treturn dmClob, errLength\n\t}\n\n\t// int64 is converted to an int\n\tstrInt64 := strconv.FormatInt(dmlen, 10)\n\tdmlenInt, errAtoi := strconv.Atoi(strInt64)\n\tif errAtoi != nil {\n\t\treturn dmClob, errAtoi\n\t}\n\n\t// Read the string\n\tstr, errReadString := dmClob.ReadString(1, dmlenInt)\n\n\t// Handle EOF errors caused by empty strings or NULL value\n\tif errReadString == io.EOF {\n\t\treturn new(string), nil\n\t}\n\n\treturn \u0026str, errReadString\n}\n// RegisterCustomDriverValueConver registered custom field processing logic, used to drive not directly convert scenarios, such as the TEXT of the dream cannot directly into a string\n// It's usually registered in the init method\nfunc init() {\n\t// dialectColumnType is a Dialect.FieldType, such as dm.TEXT\n\tzorm.RegisterCustomDriverValueConver(\"dm.TEXT\", CustomDMText{})\n}\n```\n### Kingbase\n- Configure zorm.DataSourceConfig ```DriverName:kingbase ,Dialect:kingbase```\n- Golden warehouse official drive: https://www.kingbase.com.cn/qd/index.htmhttps://bbs.kingbase.com.cn/thread-14457-1-1.html?_dsign=87f12756\n- The Kingbase 8 core is based on PostgreSQL 9.6 and can be tested using https://github.com/lib/pq, and the official driver is recommended for production environments\n- Note that ora_input_emptystr_isnull = false or ora_input_emptystr_isnull = on in the data/kingbase.conf of the database (according to the version), because golang does not have a null value, the general database is not null, golang's string defaults to '', if this is set to true, The database will set the value to null, which conflicts with the field property not null, so an error is reported.After the configuration file is modified, restart the database.\n\n### Shentong (shentong)\nIt is recommended to use official driver, configure zorm.DataSourceConfig ```DriverName:aci ,Dialect:shentong```  \n\n### Nantong (gbase)\n~~The official Go driver has not been found yet. Please configure it zorm.DataSourceConfig DriverName:gbase ,Dialect:gbase~~  \nUse odbc driver for the time being, ```DriverName:odbc ,Dialect:gbase```\n\n### TDengine\n- Since the TDengine driver does not support transactions, you need to set this setting ```DisableTransaction=true```\n- Configure zorm.DataSourceConfig ```DriverName:taosSql/taosRestful, Dialect:tdengine```\n- zorm.DataSourceConfig```TDengineInsertsColumnName```TDengine batch insert statement whether there is a column name. The default false has no column name, and the insertion value and database column order are consistent, reducing the length of the statement\n- Test case: https://zorm.cn/docs/zorm_tdengine_3.0_test  \n- TDengine is included: https://github.com/taosdata/awesome-tdengine/#orm\n\n## Database scripts and entity classes  \nGenerate entity classes or write them manually, we recommend using a code generator https://gitee.com/zhou-a-xing/zorm-generate-struct  \n\n```go \n\npackage testzorm\n\nimport (\n\t\"time\"\n\n\t\"gitee.com/chunanyong/zorm\"\n)\n\n// Build a list sentence\n\n/*\n\nDROP TABLE IF EXISTS `t_demo`;\nCREATE TABLE `t_demo` (\n`id` varchar(50) NOT NULL COMMENT 'primary key ',\n`userName` varchar(30) NOT NULL COMMENT 'name ',\n`password` varchar(50) NOT NULL COMMENT 'password ',\n`createTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n`active` int COMMENT 'Whether it is valid (0 no,1 yes)',\n PRIMARY KEY (`id`)\nENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = 'example';\n\n*/\n\n// demoStructTableName Table name constant for direct call\nconst demoStructTableName = \"t_demo\"\n\n// demoStruct example\ntype demoStruct struct {\n\t// Introduce the default struct to isolate method changes to the IEntityStruct\n\tzorm.EntityStruct\n\n\t// Id Primary key\n\tId string `column:\"id\"`\n\n\t// UserName Specifies the name\n\tUserName string `column:\"userName\"`\n\n\t// Password Password\n\tPassword string `column:\"password\"`\n\n\t// CreateTime \u003cno value\u003e\n\tCreateTime time.Time `column:\"createTime\"`\n\n\t// Active Whether it is valid (0 No,1 yes)\n\t// Active int `column:\"active\"`\n\n\t// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - end of the database field, custom fields to write in the following -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // \n\t// If the queried field is not found in the column tag, it is mapped to the struct property by name (case insensitive, support _ _ to _ hump)\n\n\t// Simulates the custom field Active\n\tActive int\n}\n\n// GetTableName Gets the table name\n// IEntityStruct interface method, entity class needs to implement!!\nfunc (entity *demoStruct) GetTableName() string {\n\treturn demoStructTableName\n}\n\n// GetPKColumnName Gets the name of the primary key field of the database table. Because to be compatible with Map, can only be the database field name\n// Joint primary key is not supported. It can be considered that there is no primary key and service control can be realized (hard choice).\n// If you do not have a primary key, you need to implement this method as well\n// IEntityStruct interface method, entity class needs to implement!!\nfunc (entity *demoStruct) GetPKColumnName() string {\n\t// If there is no primary key\n\t// return \"\"\n\treturn \"id\"\n}\n\n// GetDefaultValue To get the default value of the Map, Only used for Insert Struct, invalid for Update and UpdateNotZeroValue. The key that returns map is the Struct property name, value is the default value, and value can be nil.It cannot be the default value of the type, for example, the default value of the int type is set to 0\n\n//func (entity *EntityStruct) GetDefaultValue() map[string]interface{} {\n//\treturn map[string]interface{}{\"CreateTime\": time.Now(),\"Active\":nil}\n//}\n\n// newDemoStruct creates a default object\nfunc newDemoStruct() demoStruct {\n\tdemo := demoStruct{\n\t\t// if Id == \", \"save zorm will call zorm.FuncGenerateStringID(ctx), the default time stamp + random number, also can define your own implementations, such as zorm.FuncGenerateStringID = funcmyId\n\t\tId:\t\t zorm.FuncGenerateStringID(ctx),\n\t\tUserName:   \"defaultUserName\",\n\t\tPassword:   \"defaultPassword\",\n\t\tActive:\t 1,\n\t\tCreateTime: time.Now(),\n\t}\n\treturn demo\n}\n```\n\n## Test cases are documents\nhttps://gitee.com/wuxiangege/zorm-examples  \n```go  \n\n// testzorm uses native sql statements with no restrictions on sql syntax. The statement uses Finder as the carrier\n// Universal use of placeholders? zorm automatically replaces placeholders based on the database type, such as the postgresql database? Replace it with $1,$2...\n// zorm uses the ctx context.Context parameter to propagate the transaction. ctx is passed in from the web layer. For example, gin's c.Request.Context()\n// Transaction must be explicitly enabled using zorm.Transaction(ctx, func(ctx context.context) (interface{}, error) {})\npackage testzorm\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gitee.com/chunanyong/zorm\"\n\n\t// 00. Introduce the database driver\n\t_ \"github.com/go-sql-driver/mysql\"\n)\n\n// DBDAOs represent one database. If there are multiple databases, multiple DBDAOs are declared\nvar dbDao *zorm.DBDao\n\n// 01. Initialize the DBDao\nfunc init() {\n\n\t// Customize zorm log output\n\t// zorm.LogCallDepth = 4 // Level of log calls\n\t// zorm.FuncLogError = myFuncLogError // Function to log exceptions\n\t// zorm.FuncLogPanic = myFuncLogPanic // To log panic, the default is defaultLogError\n\t// zorm.FuncPrintSQL = myFuncPrintSQL // A function that prints sql\n\n\t// Reassign the FuncPrintSQL function to a custom log output format\n\t// log.SetFlags(log.LstdFlags)\n\t// zorm.FuncPrintSQL = zorm.FuncPrintSQL\n\n\t// Custom primary key generation\n\t// zorm.FuncGenerateStringID=funcmyId\n\n\t// Customize the Tag column name, many databases use GetContextDataSourceConfig to get config.Dialect for processing\n\t// zorm.FuncWrapFieldTagName=funcmyTagName\n\n\t// Custom decimal type implementation\n\t// zorm.FuncDecimalValue=funcmyDecimal\n\n\t// the Go database driver list: https://go.dev/wiki/SQLDrivers\n\n\t// dbDaoConfig Configure the database. This is just a simulation, the production should be reading the configuration configuration file and constructing the DataSourceConfig\n\tdbDaoConfig := zorm.DataSourceConfig{\n\t\t// DSN database connection string. parseTime=true is automatically converted to time format. The default query is the []byte array\n\t\tDSN: \"root:root@tcp(127.0.0.1:3306)/zorm?charset=utf8\u0026parseTime=true\u0026loc=Local\",\n\n\t\t// DriverName database driver name: mysql, postgres, oracle(go-ora), essentially, sqlite3, go_ibm_db, clickhouse, dm, kingbase, aci, taosSql | taosRestful Correspond to Dialect\n\t\t// sql.Open(DriverName,DSN) DriverName is the first string parameter of the sql.Open of the driver. The value can be obtained according to the actual conditions of the driver\n\t\tDriverName: \"mysql\",\n\n\t\t// the Dialect database Dialect: mysql, postgresql, oracle, MSSQL, sqlite, db2, clickhouse, dm, kingbase, shentong, tdengine and DriverName corresponding\n\t\tDialect: \"mysql\",\n\n\t\t// MaxOpenConns The default maximum number of database connections is 50\n\t\tMaxOpenConns: 50,\n\n\t\t// MaxIdleConns The default maximum number of idle connections is 50\n\t\tMaxIdleConns: 50,\n\n\t\t// ConnMaxLifetimeSecond Connection survival seconds. Default 600(10 minutes) after the connection is destroyed and rebuilt. Prevent the database from voluntarily disconnecting, resulting in dead connections. MySQL default wait_timeout 28800 seconds (8 hours)\n\t\tConnMaxLifetimeSecond: 600,\n\n\t\t// SlowSQLMillis slow sql time threshold, in milliseconds. A value less than 0 disables SQL statement output. If the value is equal to 0, only SQL statements are output and the execution time is not calculated. A value greater than 0 is used to calculate the SQL execution time and \u003e=SlowSQLMillis value\n\t\tSlowSQLMillis: 0,\n\n\t\t// DefaultTxOptions Default configuration of transaction isolation level, which defaults to nil\n\t\t// DefaultTxOptions: nil,\n\t\t// If distributed transactions are used, the default configuration is recommended\n\t\t// DefaultTxOptions: \u0026sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false},\n\n\t\t// FuncGlobalTransaction seata/hptx An adaptation function of a globally distributed transaction that returns the implementation of the IGlobalTransaction interface\n\t\t// business must call ctx, _ = zorm.BindContextEnableGlobalTransaction (ctx) on the global distribution of transactions\n\t\t// FuncGlobalTransaction : MyFuncGlobalTransaction,\n\n\t\t// SQLDB uses an existing database connection and has a higher priority than DSN\n\t\t// SQLDB : nil,\n\n\t\t// DisableTransaction disables transactions. The default value is false. If DisableTransaction=true is set, the Transaction method becomes invalid and no transaction is required. Some databases, such as TDengine, do not support transactions\n\t\t// Disable transactions should have the driver forgery transaction API, there should be no orm implementation,clickhouse's driver does just that\n\t\t// DisableTransaction :false,\n\n\t\t// TDengineInsertsColumnName Whether there are column names in the TDengine batch insert statement. The default false has no column name, and the insertion value and database column order are consistent, reducing the length of the statement\n\t\t// TDengineInsertsColumnName :false,\n\t}\n\n\t// Create dbDao based on dbDaoConfig. Perform this operation once for each database. The first database is defaultDao and the subsequent zorm.xxx method uses defaultDao by default\n\tdbDao, _ = zorm.NewDBDao(\u0026dbDaoConfig)\n}\n\n// TestInsert 02. Test save the Struct object\nfunc TestInsert(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\t// You need to start the transaction manually. If the error returned by the anonymous function is not nil, the transaction will be rolled back. If the DisableTransaction=true parameter is set, the Transaction method becomes invalid and no transaction is required\n\t// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level\n\t// such as ctx, _ := dbDao BindContextTxOptions (ctx, \u0026 SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of zorm.DataSourceConfig.DefaultTxOptions\n\t_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {\n\t\t// Create a demo object\n\t\tdemo := newDemoStruct()\n\n\t\t// Save the object. The parameter is a pointer to the object. If the primary key is increment, the value is assigned to the primary key property of the object\n\t\t_, err := zorm.Insert(ctx, \u0026demo)\n\n\t\t// If err is not returned nil, the transaction is rolled back\n\t\treturn nil, err\n\t})\n\t// Mark the test failed\n\tif err != nil {\n\t\tt.Errorf(\"Error:%v\", err)\n\t}\n}\n\n// TestInsertSlice 03. Tests batch save Struct object Slice\n// The primary key property in the Struct object cannot be assigned if the primary key is autoincrement\nfunc TestInsertSlice(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\t// You need to start the transaction manually. If the error returned by the anonymous function is not nil, the transaction will be rolled back. If the DisableTransaction=true parameter is set, the Transaction method becomes invalid and no transaction is required\n\t// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level\n\t// such as ctx, _ := dbDao BindContextTxOptions (ctx, \u0026 SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of zorm.DataSourceConfig.DefaultTxOptions\n\t_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {\n\n\t\t// slice stores the type zorm.IEntityStruct!!! Use the IEntityStruct interface, compatible with Struct entity classes\n\t\tdemoSlice := make([]zorm.IEntityStruct,0)\n\n\t\t// Create object 1\n\t\tdemo1 := newDemoStruct()\n\t\tdemo1.UserName = \"demo1\"\n\t\t// Create object 2\n\t\tdemo2 := newDemoStruct()\n\t\tdemo2.UserName = \"demo2\"\n\n\t\tdemoSlice = append(demoSlice, \u0026demo1, \u0026demo2)\n\n\t\t// Batch save objects. If the primary key is auto-increment, the auto-increment ID cannot be saved to the object.\n\t\t_, err := zorm.InsertSlice(ctx, demoSlice)\n\n\t\t// If err is not returned nil, the transaction is rolled back\n\t\treturn nil, err\n\t})\n\t// Mark the test failed\n\tif err != nil {\n\t\tt.Errorf(\"Error:%v\", err)\n\t}\n}\n\n// TestInsertEntityMap 04. Test to save an EntityMap object for scenarios where it is not convenient to use struct. Use Map as the carrier\nfunc TestInsertEntityMap(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\t// You need to start the transaction manually. If the error returned by the anonymous function is not nil, the transaction will be rolled back. If the DisableTransaction=true parameter is set, the Transaction method becomes invalid and no transaction is required\n\t// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level\n\t// such as ctx, _ := dbDao BindContextTxOptions (ctx, \u0026 SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of zorm.DataSourceConfig.DefaultTxOptions\n\t_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {\n\t\t// To create an EntityMap, pass in the table name\n\t\tentityMap := zorm.NewEntityMap(demoStructTableName)\n\t\t// Set the primary key name\n\t\tentityMap.PkColumnName = \"id\"\n\t\t// If it is an increment sequence, set the value of the sequence\n\t\t// entityMap.PkSequence = \"mySequence\"\n\n\t\t// Set Sets the field values of the database\n\t\t// If the primary key is an increment or sequence, do not set the value of the entityMap.Set primary key\n\t\tentityMap.Set(\"id\", zorm.FuncGenerateStringID(ctx))\n\t\tentityMap.Set(\"userName\", \"entityMap-userName\")\n\t\tentityMap.Set(\"password\", \"entityMap-password\")\n\t\tentityMap.Set(\"createTime\", time.Now())\n\t\tentityMap.Set(\"active\", 1)\n\n\t\t// Execute\n\t\t_, err := zorm.InsertEntityMap(ctx, entityMap)\n\n\t\t// If err is not returned nil, the transaction is rolled back\n\t\treturn nil, err\n\t})\n\t// Mark the test failed\n\tif err != nil {\n\t\tt.Errorf(\"Error:%v\", err)\n\t}\n}\n\n\n// TestInsertEntityMapSlice 05. Test batch save []IEntityMap for scenarios where it is not convenient to use struct, using Map as carrier\nfunc TestInsertEntityMapSlice(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\t_, err := Transaction(ctx, func(ctx context.Context) (interface{}, error) {\n\t\tentityMapSlice := make([]IEntityMap, 0)\n\t\tentityMap1 := NewEntityMap(demoStructTableName)\n\t\tentityMap1.PkColumnName = \"id\"\n\t\tentityMap1.Set(\"id\", zorm.FuncGenerateStringID(ctx))\n\t\tentityMap1.Set(\"userName\", \"entityMap-userName1\")\n\t\tentityMap1.Set(\"password\", \"entityMap-password1\")\n\t\tentityMap1.Set(\"createTime\", time.Now())\n\t\tentityMap1.Set(\"active\", 1)\n\n\t\tentityMap2 := NewEntityMap(demoStructTableName)\n\t\tentityMap2.PkColumnName = \"id\"\n\t\tentityMap2.Set(\"id\", zorm.FuncGenerateStringID(ctx))\n\t\tentityMap2.Set(\"userName\", \"entityMap-userName2\")\n\t\tentityMap2.Set(\"password\", \"entityMap-password2\")\n\t\tentityMap2.Set(\"createTime\", time.Now())\n\t\tentityMap2.Set(\"active\", 2)\n\n\t\tentityMapSlice = append(entityMapSlice, entityMap1 ,entityMap2)\n\n\t\t// Execute\n\t\t_, err := zorm.InsertEntityMapSlice(ctx, entityMapSlice)\n\n\t\t// If err is not returned nil, the transaction is rolled back\n\t\treturn nil, err\n\t})\n\t// Mark the test failed\n\tif err != nil {\n\t\tt.Errorf(\"Error:%v\", err)\n\t}\n}\n\n// TestQueryRow 06. Test query a struct object\nfunc TestQueryRow(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\t// Declares a pointer to an object that holds the returned data\n\tdemo := demoStruct{}\n\n\t// finder used to construct the query\n\t// finder := zorm.NewSelectFinder(demoStructTableName) // select * from t_demo\n\t// finder := zorm.NewSelectFinder(demoStructTableName, \"id,userName\") // select id,userName from t_demo\n\tfinder := zorm.NewFinder().Append(\"SELECT * FROM \" + demoStructTableName) // select * from t_demo\n\t// finder by default, sql injection checking is enabled to disallow concatenation of 'single quotes in statements. You can set finder.injectioncheck = false to undo the restriction\n\n\t// finder.Append The first argument is the statement and the following arguments are the corresponding values in the correct order. Uniform use of statements? zorm handles database differences\n\t// in (?) Arguments must have () parentheses, not in?\n\tfinder.Append(\"WHERE id=? and active in(?) \", \"20210630163227149563000042432429\", []int{0, 1})\n\n\t// How do I use like\n\t// finder.Append(\"WHERE id like ? \", \"20210630163227149563000042432429%\")\n\n\t// If the value of \"has\" is true, the database has data\n\thas, err := zorm.QueryRow(ctx, finder, \u0026demo)\n\n\tif err != nil { // Mark the test failed\n\t\tt.Errorf(\"Error:%v\", err)\n\t}\n\t// Print the result\n\tfmt.Println(demo)\n}\n\n// TestQueryRowMap 07. Test query map receives results. It is flexible for scenarios that are not suitable for structs\nfunc TestQueryRowMap(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\t// finder used to construct the query\n\t// finder := zorm.NewSelectFinder(demoStructTableName) // select * from t_demo\n\tfinder := zorm.NewFinder().Append(\"SELECT * FROM \" + demoStructTableName) // select * from t_demo\n\t// finder.Append The first argument is the statement and the following arguments are the corresponding values in the correct order. Uniform use of statements? zorm handles database differences\n\t// in (?) Arguments must have () parentheses, not in?\n\tfinder.Append(\"WHERE id=? and active in(?) \", \"20210630163227149563000042432429\", []int{0, 1})\n\t// Run the query\n\tresultMap, err := zorm.QueryRowMap(ctx, finder)\n\n\tif err != nil { // Mark the test failed\n\t\tt.Errorf(\"Error:%v\", err)\n\t}\n\t// Print the result\n\tfmt.Println(resultMap)\n}\n\n// TestQuery 08. Test the list of query objects\nfunc TestQuery(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\t// Create a slice for receiving results\n\tlist := make([]demoStruct, 0)\n\n\t// finder used to construct the query\n\t// finder := zorm.NewSelectFinder(demoStructTableName) // select * from t_demo\n\tfinder := zorm.NewFinder().Append(\"SELECT id FROM \" + demoStructTableName) // select * from t_demo\n\t// Create a paging object. After the query is complete, the page object can be directly used by the front-end paging component\n\tpage := zorm.NewPage()\n\tpage.PageNo = 1   // Query page 1. The default value is 1\n\tpage.PageSize = 20 // 20 per page. The default is 20\n\n\t// The total number of entries is not queried\n\t// finder.SelectTotalCount = false\n\n\t// You can manually specify paging statements if they are particularly complex statements that cause count statement construction to fail\n\t// countFinder := zorm.NewFinder().Append(\"select count(*) from (\")\n\t// countFinder.AppendFinder(finder)\n\t// countFinder.Append(\") tempcountfinder\")\n\t// finder.CountFinder = countFinder\n\n\t// Run the query\n\terr := zorm.Query(ctx, finder, \u0026list, page)\n\tif err != nil { // Mark the test failed\n\t\tt.Errorf(\"Error:%v\", err)\n\t}\n\t// Print the result\n\tfmt.Println(\"Total number of items :\", page.TotalCount, \"List :\", list)\n}\n\n// TestQueryMap 09. Test query map list. Used in the scenario where struct is not convenient\nfunc TestQueryMap(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\t// finder used to construct the query\n\t// finder := zorm.NewSelectFinder(demoStructTableName) // select * from t_demo\n\tfinder := zorm.NewFinder().Append(\"SELECT * FROM \" + demoStructTableName) // select * from t_demo\n\t// Create a paging object. After the query is complete, the page object can be directly used by the front-end paging component\n\tpage := zorm.NewPage()\n\tpage.PageNo = 1   // Query page 1. The default value is 1\n\tpage.PageSize = 20 // 20 per page. The default is 20\n\n\t// The total number of entries is not queried\n\t// finder.SelectTotalCount = false\n\t\n\t// You can manually specify paging statements if they are particularly complex statements that cause count statement construction to fail\n\t// countFinder := zorm.NewFinder().Append(\"select count(*) from (\")\n\t// countFinder.AppendFinder(finder)\n\t// countFinder.Append(\") tempcountfinder\")\n\t// finder.CountFinder = countFinder\n\n\t// Run the query\n\tlistMap, err := zorm.QueryMap(ctx, finder, page)\n\tif err != nil { // Mark the test failed\n\t\tt.Errorf(\"Error:%v\", err)\n\t}\n\t// Print the result\n\tfmt.Println(\"Total number of items :\", page.TotalCount, \"List :\", listMap)\n}\n\n// TestUpdateNotZeroValue 10. Update the struct object with only the non-zero fields. The primary key must have a value\nfunc TestUpdateNotZeroValue(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\t// You need to start the transaction manually. If the error returned by the anonymous function is not nil, the transaction will be rolled back. If the DisableTransaction=true parameter is set, the Transaction method becomes invalid and no transaction is required\n\t// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level\n\t// such as ctx, _ := dbDao BindContextTxOptions (ctx, \u0026 SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of zorm.DataSourceConfig.DefaultTxOptions\n\t_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {\n\t\t// Declares a pointer to an object used to update data\n\t\tdemo := demoStruct{}\n\t\tdemo.Id = \"20210630163227149563000042432429\"\n\t\tdemo.UserName = \"UpdateNotZeroValue\"\n\n\t\t// UPDATE \"sql\":\"UPDATE t_demo SET userName=? WHERE id=?\" ,\"args\":[\"UpdateNotZeroValue\",\"20210630163227149563000042432429\"]\n\t\t_, err := zorm.UpdateNotZeroValue(ctx, \u0026demo)\n\n\t\t// If err is not returned nil, the transaction is rolled back\n\t\treturn nil, err\n\t})\n\tif err != nil { // Mark the test failed\n\t\tt.Errorf(\"Error:%v\", err)\n\t}\n\n}\n\n// TestUpdate 11. Update the struct object, updating all fields. The primary key must have a value\nfunc TestUpdate(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\t// You need to start the transaction manually. If the error returned by the anonymous function is not nil, the transaction will be rolled back. If the DisableTransaction=true parameter is set, the Transaction method becomes invalid and no transaction is required\n\t// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level\n\t// such as ctx, _ := dbDao BindContextTxOptions (ctx, \u0026 SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of zorm.DataSourceConfig.DefaultTxOptions\n\t_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {\n\n\t\t// Declares a pointer to an object used to update data\n\t\tdemo := demoStruct{}\n\t\tdemo.Id = \"20210630163227149563000042432429\"\n\t\tdemo.UserName = \"TestUpdate\"\n\n\t\t_, err := zorm.Update(ctx, \u0026demo)\n\n\t\t// If err is not returned nil, the transaction is rolled back\n\t\treturn nil, err\n\t})\n\tif err != nil { // Mark the test failed\n\t\tt.Errorf(\"Error:%v\", err)\n\t}\n}\n\n// TestUpdateFinder 12. With finder update,zorm's most flexible way of writing any update statement, even manually writing insert statements\nfunc TestUpdateFinder(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\t// You need to start the transaction manually. If the error returned by the anonymous function is not nil, the transaction will be rolled back. If the DisableTransaction=true parameter is set, the Transaction method becomes invalid and no transaction is required\n\t// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level\n\t// such as ctx, _ := dbDao BindContextTxOptions (ctx, \u0026 SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of zorm.DataSourceConfig.DefaultTxOptions\n\t_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {\n\t\t// finder := zorm.NewUpdateFinder(demoStructTableName) // UPDATE t_demo SET\n\t\t// finder := zorm.NewDeleteFinder(demoStructTableName) // DELETE FROM t_demo\n\t\tfinder := zorm.NewFinder().Append(\"UPDATE\").Append(demoStructTableName).Append(\"SET\") // UPDATE t_demo SET\n\t\tfinder.Append(\"userName=? ,active=?\", \"TestUpdateFinder\", 1).Append(\"WHERE id=?\", \"20210630163227149563000042432429\")\n\n\t\t// UPDATE \"sql\":\"UPDATE t_demo SET userName=? ,active=? WHERE id=?\" ,\"args\":[\"TestUpdateFinder\",1,\"20210630163227149563000042432429\"]\n\t\t_, err := zorm.UpdateFinder(ctx, finder)\n\n\t\t// If err is not returned nil, the transaction is rolled back\n\t\treturn nil, err\n\t})\n\tif err != nil { // Mark the test failed\n\t\tt.Errorf(\"Error:%v\", err)\n\t}\n\n}\n\n// TestUpdateEntityMap 13. Update an EntityMap. The primary key must have a value\nfunc TestUpdateEntityMap(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\t// You need to start the transaction manually. If the error returned by the anonymous function is not nil, the transaction will be rolled back. If the DisableTransaction=true parameter is set, the Transaction method becomes invalid and no transaction is required\n\t// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level\n\t// such as ctx, _ := dbDao BindContextTxOptions (ctx, \u0026 SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of zorm.DataSourceConfig.DefaultTxOptions\n\t_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {\n\t\t// To create an EntityMap, pass in the table name\n\t\tentityMap := zorm.NewEntityMap(demoStructTableName)\n\t\t// Set the primary key name\n\t\tentityMap.PkColumnName = \"id\"\n\t\t// Set Sets the field value of the database. The primary key must have a value\n\t\tentityMap.Set(\"id\", \"20210630163227149563000042432429\")\n\t\tentityMap.Set(\"userName\", \"TestUpdateEntityMap\")\n\t\t// UPDATE \"sql\":\"UPDATE t_demo SET userName=? WHERE id=?\" ,\"args\":[\"TestUpdateEntityMap\",\"20210630163227149563000042432429\"]\n\t\t_, err := zorm.UpdateEntityMap(ctx, entityMap)\n\n\t\t// If err is not returned nil, the transaction is rolled back\n\t\treturn nil, err\n\t})\n\tif err != nil { // Mark the test failed\n\t\tt.Errorf(\"Error:%v\", err)\n\t}\n\n}\n\n// TestDelete 14. Delete a struct object. The primary key must have a value\nfunc TestDelete(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\t// You need to start the transaction manually. If the error returned by the anonymous function is not nil, the transaction will be rolled back. If the DisableTransaction=true parameter is set, the Transaction method becomes invalid and no transaction is required\n\t// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level\n\t// such as ctx, _ := dbDao BindContextTxOptions (ctx, \u0026 SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of zorm.DataSourceConfig.DefaultTxOptions\n\t_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {\n\t\tdemo := demoStruct{}\n\t\tdemo.Id = \"20210630163227149563000042432429\"\n\n\t\t// \"sql\":\"DELETE FROM t_demo WHERE id=?\" ,\"args\":[\"20210630163227149563000042432429\"]\n\t\t_, err := zorm.Delete(ctx, \u0026demo)\n\n\t\t// If err is not returned nil, the transaction is rolled back\n\t\treturn nil, err\n\t})\n\tif err != nil { // Mark the test failed\n\t\tt.Errorf(\"Error:%v\", err)\n\t}\n\n}\n\n// TestProc 15. Test calls the stored procedure\nfunc TestProc(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\tdemo := demoStruct{}\n\tfinder := zorm.NewFinder().Append(\"call testproc(?)\", \"u_10001\")\n\tzorm.QueryRow(ctx, finder, \u0026demo)\n\tfmt.Println(demo)\n}\n\n// TestFunc 16. Test calls custom functions\nfunc TestFunc(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\tuserName := \"\"\n\tfinder := zorm.NewFinder().Append(\"select testfunc(?)\", \"u_10001\")\n\tzorm.QueryRow(ctx, finder, \u0026userName)\n\tfmt.Println(userName)\n}\n\n// TestOther 17. Some other instructions. Thank you very much for seeing this line\nfunc TestOther(t *testing.T) {\n\t// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()\n\tvar ctx = context.Background()\n\n\t// Scenario 1. Multiple databases. The dbDao of the corresponding database calls BindContextDBConnection, binds the database connection to the returned ctx, and passes ctx to zorm's function\n\t// You can also rewrite the FuncReadWriteStrategy function to return the DBDao of the specified database by setting a different key via ctx\n\tnewCtx, err := dbDao.BindContextDBConnection(ctx)\n\tif err != nil { // Mark the test failed\n\t\tt.Errorf(\"Error:%v\", err)\n\t}\n\n\tfinder := zorm.NewFinder().Append(\"SELECT * FROM \" + demoStructTableName) // select * from t_demo\n\t// Pass the new newCtx to zorm's function\n\tlist, _ := zorm.QueryMap(newCtx, finder, nil)\n\tfmt.Println(list)\n\n\t// Scenario 2. Read/write separation of a single database. Set the read-write separation policy function.\n\tzorm.FuncReadWriteStrategy = myReadWriteStrategy\n\n\t// Scenario 3. If multiple databases exist and read and write data are separated from each other, perform this operation according to Scenario 1.\n\t// You can also rewrite the FuncReadWriteStrategy function to return the DBDao of the specified database by setting a different key via ctx\n\n}\n\n// myReadWriteStrategy Database read-write strategy rwType=0 read,rwType=1 write\n// You can also set different keys through ctx to return the DBDao of the specified database\nfunc myReadWriteStrategy(ctx context.Context, rwType int) (*zorm.DBDao, error) {\n\t// Return the required read/write dao based on your business scenario. This function is called every time a database connection is needed\n\t// if rwType == 0 {\n\t// return dbReadDao\n\t// }\n\t// return dbWriteDao\n\n\treturn dbDao, nil\n}\n\n// --------------------------------------------\n// ICustomDriverValueConver interface, see examples of DaMeng\n\n// --------------------------------------------\n// OverrideFunc Rewrite the functions of ZORM, when you use this function, you have to know what you are doing\n\n// oldInsertFunc The default Insert implementation\nvar oldInsertFunc func(ctx context.Context, entity zorm.IEntityStruct) (int, error)\n\n// newInsertFunc New Insert implementation\nvar newInsertFunc = func(ctx context.Context, entity zorm.IEntityStruct) (int, error) {\n\tfmt.Println(\"Insert before\")\n\ti, err := oldInsertFunc(ctx, entity)\n\tfmt.Println(\"Insert after\")\n\treturn i, err\n}\n\n// Register the old function in the init function that overrides the old one\nfunc init() {\n\tok, oldFunc, err := zorm.OverrideFunc(\"Insert\", newInsertFunc)\n\tif ok \u0026\u0026 err == nil {\n\t\toldInsertFunc = oldFunc.(func(ctx context.Context, entity zorm.IEntityStruct) (int, error))\n\t}\n}\n\n```  \n## Global transaction\n### seata-go CallbackWithCtx function mode\n```go\n// DataSourceConfig configures DefaultTxOptions\n// DefaultTxOptions: \u0026sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false},\n\n// Import the seata-go dependency package\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/seata/seata-go/pkg/client\"\n\t\"github.com/seata/seata-go/pkg/tm\"\n\tseataSQL \"github.com/seata/seata-go/pkg/datasource/sql\" //Note: zorm's DriverName: seataSQL.SeataATMySQLDriver, !!!!\n)\n\n// Path of the configuration file\nvar configPath = \"./conf/client.yml\"\n\nfunc main() {\n\n\t// Initialize the configuration\n\tconf := config.InitConf(configPath)\n\t// Initialize the zorm database\n\t// note: zorm DriverName: seataSQL SeataATMySQLDriver,!!!!!!!!!!\n\tinitZorm()\n\n\t// Start distributed transactions\n\ttm.WithGlobalTx(context.Background(), \u0026tm.GtxConfig{\n\t\tName:\t\"ATSampleLocalGlobalTx\",\n\t\tTimeout: time.Second * 30,\n\t}, CallbackWithCtx)\n\t// CallbackWithCtx business callback definition\n\t// type CallbackWithCtx func(ctx context.Context) error\n\n\n\t// Get the XID after the transaction is started. This can be passed through gin's header, or otherwise\n\t// xid:=tm.GetXID(ctx)\n\t// tm.SetXID(ctx, xid)\n\n\t// If the gin framework is used, middleware binding parameters can be used\n\t// r.Use(ginmiddleware.TransactionMiddleware())\n}\n\n```\n\n### seata-go transaction hosting mode\n\n```go\n// Do not use CallbackWithCtx function,zorm to achieve transaction management, no modification of business code, zero intrusion to achieve distributed transactions\n\n\n// The distributed transaction must be started manually and must be invoked before the local transaction is started\nctx,_ = zorm.BindContextEnableGlobalTransaction(ctx)\n// Distributed transaction sample code\n_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {\n\n\t// Get the XID of the current distributed transaction. Don't worry about how, if it is a distributed transaction environment, the value will be set automatically\n\t// xid := ctx.Value(\"XID\").(string)\n\n\t// Pass the xid to the third party application\n\t// req.Header.Set(\"XID\", xid)\n\n\t// If err is not returned nil, local and distributed transactions are rolled back\n\treturn nil, err\n})\n\n// /---------- Third-party application -------/ //\n\n\t// Do not use the middleware provided by seata-go by default, just ctx binding XID!!!\n\t//// r.Use(ginmiddleware.TransactionMiddleware())\n\txid := c.GetHeader(constant.XidKey)\n\tctx = context.WithValue(ctx, \"XID\", xid)\n\n\t// The distributed transaction must be started manually and must be invoked before the local transaction is started\n\tctx,_ = zorm.BindContextEnableGlobalTransaction(ctx)\n\t// ctx invokes the business transaction after binding the XID\n\t_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {\n\n\t// Business code......\n\n\t// If err is not returned nil, local and distributed transactions are rolled back\n\treturn nil, err\n})\n\n// It is recommended that the following code be placed in a separate file\n// ... // \n\n// ZormGlobalTransaction packaging seata *tm.GlobalTransactionManager, zorm.IGlobalTransaction interface\ntype ZormGlobalTransaction struct {\n\t*tm.GlobalTransactionManager\n}\n\n// MyFuncGlobalTransaction zorm A function that ADAPTS a seata globally distributed transaction\n// important!!!! Need to configure the zorm.DataSourceConfig.FuncGlobalTransaction = MyFuncGlobalTransaction important!!!!!!\nfunc MyFuncGlobalTransaction(ctx context.Context) (zorm.IGlobalTransaction, context.Context, context.Context, error) {\n\t// Create a seata-go transaction\n\tglobalTx := tm.GetGlobalTransactionManager()\n\t// Use the zorm.IGlobalTransaction interface object to wrap distributed transactions and isolate the seata-go dependencies\n\tglobalTransaction := \u0026ZormGlobalTransaction{globalTx}\n\n\tif tm.IsSeataContext(ctx) {\n\t\treturn globalTransaction, ctx, ctx, nil\n\t}\n\t// open global transaction for the first time\n\tctx = tm.InitSeataContext(ctx)\n\t// There is a request to come in, manually get the XID\n\txidObj := ctx.Value(\"XID\")\n\tif xidObj ! = nil {\n\t\txid := xidObj.(string)\n\t\ttm.SetXID(ctx, xid)\n\t}\n\ttm.SetTxName(ctx, \"ATSampleLocalGlobalTx\")\n\n\t// use new context to process current global transaction.\n\tif tm.IsGlobalTx(ctx) {\n\t\tglobalRootContext := transferTx(ctx)\n\t\treturn globalTransaction, ctx, globalRootContext, nil\n\t}\n\treturn globalTransaction, ctx, ctx, nil\n}\n\n// IGlobalTransaction managed global distributed transaction interface (zorm.IGlobalTransaction). seata and hptx currently implement the same code, only the reference implementation package is different\n\n// BeginGTX Starts global distributed transactions\nfunc (gtx *ZormGlobalTransaction) BeginGTX(ctx context.Context, globalRootContext context.Context) error {\n\t//tm.SetTxStatus(globalRootContext, message.GlobalStatusBegin)\n\terr := gtx.Begin(globalRootContext, time.Second*30)\n\treturn err\n}\n\n// CommitGTX Commit global distributed transactions\nfunc (gtx *ZormGlobalTransaction) CommitGTX(ctx context.Context, globalRootContext context.Context) error {\n\tgtr := tm.GetTx(globalRootContext)\n\treturn gtx.Commit(globalRootContext, gtr)\n}\n\n// RollbackGTX rolls back globally distributed transactions\nfunc (gtx *ZormGlobalTransaction) RollbackGTX(ctx context.Context, globalRootContext context.Context) error {\n\tgtr := tm.GetTx(globalRootContext)\n\t// If it is the Participant role, change it to the Launcher role to allow branch transactions to submit global transactions.\n\tif gtr.TxRole != tm.Launcher {\n\t\tgtr.TxRole = tm.Launcher\n\t}\n\treturn gtx.Rollback(globalRootContext, gtr)\n}\n// GetGTXID Gets the XID of the globally distributed transaction\nfunc (gtx *ZormGlobalTransaction) GetGTXID(ctx context.Context, globalRootContext context.Context) (string.error) {\n\treturn tm.GetXID(globalRootContext), nil\n}\n\n// transferTx transfer the gtx into a new ctx from old ctx.\n// use it to implement suspend and resume instead of seata java\nfunc transferTx(ctx context.Context) context.Context {\n\tnewCtx := tm.InitSeataContext(context.Background())\n\ttm.SetXID(newCtx, tm.GetXID(ctx))\n\treturn newCtx\n}\n\n// ... // \n```\n\n\n### hptx proxy mode\n[in hptx proxy mode for zorm use example](https://github.com/CECTC/hptx-samples/tree/main/http_proxy_zorm)   \n```go\n// DataSourceConfig configures DefaultTxOptions\n// DefaultTxOptions: \u0026sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false},\n\n// Introduce the hptx dependency package\nimport (\n\t\"github.com/cectc/hptx\"\n\t\"github.com/cectc/hptx/pkg/config\"\n\t\"github.com/cectc/hptx/pkg/resource\"\n\t\"github.com/cectc/mysql\"\n\t\"github.com/cectc/hptx/pkg/tm\"\n\n\tgtxContext \"github.com/cectc/hptx/pkg/base/context\"\n)\n\n// Path of the configuration file\nvar configPath = \"./conf/config.yml\"\n\nfunc main() {\n\n\t// Initialize the configuration\n\thptx.InitFromFile(configPath)\n\t\n\t// Register the mysql driver\n\tmysql.RegisterResource(config.GetATConfig().DSN)\n\tresource.InitATBranchResource(mysql.GetDataSourceManager())\n\t// sqlDB, err := sql.Open(\"mysql\", config.GetATConfig().DSN)\n\n\n\t// After the normal initialization of zorm, be sure to put it after the hptx mysql initialization!!\n\n\t// ... // \n\t// tm register transaction service, refer to the official example (transaction hosting is mainly to remove proxy, zero intrusion on the business)\n\ttm.Implement(svc.ProxySvc)\n\t// ... // \n\n\n\t// Get the hptx rootContext\n\t// rootContext := gtxContext.NewRootContext(ctx)\n\t// rootContext := ctx.(*gtxContext.RootContext)\n\n\t// Create an hptx transaction\n\t// globalTx := tm.GetCurrentOrCreate(rootContext)\n\n\t// Start the transaction\n\t// globalTx. BeginWithTimeoutAndName (int32 (6000), \"name of the transaction,\" rootContext)\n\n\t// Get the XID after the transaction is started. This can be passed through the gin header, or otherwise\n\t// xid:=rootContext.GetXID()\n\n\t// If using gin frame, get ctx\n\t// ctx := c.Request.Context()\n\n\t// Accept the XID passed and bind it to the local ctx\n\t// ctx =context.WithValue(ctx,mysql.XID,xid)\n}\n```\n\n### hptx transaction hosting mode\n[zorm transaction hosting hptx example](https://github.com/CECTC/hptx-samples/tree/main/http_zorm)\n```go\n// Do not use proxy proxy mode,zorm to achieve transaction management, no modification of business code, zero intrusion to achieve distributed transactions\n// tm.Implement(svc.ProxySvc)\n\n// The distributed transaction must be started manually and must be invoked before the local transaction is started\nctx,_ = zorm.BindContextEnableGlobalTransaction(ctx)\n// Distributed transaction sample code\n_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {\n\n\t// Get the XID of the current distributed transaction. Don't worry about how, if it is a distributed transaction environment, the value will be set automatically\n\t// xid := ctx.Value(\"XID\").(string)\n\n\t// Pass the xid to the third party application\n\t// req.Header.Set(\"XID\", xid)\n\n\t// If err is not returned nil, local and distributed transactions are rolled back\n\treturn nil, err\n})\n\n// /---------- Third-party application -------// /\n\n// Before third-party applications can start transactions,ctx needs to bind Xids, such as gin framework\n\n// Accept the XID passed and bind it to the local ctx\n// xid:=c.Request.Header.Get(\"XID\")\n// ctx is obtained\n// ctx := c.Request.Context()\n// ctx = context.WithValue(ctx,\"XID\",xid)\n\n// The distributed transaction must be started manually and must be invoked before the local transaction is started\nctx,_ = zorm.BindContextEnableGlobalTransaction(ctx)\n// ctx invokes the business transaction after binding the XID\n_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {\n\n\t// Business code......\n\n\t// If err is not returned nil, local and distributed transactions are rolled back\n\treturn nil, err\n})\n\n\n\n// It is recommended that the following code be placed in a separate file\n// ... // \n\n// ZormGlobalTransaction packaging hptx *tm.DefaultGlobalTransaction, zorm.IGlobalTransaction interface\ntype ZormGlobalTransaction struct {\n\t*tm.DefaultGlobalTransaction\n}\n\n// MyFuncGlobalTransaction zorm A function that ADAPTS a hptx globally distributed transaction\n// important!!!! Need to configure the zorm.DataSourceConfig.FuncGlobalTransaction = MyFuncGlobalTransaction important!!!!!!\nfunc MyFuncGlobalTransaction(ctx context.Context) (zorm.IGlobalTransaction, context.Context, context.Context, error) {\n\t// Obtain the hptx rootContext\n\trootContext := gtxContext.NewRootContext(ctx)\n\t// Create a hptx transaction\n\tglobalTx := tm.GetCurrentOrCreate(rootContext)\n\t// Use the zorm.IGlobalTransaction interface object to wrap distributed transactions and isolate hptx dependencies\n\tglobalTransaction := \u0026ZormGlobalTransaction{globalTx}\n\n\treturn globalTransaction, ctx, rootContext, nil\n}\n\n// IGlobalTransaction managed global distributed transaction interface (zorm.IGlobalTransaction). seata and hptx currently implement the same code, only the reference implementation package is different\n\n// BeginGTX Starts global distributed transactions\nfunc (gtx *ZormGlobalTransaction) BeginGTX(ctx context.Context, globalRootContext context.Context) error {\n\trootContext := globalRootContext.(*gtxContext.RootContext)\n\treturn gtx.BeginWithTimeout(int32(6000), rootContext)\n}\n\n// CommitGTX Commit global distributed transactions\nfunc (gtx *ZormGlobalTransaction) CommitGTX(ctx context.Context, globalRootContext context.Context) error {\n\trootContext := globalRootContext.(*gtxContext.RootContext)\n\treturn gtx.Commit(rootContext)\n}\n\n// RollbackGTX rolls back globally distributed transactions\nfunc (gtx *ZormGlobalTransaction) RollbackGTX(ctx context.Context, globalRootContext context.Context) error {\n\trootContext := globalRootContext.(*gtxContext.RootContext)\n\t// If it is the Participant role, change it to the Launcher role to allow branch transactions to submit global transactions.\n\tif gtx.Role != tm.Launcher {\n\t\tgtx.Role = tm.Launcher\n\t}\n\treturn gtx.Rollback(rootContext)\n}\n// GetGTXID Gets the XID of the globally distributed transaction\nfunc (gtx *ZormGlobalTransaction) GetGTXID(ctx context.Context, globalRootContext context.Context) (string.error) {\n\trootContext := globalRootContext.(*gtxContext.RootContext)\n\treturn rootContext.GetXID(), nil\n}\n\n// ... // \n```\n###  dbpack distributed transactions \n```dbpack``` document: https://cectc.github.io/dbpack-doc/#/README deployment with a Mesh, the application integration is simple, just need to get xid, in a hint of SQL statements\n```go\n// Before starting dbpack transactions,ctx needs to bind sql hints, such as using the gin framework to obtain the xid passed by the header\nxid := c.Request.Header.Get(\"xid\")\n// Generate sql hint content using xid, and then bind the hint to ctx\nhint := fmt.Sprintf(\"/*+ XID('%s') */\", xid)\n// ctx is obtained\nctx := c.Request.Context()\n// Bind the hint to ctx\nctx,_ = zorm.BindContextSQLHint(ctx, hint)\n\n// After ctx binds the sql hint, the business transaction is invoked and ctx is transmitted to realize the propagation of the distributed transaction\n_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {\n\n\t// Business code......\n\n\t// If err is not returned nil, local and distributed transactions are rolled back\n\treturn nil, err\n})\n```\n","funding_links":[],"categories":["Language bindings","Go"],"sub_categories":["Golang"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspringrain%2Fzorm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspringrain%2Fzorm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspringrain%2Fzorm/lists"}