zorm

package module
v1.7.6 Latest Latest
Warning

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

Go to latest
Published: Apr 18, 2024 License: Apache-2.0 Imports: 17 Imported by: 33

README

Introduction

zorm logo
This is a lightweight ORM,zero dependency, that supports DM,Kingbase,shentong,TDengine,mysql,postgresql,oracle,mssql,sqlite,db2,clickhouse...

Official website:https://zorm.cn
source code address:https://gitee.com/chunanyong/zorm
test case: https://gitee.com/wuxiangege/zorm-examples/
Video tutorial: https://www.bilibili.com/video/BV1L24y1976U/

go get gitee.com/chunanyong/zorm 
  • Based on native SQL statements, the learning cost is lower
  • Code generator
  • The code is concise, the main body is 3000 lines, zero dependency 5000 lines, detailed comments, easy to customize and modify
  • Support for transaction propagation, which was the main reason for the birth of ZORM
  • Support dm (dameng), kingbase (jincang), shentong (Shentong), gbase (Nantong), TDengine, mysql, postgresql, oracle, mssql, sqlite, db2, clickhouse...
  • Supports multi-database and read/write splitting
  • Joint primary keys are not supported, workarounds are assumed to be no primary keys, and business control is implemented (difficult trade-offs)
  • Support seata, HPTX, dbpack distributed transactions, support global transaction hosting, do not modify business code, zero intrusion distributed transactions
  • Support clickhouse, update, delete statements using SQL92 standard syntax

Transaction propagation

Transaction propagation is the core function of ZORM and the main reason why all methods of ZORM have ctx parameters.
ZORM 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.

Description of the source repository

The 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.
Open source has no borders, but developers have their own homeland.
Strictly speaking, GitHub is governed by US law https://www.infoq.cn/article/SA72SsSeZBpUSH_ZH8XB do my best to support the domestic open source community, don't like it, don't spray, thank you!

Support domestic database

ZORM 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

Da Meng (DM)

  • Configure zorm.DataSourceConfig DriverName:dm ,Dialect:dm
  • Damon Database Driver: gitee.com/chunanyong/dm
  • 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
import (
	// 00. Introduce the database driver
	"gitee.com/chunanyong/dm"
	"io"
)

// 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
type CustomDMText struct{}

// GetDriverValue Returns an instance of driver.Value, the struct attribute type, based on the database column type
// The structFieldType is passed nil because the map received or field does not exist
func (dmtext CustomDMText) GetDriverValue(ctx context.Context, columnType *sql.ColumnType, structFieldType *reflect.Type) (driver.Value, error) {
	// If you want to use the structFieldType, you need to determine if it is nil
	// if structFieldType != nil {
	// }

	return &dm.DmClob{}, nil
}

// ConverDriverValue database column type, temporary received Value of driver. value returned by GetDriverValue,struct attribute type
// The structFieldType is passed nil because the map received or field does not exist
// Returns a pointer, pointer, pointer that matches the received type value!!!!
func (dmtext CustomDMText) ConverDriverValue(ctx context.Context, columnType *sql.ColumnType, tempDriverValue driver.Value, structFieldType *reflect.Type) (interface{}, error) {
	// If you want to use the structFieldType, you need to determine if it is nil
	// if structFieldType != nil {
	// }

	// Type conversion
	dmClob, isok := tempDriverValue.(*dm.DmClob)
	if !isok {
		return tempDriverValue, errors.New("->ConverDriverValue--> Failed to convert to *dm.DmClob")
	}
	if dmClob == nil || !dmClob.Valid {
		return new(string), nil
	}
	// Get the length
	dmlen, errLength := dmClob.GetLength()
	if errLength != nil {
		return dmClob, errLength
	}

	// int64 is converted to an int
	strInt64 := strconv.FormatInt(dmlen, 10)
	dmlenInt, errAtoi := strconv.Atoi(strInt64)
	if errAtoi != nil {
		return dmClob, errAtoi
	}

	// Read the string
	str, errReadString := dmClob.ReadString(1, dmlenInt)

	// Handle EOF errors caused by empty strings or NULL value
	if errReadString == io.EOF {
		return new(string), nil
	}

	return &str, errReadString
}
// 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
// It's usually registered in the init method
func init() {
	// dialectColumnType is a Dialect.FieldType, such as dm.TEXT
	zorm.RegisterCustomDriverValueConver("dm.TEXT", CustomDMText{})
}

Kingbase

  • Configure zorm.DataSourceConfig DriverName:kingbase ,Dialect:kingbase
  • Golden warehouse official drive: https://www.kingbase.com.cn/qd/index.htmhttps://bbs.kingbase.com.cn/thread-14457-1-1.html?_dsign=87f12756
  • 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
  • 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.

Shentong (shentong)

It is recommended to use official driver, configure zorm.DataSourceConfig DriverName:aci ,Dialect:shentong

Nantong (gbase)

The official Go driver has not been found yet. Please configure it zorm.DataSourceConfig DriverName:gbase ,Dialect:gbase
Use odbc driver for the time being, DriverName:odbc ,Dialect:gbase

TDengine

  • Since the TDengine driver does not support transactions, you need to set this setting DisableTransaction=true
  • Configure zorm.DataSourceConfig DriverName:taosSql/taosRestful, Dialect:tdengine
  • zorm.DataSourceConfigTDengineInsertsColumnNameTDengine 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
  • Test case: https://zorm.cn/post/zorm_tdengine_3.0_test
  • TDengine is included: https://github.com/taosdata/awesome-tdengine/#orm

Database scripts and entity classes

Generate entity classes or write them manually, we recommend using a code generator https://gitee.com/zhou-a-xing/zorm-generate-struct


package testzorm

import (
	"time"

	"gitee.com/chunanyong/zorm"
)

// Build a list sentence

/*

DROP TABLE IF EXISTS `t_demo`;
CREATE TABLE `t_demo` (
`id` varchar(50) NOT NULL COMMENT 'primary key ',
`userName` varchar(30) NOT NULL COMMENT 'name ',
`password` varchar(50) NOT NULL COMMENT 'password ',
`createTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
`active` int COMMENT 'Whether it is valid (0 no,1 yes)',
 PRIMARY KEY (`id`)
ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = 'example';

*/

// demoStructTableName Table name constant for direct call
const demoStructTableName = "t_demo"

// demoStruct example
type demoStruct struct {
	// Introduce the default struct to isolate method changes to the IEntityStruct
	zorm.EntityStruct

	// Id Primary key
	Id string `column:"id"`

	// UserName Specifies the name
	UserName string `column:"userName"`

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

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

	// Active Whether it is valid (0 No,1 yes)
	// Active int `column:"active"`

	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - end of the database field, custom fields to write in the following -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // 
	// 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)

	// Simulates the custom field Active
	Active int
}

// GetTableName Gets the table name
// IEntityStruct interface method, entity class needs to implement!!
func (entity *demoStruct) GetTableName() string {
	return demoStructTableName
}

// 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
// Joint primary key is not supported. It can be considered that there is no primary key and service control can be realized (hard choice).
// If you do not have a primary key, you need to implement this method as well
// IEntityStruct interface method, entity class needs to implement!!
func (entity *demoStruct) GetPKColumnName() string {
	// If there is no primary key
	// return ""
	return "id"
}

// 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

//func (entity *EntityStruct) GetDefaultValue() map[string]interface{} {
//	return map[string]interface{}{"CreateTime": time.Now(),"Active":nil}
//}

// newDemoStruct creates a default object
func newDemoStruct() demoStruct {
	demo := demoStruct{
		// 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
		Id:		 zorm.FuncGenerateStringID(ctx),
		UserName:   "defaultUserName",
		Password:   "defaultPassword",
		Active:	 1,
		CreateTime: time.Now(),
	}
	return demo
}

Test cases are documents

https://gitee.com/wuxiangege/zorm-examples


// testzorm uses native sql statements with no restrictions on sql syntax. The statement uses Finder as the carrier
// Universal use of placeholders? zorm automatically replaces placeholders based on the database type, such as the postgresql database? Replace it with $1,$2...
// 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()
// Transaction must be explicitly enabled using zorm.Transaction(ctx, func(ctx context.context) (interface{}, error) {})
package testzorm

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

	"gitee.com/chunanyong/zorm"

	// 00. Introduce the database driver
	_ "github.com/go-sql-driver/mysql"
)

// DBDAOs represent one database. If there are multiple databases, multiple DBDAOs are declared
var dbDao *zorm.DBDao

// 01. Initialize the DBDao
func init() {

	// Customize zorm log output
	// zorm.LogCallDepth = 4 // Level of log calls
	// zorm.FuncLogError = myFuncLogError // Function to log exceptions
	// zorm.FuncLogPanic = myFuncLogPanic // To log panic, the default is defaultLogError
	// zorm.FuncPrintSQL = myFuncPrintSQL // A function that prints sql

	// Reassign the FuncPrintSQL function to a custom log output format
	// log.SetFlags(log.LstdFlags)
	// zorm.FuncPrintSQL = zorm.FuncPrintSQL

	// Custom primary key generation
	// zorm.FuncGenerateStringID=funcmyId

	// Customize the Tag column name
	// zorm.FuncWrapFieldTagName=funcmyTagName

	// Custom decimal type implementation
	// zorm.FuncDecimalValue=funcmyDecimal

	// the Go database driver list: https://go.dev/wiki/SQLDrivers

	// dbDaoConfig Configure the database. This is just a simulation, the production should be reading the configuration configuration file and constructing the DataSourceConfig
	dbDaoConfig := zorm.DataSourceConfig{
		// DSN database connection string. parseTime=true is automatically converted to time format. The default query is the []byte array
		DSN: "root:root@tcp(127.0.0.1:3306)/zorm?charset=utf8&parseTime=true&loc=Local",

		// DriverName database driver name: mysql, postgres, oracle(go-ora), essentially, sqlite3, go_ibm_db, clickhouse, dm, kingbase, aci, taosSql | taosRestful Correspond to Dialect
		// 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
		DriverName: "mysql",

		// the Dialect database Dialect: mysql, postgresql, oracle, MSSQL, sqlite, db2, clickhouse, dm, kingbase, shentong, tdengine and DriverName corresponding
		Dialect: "mysql",

		// MaxOpenConns The default maximum number of database connections is 50
		MaxOpenConns: 50,

		// MaxIdleConns The default maximum number of idle connections is 50
		MaxIdleConns: 50,

		// 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)
		ConnMaxLifetimeSecond: 600,

		// 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 >=SlowSQLMillis value
		SlowSQLMillis: 0,

		// DefaultTxOptions Default configuration of transaction isolation level, which defaults to nil
		// DefaultTxOptions: nil,
		// If distributed transactions are used, the default configuration is recommended
		// DefaultTxOptions: &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false},

		// FuncGlobalTransaction seata/hptx An adaptation function of a globally distributed transaction that returns the implementation of the IGlobalTransaction interface
		// business must call ctx, _ = zorm.BindContextEnableGlobalTransaction (ctx) on the global distribution of transactions
		// FuncGlobalTransaction : MyFuncGlobalTransaction,

		// SQLDB uses an existing database connection and has a higher priority than DSN
		// SQLDB : nil,

		// 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
		// Disable transactions should have the driver forgery transaction API, there should be no orm implementation,clickhouse's driver does just that
		// DisableTransaction :false,

		// 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
		// TDengineInsertsColumnName :false,
	}

	// 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
	dbDao, _ = zorm.NewDBDao(&dbDaoConfig)
}

// TestInsert 02. Test save the Struct object
func TestInsert(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()
	var ctx = context.Background()

	// 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
	// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level
	// such as ctx, _ := dbDao BindContextTxOptions (ctx, & SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of zorm.DataSourceConfig.DefaultTxOptions
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		// Create a demo object
		demo := newDemoStruct()

		// 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
		_, err := zorm.Insert(ctx, &demo)

		// If err is not returned nil, the transaction is rolled back
		return nil, err
	})
	// Mark the test failed
	if err != nil {
		t.Errorf("Error:%v", err)
	}
}

// TestInsertSlice 03. Tests batch save Struct object Slice
// The primary key property in the Struct object cannot be assigned if the primary key is autoincrement
func TestInsertSlice(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()
	var ctx = context.Background()

	// 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
	// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level
	// such as ctx, _ := dbDao BindContextTxOptions (ctx, & SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of zorm.DataSourceConfig.DefaultTxOptions
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {

		// slice stores the type zorm.IEntityStruct!!! Use the IEntityStruct interface, compatible with Struct entity classes
		demoSlice := make([]zorm.IEntityStruct,0)

		// Create object 1
		demo1 := newDemoStruct()
		demo1.UserName = "demo1"
		// Create object 2
		demo2 := newDemoStruct()
		demo2.UserName = "demo2"

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

		// Batch save objects. If the primary key is auto-increment, the auto-increment ID cannot be saved to the object.
		_, err := zorm.InsertSlice(ctx, demoSlice)

		// If err is not returned nil, the transaction is rolled back
		return nil, err
	})
	// Mark the test failed
	if err != nil {
		t.Errorf("Error:%v", err)
	}
}

// TestInsertEntityMap 04. Test to save an EntityMap object for scenarios where it is not convenient to use struct. Use Map as the carrier
func TestInsertEntityMap(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()
	var ctx = context.Background()

	// 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
	// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level
	// such as ctx, _ := dbDao BindContextTxOptions (ctx, & SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of zorm.DataSourceConfig.DefaultTxOptions
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		// To create an EntityMap, pass in the table name
		entityMap := zorm.NewEntityMap(demoStructTableName)
		// Set the primary key name
		entityMap.PkColumnName = "id"
		// If it is an increment sequence, set the value of the sequence
		// entityMap.PkSequence = "mySequence"

		// Set Sets the field values of the database
		// If the primary key is an increment or sequence, do not set the value of the entityMap.Set primary key
		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)

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

		// If err is not returned nil, the transaction is rolled back
		return nil, err
	})
	// Mark the test failed
	if err != nil {
		t.Errorf("Error:%v", err)
	}
}


// TestInsertEntityMapSlice 05. Test batch save []IEntityMap for scenarios where it is not convenient to use struct, using Map as carrier
func TestInsertEntityMapSlice(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's 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)

		// Execute
		_, err := zorm.InsertEntityMapSlice(ctx, entityMapSlice)

		// If err is not returned nil, the transaction is rolled back
		return nil, err
	})
	// Mark the test failed
	if err != nil {
		t.Errorf("Error:%v", err)
	}
}

// TestQueryRow 06. Test query a struct object
func TestQueryRow(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()
	var ctx = context.Background()

	// Declares a pointer to an object that holds the returned data
	demo := demoStruct{}

	// finder used to construct the query
	// 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 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

	// 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
	// in (?) Arguments must have () parentheses, not in?
	finder.Append("WHERE id=? and active in(?) ", "20210630163227149563000042432429", []int{0, 1})

	// How do I use like
	// finder.Append("WHERE id like ? ", "20210630163227149563000042432429%")

	// If the value of "has" is true, the database has data
	has, err := zorm.QueryRow(ctx, finder, &demo)

	if err != nil { // Mark the test failed
		t.Errorf("Error:%v", err)
	}
	// Print the result
	fmt.Println(demo)
}

// TestQueryRowMap 07. Test query map receives results. It is flexible for scenarios that are not suitable for structs
func TestQueryRowMap(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()
	var ctx = context.Background()

	// finder used to construct the query
	// finder := zorm.NewSelectFinder(demoStructTableName) // select * from t_demo
	finder := zorm.NewFinder().Append("SELECT * FROM " + demoStructTableName) // select * from t_demo
	// 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
	// in (?) Arguments must have () parentheses, not in?
	finder.Append("WHERE id=? and active in(?) ", "20210630163227149563000042432429", []int{0, 1})
	// Run the query
	resultMap, err := zorm.QueryRowMap(ctx, finder)

	if err != nil { // Mark the test failed
		t.Errorf("Error:%v", err)
	}
	// Print the result
	fmt.Println(resultMap)
}

// TestQuery 08. Test the list of query objects
func TestQuery(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()
	var ctx = context.Background()

	// Create a slice for receiving results
	list := make([]demoStruct, 0)

	// finder used to construct the query
	// finder := zorm.NewSelectFinder(demoStructTableName) // select * from t_demo
	finder := zorm.NewFinder().Append("SELECT id FROM " + demoStructTableName) // select * from t_demo
	// Create a paging object. After the query is complete, the page object can be directly used by the front-end paging component
	page := zorm.NewPage()
	page.PageNo = 1   // Query page 1. The default value is 1
	page.PageSize = 20 // 20 per page. The default is 20

	// The total number of entries is not queried
	// finder.SelectTotalCount = false

	// You can manually specify paging statements if they are particularly complex statements that cause count statement construction to fail
	// countFinder := zorm.NewFinder().Append("select count(*) from (")
	// countFinder.AppendFinder(finder)
	// countFinder.Append(") tempcountfinder")
	// finder.CountFinder = countFinder

	// Run the query
	err := zorm.Query(ctx, finder, &list, page)
	if err != nil { // Mark the test failed
		t.Errorf("Error:%v", err)
	}
	// Print the result
	fmt.Println("Total number of items :", page.TotalCount, "List :", list)
}

// TestQueryMap 09. Test query map list. Used in the scenario where struct is not convenient
func TestQueryMap(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()
	var ctx = context.Background()

	// finder used to construct the query
	// finder := zorm.NewSelectFinder(demoStructTableName) // select * from t_demo
	finder := zorm.NewFinder().Append("SELECT * FROM " + demoStructTableName) // select * from t_demo
	// Create a paging object. After the query is complete, the page object can be directly used by the front-end paging component
	page := zorm.NewPage()
	page.PageNo = 1   // Query page 1. The default value is 1
	page.PageSize = 20 // 20 per page. The default is 20

	// The total number of entries is not queried
	// finder.SelectTotalCount = false
	
	// You can manually specify paging statements if they are particularly complex statements that cause count statement construction to fail
	// countFinder := zorm.NewFinder().Append("select count(*) from (")
	// countFinder.AppendFinder(finder)
	// countFinder.Append(") tempcountfinder")
	// finder.CountFinder = countFinder

	// Run the query
	listMap, err := zorm.QueryMap(ctx, finder, page)
	if err != nil { // Mark the test failed
		t.Errorf("Error:%v", err)
	}
	// Print the result
	fmt.Println("Total number of items :", page.TotalCount, "List :", listMap)
}

// TestUpdateNotZeroValue 10. Update the struct object with only the non-zero fields. The primary key must have a value
func TestUpdateNotZeroValue(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()
	var ctx = context.Background()

	// 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
	// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level
	// such as ctx, _ := dbDao BindContextTxOptions (ctx, & SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of zorm.DataSourceConfig.DefaultTxOptions
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		// Declares a pointer to an object used to update data
		demo := demoStruct{}
		demo.Id = "20210630163227149563000042432429"
		demo.UserName = "UpdateNotZeroValue"

		// UPDATE "sql":"UPDATE t_demo SET userName=? WHERE id=?" ,"args":["UpdateNotZeroValue","20210630163227149563000042432429"]
		_, err := zorm.UpdateNotZeroValue(ctx, &demo)

		// If err is not returned nil, the transaction is rolled back
		return nil, err
	})
	if err != nil { // Mark the test failed
		t.Errorf("Error:%v", err)
	}

}

// TestUpdate 11. Update the struct object, updating all fields. The primary key must have a value
func TestUpdate(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()
	var ctx = context.Background()

	// 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
	// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level
	// such as ctx, _ := dbDao BindContextTxOptions (ctx, & SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of zorm.DataSourceConfig.DefaultTxOptions
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {

		// Declares a pointer to an object used to update data
		demo := demoStruct{}
		demo.Id = "20210630163227149563000042432429"
		demo.UserName = "TestUpdate"

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

		// If err is not returned nil, the transaction is rolled back
		return nil, err
	})
	if err != nil { // Mark the test failed
		t.Errorf("Error:%v", err)
	}
}

// TestUpdateFinder 12. With finder update,zorm's most flexible way of writing any update statement, even manually writing insert statements
func TestUpdateFinder(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()
	var ctx = context.Background()

	// 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
	// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level
	// such as ctx, _ := dbDao BindContextTxOptions (ctx, & SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of 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")

		// UPDATE "sql":"UPDATE t_demo SET userName=? ,active=? WHERE id=?" ,"args":["TestUpdateFinder",1,"20210630163227149563000042432429"]
		_, err := zorm.UpdateFinder(ctx, finder)

		// If err is not returned nil, the transaction is rolled back
		return nil, err
	})
	if err != nil { // Mark the test failed
		t.Errorf("Error:%v", err)
	}

}

// TestUpdateEntityMap 13. Update an EntityMap. The primary key must have a value
func TestUpdateEntityMap(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()
	var ctx = context.Background()

	// 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
	// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level
	// such as ctx, _ := dbDao BindContextTxOptions (ctx, & SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of zorm.DataSourceConfig.DefaultTxOptions
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		// To create an EntityMap, pass in the table name
		entityMap := zorm.NewEntityMap(demoStructTableName)
		// Set the primary key name
		entityMap.PkColumnName = "id"
		// Set Sets the field value of the database. The primary key must have a value
		entityMap.Set("id", "20210630163227149563000042432429")
		entityMap.Set("userName", "TestUpdateEntityMap")
		// UPDATE "sql":"UPDATE t_demo SET userName=? WHERE id=?" ,"args":["TestUpdateEntityMap","20210630163227149563000042432429"]
		_, err := zorm.UpdateEntityMap(ctx, entityMap)

		// If err is not returned nil, the transaction is rolled back
		return nil, err
	})
	if err != nil { // Mark the test failed
		t.Errorf("Error:%v", err)
	}

}

// TestDelete 14. Delete a struct object. The primary key must have a value
func TestDelete(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()
	var ctx = context.Background()

	// 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
	// if zorm.DataSourceConfig.DefaultTxOptions configuration does not meet the requirements, can be in zorm, Transaction before Transaction method set the Transaction isolation level
	// such as ctx, _ := dbDao BindContextTxOptions (ctx, & SQL TxOptions {Isolation: SQL LevelDefault, ReadOnly: False}), if txOptions is nil, the use of 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)

		// If err is not returned nil, the transaction is rolled back
		return nil, err
	})
	if err != nil { // Mark the test failed
		t.Errorf("Error:%v", err)
	}

}

// TestProc 15. Test calls the stored procedure
func TestProc(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's 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. Test calls custom functions
func TestFunc(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's 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. Some other instructions. Thank you very much for seeing this line
func TestOther(t *testing.T) {
	// ctx is generally a request for one ctx, normally there should be a web layer in, such as gin's c. Request.Context()
	var ctx = context.Background()

	// 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
	// You can also rewrite the FuncReadWriteStrategy function to return the DBDao of the specified database by setting a different key via ctx
	newCtx, err := dbDao.BindContextDBConnection(ctx)
	if err != nil { // Mark the test failed
		t.Errorf("Error:%v", err)
	}

	finder := zorm.NewFinder().Append("SELECT * FROM " + demoStructTableName) // select * from t_demo
	// Pass the new newCtx to zorm's function
	list, _ := zorm.QueryMap(newCtx, finder, nil)
	fmt.Println(list)

	// Scenario 2. Read/write separation of a single database. Set the read-write separation policy function.
	zorm.FuncReadWriteStrategy = myReadWriteStrategy

	// Scenario 3. If multiple databases exist and read and write data are separated from each other, perform this operation according to Scenario 1.
	// You can also rewrite the FuncReadWriteStrategy function to return the DBDao of the specified database by setting a different key via ctx

}

// myReadWriteStrategy Database read-write strategy rwType=0 read,rwType=1 write
// You can also set different keys through ctx to return the DBDao of the specified database
func myReadWriteStrategy(ctx context.Context, rwType int) (*zorm.DBDao, error) {
	// Return the required read/write dao based on your business scenario. This function is called every time a database connection is needed
	// if rwType == 0 {
	// return dbReadDao
	// }
	// return dbWriteDao

	return dbDao, nil
}

// --------------------------------------------
// ICustomDriverValueConver interface, see examples of DaMeng

// --------------------------------------------
// OverrideFunc Rewrite the functions of ZORM, when you use this function, you have to know what you are doing

// oldInsertFunc The default Insert implementation
var oldInsertFunc func(ctx context.Context, entity zorm.IEntityStruct) (int, error)

// newInsertFunc New Insert implementation
var newInsertFunc = func(ctx context.Context, entity zorm.IEntityStruct) (int, error) {
	fmt.Println("Insert before")
	i, err := oldInsertFunc(ctx, entity)
	fmt.Println("Insert after")
	return i, err
}

// Register the old function in the init function that overrides the old one
func init() {
	ok, oldFunc, err := zorm.OverrideFunc("Insert", newInsertFunc)
	if ok && err == nil {
		oldInsertFunc = oldFunc.(func(ctx context.Context, entity zorm.IEntityStruct) (int, error))
	}
}

Global transaction

seata-go CallbackWithCtx function mode

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

// Import the seata-go dependency package
import (
	"context"
	"fmt"
	"time"

	"github.com/seata/seata-go/pkg/client"
	"github.com/seata/seata-go/pkg/tm"
	seataSQL "github.com/seata/seata-go/pkg/datasource/sql" //Note: zorm's DriverName: seataSQL.SeataATMySQLDriver, !!!!
)

// Path of the configuration file
var configPath = "./conf/client.yml"

func main() {

	// Initialize the configuration
	conf := config.InitConf(configPath)
	// Initialize the zorm database
	// note: zorm DriverName: seataSQL SeataATMySQLDriver,!!!!!!!!!!
	initZorm()

	// Start distributed transactions
	tm.WithGlobalTx(context.Background(), &tm.GtxConfig{
		Name:	"ATSampleLocalGlobalTx",
		Timeout: time.Second * 30,
	}, CallbackWithCtx)
	// CallbackWithCtx business callback definition
	// type CallbackWithCtx func(ctx context.Context) error


	// Get the XID after the transaction is started. This can be passed through gin's header, or otherwise
	// xid:=tm.GetXID(ctx)
	// tm.SetXID(ctx, xid)

	// If the gin framework is used, middleware binding parameters can be used
	// r.Use(ginmiddleware.TransactionMiddleware())
}

seata-go transaction hosting mode

// Do not use CallbackWithCtx function,zorm to achieve transaction management, no modification of business code, zero intrusion to achieve distributed transactions


// The distributed transaction must be started manually and must be invoked before the local transaction is started
ctx,_ = zorm.BindContextEnableGlobalTransaction(ctx)
// Distributed transaction sample code
_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {

	// 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
	// xid := ctx.Value("XID").(string)

	// Pass the xid to the third party application
	// req.Header.Set("XID", xid)

	// If err is not returned nil, local and distributed transactions are rolled back
	return nil, err
})

// /---------- Third-party application -------/ //

	// Do not use the middleware provided by seata-go by default, just ctx binding XID!!!
	//// r.Use(ginmiddleware.TransactionMiddleware())
	xid := c.GetHeader(constant.XidKey)
	ctx = context.WithValue(ctx, "XID", xid)

	// The distributed transaction must be started manually and must be invoked before the local transaction is started
	ctx,_ = zorm.BindContextEnableGlobalTransaction(ctx)
	// ctx invokes the business transaction after binding the XID
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {

	// Business code......

	// If err is not returned nil, local and distributed transactions are rolled back
	return nil, err
})

// It is recommended that the following code be placed in a separate file
// ... // 

// ZormGlobalTransaction packaging seata *tm.GlobalTransactionManager, zorm.IGlobalTransaction interface
type ZormGlobalTransaction struct {
	*tm.GlobalTransactionManager
}

// MyFuncGlobalTransaction zorm A function that ADAPTS a seata globally distributed transaction
// important!!!! Need to configure the zorm.DataSourceConfig.FuncGlobalTransaction = MyFuncGlobalTransaction important!!!!!!
func MyFuncGlobalTransaction(ctx context.Context) (zorm.IGlobalTransaction, context.Context, context.Context, error) {
	// Create a seata-go transaction
	globalTx := tm.GetGlobalTransactionManager()
	// Use the zorm.IGlobalTransaction interface object to wrap distributed transactions and isolate the seata-go dependencies
	globalTransaction := &ZormGlobalTransaction{globalTx}

	if tm.IsSeataContext(ctx) {
		return globalTransaction, ctx, ctx, nil
	}
	// open global transaction for the first time
	ctx = tm.InitSeataContext(ctx)
	// There is a request to come in, manually get the XID
	xidObj := ctx.Value("XID")
	if xidObj ! = nil {
		xid := xidObj.(string)
		tm.SetXID(ctx, xid)
	}
	tm.SetTxName(ctx, "ATSampleLocalGlobalTx")

	// use new context to process current global transaction.
	if tm.IsGlobalTx(ctx) {
		globalRootContext := transferTx(ctx)
		return globalTransaction, ctx, globalRootContext, nil
	}
	return globalTransaction, ctx, ctx, nil
}

// IGlobalTransaction managed global distributed transaction interface (zorm.IGlobalTransaction). seata and hptx currently implement the same code, only the reference implementation package is different

// BeginGTX Starts global distributed transactions
func (gtx *ZormGlobalTransaction) BeginGTX(ctx context.Context, globalRootContext context.Context) error {
	//tm.SetTxStatus(globalRootContext, message.GlobalStatusBegin)
	err := gtx.Begin(globalRootContext, time.Second*30)
	return err
}

// CommitGTX Commit global distributed transactions
func (gtx *ZormGlobalTransaction) CommitGTX(ctx context.Context, globalRootContext context.Context) error {
	gtr := tm.GetTx(globalRootContext)
	return gtx.Commit(globalRootContext, gtr)
}

// RollbackGTX rolls back globally distributed transactions
func (gtx *ZormGlobalTransaction) RollbackGTX(ctx context.Context, globalRootContext context.Context) error {
	gtr := tm.GetTx(globalRootContext)
	// If it is the Participant role, change it to the Launcher role to allow branch transactions to submit global transactions.
	if gtr.TxRole != tm.Launcher {
		gtr.TxRole = tm.Launcher
	}
	return gtx.Rollback(globalRootContext, gtr)
}
// GetGTXID Gets the XID of the globally distributed transaction
func (gtx *ZormGlobalTransaction) GetGTXID(ctx context.Context, globalRootContext context.Context) (string.error) {
	return tm.GetXID(globalRootContext), nil
}

// transferTx transfer the gtx into a new ctx from old ctx.
// use it to implement suspend and resume instead of seata java
func transferTx(ctx context.Context) context.Context {
	newCtx := tm.InitSeataContext(context.Background())
	tm.SetXID(newCtx, tm.GetXID(ctx))
	return newCtx
}

// ... // 

hptx proxy mode

in hptx proxy mode for zorm use example

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

// Introduce the hptx dependency package
import (
	"github.com/cectc/hptx"
	"github.com/cectc/hptx/pkg/config"
	"github.com/cectc/hptx/pkg/resource"
	"github.com/cectc/mysql"
	"github.com/cectc/hptx/pkg/tm"

	gtxContext "github.com/cectc/hptx/pkg/base/context"
)

// Path of the configuration file
var configPath = "./conf/config.yml"

func main() {

	// Initialize the configuration
	hptx.InitFromFile(configPath)
	
	// Register the mysql driver
	mysql.RegisterResource(config.GetATConfig().DSN)
	resource.InitATBranchResource(mysql.GetDataSourceManager())
	// sqlDB, err := sql.Open("mysql", config.GetATConfig().DSN)


	// After the normal initialization of zorm, be sure to put it after the hptx mysql initialization!!

	// ... // 
	// tm register transaction service, refer to the official example (transaction hosting is mainly to remove proxy, zero intrusion on the business)
	tm.Implement(svc.ProxySvc)
	// ... // 


	// Get the hptx rootContext
	// rootContext := gtxContext.NewRootContext(ctx)
	// rootContext := ctx.(*gtxContext.RootContext)

	// Create an hptx transaction
	// globalTx := tm.GetCurrentOrCreate(rootContext)

	// Start the transaction
	// globalTx. BeginWithTimeoutAndName (int32 (6000), "name of the transaction," rootContext)

	// Get the XID after the transaction is started. This can be passed through the gin header, or otherwise
	// xid:=rootContext.GetXID()

	// If using gin frame, get ctx
	// ctx := c.Request.Context()

	// Accept the XID passed and bind it to the local ctx
	// ctx =context.WithValue(ctx,mysql.XID,xid)
}

hptx transaction hosting mode

zorm transaction hosting hptx example

// Do not use proxy proxy mode,zorm to achieve transaction management, no modification of business code, zero intrusion to achieve distributed transactions
// tm.Implement(svc.ProxySvc)

// The distributed transaction must be started manually and must be invoked before the local transaction is started
ctx,_ = zorm.BindContextEnableGlobalTransaction(ctx)
// Distributed transaction sample code
_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {

	// 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
	// xid := ctx.Value("XID").(string)

	// Pass the xid to the third party application
	// req.Header.Set("XID", xid)

	// If err is not returned nil, local and distributed transactions are rolled back
	return nil, err
})

// /---------- Third-party application -------// /

// Before third-party applications can start transactions,ctx needs to bind Xids, such as gin framework

// Accept the XID passed and bind it to the local ctx
// xid:=c.Request.Header.Get("XID")
// ctx is obtained
// ctx := c.Request.Context()
// ctx = context.WithValue(ctx,"XID",xid)

// The distributed transaction must be started manually and must be invoked before the local transaction is started
ctx,_ = zorm.BindContextEnableGlobalTransaction(ctx)
// ctx invokes the business transaction after binding the XID
_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {

	// Business code......

	// If err is not returned nil, local and distributed transactions are rolled back
	return nil, err
})



// It is recommended that the following code be placed in a separate file
// ... // 

// ZormGlobalTransaction packaging hptx *tm.DefaultGlobalTransaction, zorm.IGlobalTransaction interface
type ZormGlobalTransaction struct {
	*tm.DefaultGlobalTransaction
}

// MyFuncGlobalTransaction zorm A function that ADAPTS a hptx globally distributed transaction
// important!!!! Need to configure the zorm.DataSourceConfig.FuncGlobalTransaction = MyFuncGlobalTransaction important!!!!!!
func MyFuncGlobalTransaction(ctx context.Context) (zorm.IGlobalTransaction, context.Context, context.Context, error) {
	// Obtain the hptx rootContext
	rootContext := gtxContext.NewRootContext(ctx)
	// Create a hptx transaction
	globalTx := tm.GetCurrentOrCreate(rootContext)
	// Use the zorm.IGlobalTransaction interface object to wrap distributed transactions and isolate hptx dependencies
	globalTransaction := &ZormGlobalTransaction{globalTx}

	return globalTransaction, ctx, rootContext, nil
}

// IGlobalTransaction managed global distributed transaction interface (zorm.IGlobalTransaction). seata and hptx currently implement the same code, only the reference implementation package is different

// BeginGTX Starts global distributed transactions
func (gtx *ZormGlobalTransaction) BeginGTX(ctx context.Context, globalRootContext context.Context) error {
	rootContext := globalRootContext.(*gtxContext.RootContext)
	return gtx.BeginWithTimeout(int32(6000), rootContext)
}

// CommitGTX Commit global distributed transactions
func (gtx *ZormGlobalTransaction) CommitGTX(ctx context.Context, globalRootContext context.Context) error {
	rootContext := globalRootContext.(*gtxContext.RootContext)
	return gtx.Commit(rootContext)
}

// RollbackGTX rolls back globally distributed transactions
func (gtx *ZormGlobalTransaction) RollbackGTX(ctx context.Context, globalRootContext context.Context) error {
	rootContext := globalRootContext.(*gtxContext.RootContext)
	// If it is the Participant role, change it to the Launcher role to allow branch transactions to submit global transactions.
	if gtx.Role != tm.Launcher {
		gtx.Role = tm.Launcher
	}
	return gtx.Rollback(rootContext)
}
// GetGTXID Gets the XID of the globally distributed transaction
func (gtx *ZormGlobalTransaction) GetGTXID(ctx context.Context, globalRootContext context.Context) (string.error) {
	rootContext := globalRootContext.(*gtxContext.RootContext)
	return rootContext.GetXID(), nil
}

// ... // 

dbpack distributed transactions

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

// Before starting dbpack transactions,ctx needs to bind sql hints, such as using the gin framework to obtain the xid passed by the header
xid := c.Request.Header.Get("xid")
// Generate sql hint content using xid, and then bind the hint to ctx
hint := fmt.Sprintf("/*+ XID('%s') */", xid)
// ctx is obtained
ctx := c.Request.Context()
// Bind the hint to ctx
ctx,_ = zorm.BindContextSQLHint(ctx, hint)

// After ctx binds the sql hint, the business transaction is invoked and ctx is transmitted to realize the propagation of the distributed transaction
_, err := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {

	// Business code......

	// If err is not returned nil, local and distributed transactions are rolled back
	return nil, err
})

Documentation

Overview

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

Index

Constants

This section is empty.

Variables

View Source
var FuncDecimalValue = func(ctx context.Context, config *DataSourceConfig) interface{} {
	return &decimal.Decimal{}
}

FuncDecimalValue 设置decimal类型接收值,复写函数自定义decimal实现,例如github.com/shopspring/decimal,返回的是指针

View Source
var FuncGenerateStringID = func(ctx context.Context) string {

	randNum, randErr := rand.Int(rand.Reader, big.NewInt(1000000000))
	if randErr != nil {
		return ""
	}

	rand9 := fmt.Sprintf("%09d", randNum)

	pk := time.Now().Format("2006.01.02.15.04.05.000000000")
	pk = strings.ReplaceAll(pk, ".", "")

	pk = pk + rand9
	return pk
}

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

View Source
var FuncLogError func(ctx context.Context, err error) = defaultLogError

FuncLogError 记录error日志.NewDBDao方法里的异常,ctx为nil,扩展时请注意 FuncLogError Record error log

View Source
var FuncLogPanic func(ctx context.Context, err error) = defaultLogPanic

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

View Source
var FuncPrintSQL func(ctx context.Context, sqlstr string, args []interface{}, execSQLMillis int64) = defaultPrintSQL

FuncPrintSQL 打印sql语句,参数和执行时间,小于0是禁用日志输出;等于0是只输出日志,不计算SQ执行时间;大于0是计算执行时间,并且大于指定值 FuncPrintSQL Print sql statement and parameters

View Source
var FuncReadWriteStrategy = func(ctx context.Context, rwType int) (*DBDao, error) {
	if defaultDao == nil {
		return nil, errors.New("->FuncReadWriteStrategy-->defaultDao为nil,请检查数据库初始化配置是否正确,主要是DSN,DriverName和Dialect")
	}
	return defaultDao, nil
}

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

View Source
var FuncWrapFieldTagName func(field *reflect.StructField, colName string) string = nil

FuncWrapFieldTagName 用于包裹字段名, eg. `describe`

View Source
var LogCallDepth = 4

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

View Source
var Query = func(ctx context.Context, finder *Finder, rowsSlicePtr interface{}, page *Page) error {
	return query(ctx, finder, rowsSlicePtr, page)
}

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

Functions

func BindContextDisableTransaction added in v1.6.1

func BindContextDisableTransaction(parent context.Context) (context.Context, error)

BindContextDisableTransaction context禁用事务,必须放到事务开启之前调用.用在不使用事务更新数据库的场景,强烈建议不要使用这个方法,更新数据库必须有事务!!!

func BindContextEnableGlobalTransaction added in v1.5.7

func BindContextEnableGlobalTransaction(parent context.Context) (context.Context, error)

BindContextEnableGlobalTransaction context启用分布式事务,不再自动设置,必须手动启用分布式事务,必须放到本地事务开启之前调用

func BindContextMustUpdateCols added in v1.7.0

func BindContextMustUpdateCols(parent context.Context, cols []string) (context.Context, error)

BindContextMustUpdateCols 指定必须更新的数据库字段,只对UpdateNotZeroValue方法有效.cols是数据库列名切片 ctx里bind的值zorm不会清空,使用时不要覆盖原始的ctx或者不要传给多个UpdateNotZeroValue方法.

func BindContextOnlyUpdateCols added in v1.7.0

func BindContextOnlyUpdateCols(parent context.Context, cols []string) (context.Context, error)

BindContextOnlyUpdateCols 指定仅更新的数据库字段,只对Update方法有效.cols是数据库列名切片 ctx里bind的值zorm不会清空,使用时不要覆盖原始的ctx或者不要传给多个Update方法.

func BindContextSQLHint added in v1.5.7

func BindContextSQLHint(parent context.Context, hint string) (context.Context, error)

BindContextSQLHint context中绑定sql的hint,使用这个Context的方法都会传播hint传播的语句 hint 是完整的sql片段, 例如: hint:="/*+ XID('gs/aggregationSvc/2612341069705662465') */" 在第一个单词的后面拼接 hint sql,例如 select /*+ XID('gs/aggregationSvc/2612341069705662465') */ id,name from user

func Delete added in v1.3.1

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

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

func Insert added in v1.3.1

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

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

func InsertEntityMap added in v1.3.1

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

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

func InsertEntityMapSlice added in v1.6.3

func InsertEntityMapSlice(ctx context.Context, entityMapSlice []IEntityMap) (int, error)

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

func InsertSlice added in v1.3.3

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

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

func IsBindDBConnection added in v1.7.0

func IsBindDBConnection(ctx context.Context) (bool, error)

IsBindDBConnection 检查ctx是否已经绑定数据库连接

func IsInTransaction added in v1.5.9

func IsInTransaction(ctx context.Context) (bool, error)

IsInTransaction 检查ctx是否包含事务

func OverrideFunc added in v1.6.1

func OverrideFunc(funcName string, funcObject interface{}) (bool, interface{}, error)

// OverrideFunc 重写ZORM的函数,当你使用这个函数时,你必须知道自己在做什么

//oldInsertFunc 默认的Insert实现 var oldInsertFunc func(ctx context.Context, entity zorm.IEntityStruct) (int, error)

//newInsertFunc 新的Insert实现

var newInsertFunc = func(ctx context.Context, entity zorm.IEntityStruct) (int, error) {
	fmt.Println("Insert前")
	i, err := oldInsertFunc(ctx, entity)
	fmt.Println("Insert后")
	return i, err
}

// 在init函数中注册覆盖老的函数

func init() {
	ok, oldFunc, err := zorm.OverrideFunc("Insert", newInsertFunc)
	if ok && err == nil {
		oldInsertFunc = oldFunc.(func(ctx context.Context, entity zorm.IEntityStruct) (int, error))
	}
}

OverrideFunc 重写ZORM的函数,用于风险监控,只要查看这个函数的调用,就知道哪些地方重写了函数,避免项目混乱.当你使用这个函数时,你必须知道自己在做什么 funcName 是需要重写的方法命,funcObject是对应的函数. 返回值bool是否重写成功,interface{}是重写前的函数 一般是在init里调用重写

func QueryMap

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

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

func QueryRow added in v1.4.2

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

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

func QueryRowMap added in v1.4.2

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

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

func RegisterCustomDriverValueConver added in v1.6.1

func RegisterCustomDriverValueConver(dialectColumnType string, customDriverValueConver ICustomDriverValueConver) error

RegisterCustomDriverValueConver 注册自定义的字段处理逻辑,用于驱动无法直接转换的场景,例如达梦的 TEXT 无法直接转化成 string dialectColumnType 值是 Dialect.字段类型,例如: dm.TEXT 一般是放到init方法里进行注册

func Transaction

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

Transaction 的示例代码

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

	  //业务代码

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

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

func Update added in v1.3.1

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

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

func UpdateEntityMap

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

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

func UpdateFinder

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

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

func UpdateNotZeroValue added in v1.3.1

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

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

Types

type DBDao added in v1.3.1

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

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

func NewDBDao added in v1.3.1

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

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

func (*DBDao) BindContextDBConnection added in v1.3.1

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

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

func (*DBDao) BindContextTxOptions added in v1.4.3

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

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

func (*DBDao) CloseDB added in v1.5.5

func (dbDao *DBDao) CloseDB() error

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

type DataSourceConfig

type DataSourceConfig struct {
	// DSN dataSourceName 连接字符串
	// DSN DataSourceName Database connection string
	DSN string

	// DriverName 数据库驱动名称:mysql,postgres,oracle(go-ora),sqlserver,sqlite3,go_ibm_db,clickhouse,dm,kingbase,aci,taosSql|taosRestful 和Dialect对应
	// DriverName:mysql,dm,postgres,opi8,sqlserver,sqlite3,go_ibm_db,clickhouse,kingbase,aci,taosSql|taosRestful corresponds to Dialect
	DriverName string

	// Dialect 数据库方言:mysql,postgresql,oracle,mssql,sqlite,db2,clickhouse,dm,kingbase,shentong,tdengine 和 DriverName 对应
	// Dialect:mysql,postgresql,oracle,mssql,sqlite,db2,clickhouse,dm,kingbase,shentong,tdengine corresponds to DriverName
	Dialect string

	// SlowSQLMillis 慢sql的时间阈值,单位毫秒.小于0是禁用SQL语句输出;等于0是只输出SQL语句,不计算执行时间;大于0是计算SQL执行时间,并且>=SlowSQLMillis值
	SlowSQLMillis int

	// MaxOpenConns 数据库最大连接数,默认50
	// MaxOpenConns Maximum number of database connections, Default 50
	MaxOpenConns int

	// MaxIdleConns 数据库最大空闲连接数,默认50
	// MaxIdleConns The maximum number of free connections to the database default 50
	MaxIdleConns int

	// ConnMaxLifetimeSecond 连接存活秒时间. 默认600(10分钟)后连接被销毁重建.避免数据库主动断开连接,造成死连接.MySQL默认wait_timeout 28800秒(8小时)
	// ConnMaxLifetimeSecond (Connection survival time in seconds)Destroy and rebuild the connection after the default 600 seconds (10 minutes)
	// Prevent the database from actively disconnecting and causing dead connections. MySQL Default wait_timeout 28800 seconds
	ConnMaxLifetimeSecond int

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

	// DisableTransaction 禁用事务,默认false,如果设置了DisableTransaction=true,Transaction方法失效,不再要求有事务.为了处理某些数据库不支持事务,比如TDengine
	// 禁用事务应该有驱动伪造事务API,不应该由orm实现
	DisableTransaction bool

	// FuncGlobalTransaction seata/hptx全局分布式事务的适配函数,返回IGlobalTransaction接口的实现
	// 业务必须调用zorm.BindContextEnableGlobalTransaction(ctx)开启全局分布事务
	// seata-go 的ctx是统一的绑定的是struct,也不是XID字符串.  hptx是分离的,所以返回了两个ctx,兼容两个库
	FuncGlobalTransaction func(ctx context.Context) (IGlobalTransaction, context.Context, context.Context, error)

	// SQLDB 使用现有的数据库连接,优先级高于DSN
	SQLDB *sql.DB

	// TDengineInsertsColumnName TDengine批量insert语句中是否有列名.默认false没有列名,插入值和数据库列顺序保持一致,减少语句长度
	TDengineInsertsColumnName bool
}

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

func GetContextDataSourceConfig added in v1.7.4

func GetContextDataSourceConfig(ctx context.Context) (*DataSourceConfig, error)

GetContextDataSourceConfig 从ctx中获取DataSourceConfig对象

type EntityMap

type EntityMap struct {

	// 主键列名
	PkColumnName string `json:"pkColumnName,omitempty"`
	// 主键序列,如果有值,优先级最高
	PkSequence string `json:"pkSequence,omitempty"`
	// contains filtered or unexported fields
}

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

func NewEntityMap

func NewEntityMap(tbName string) *EntityMap

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

func (*EntityMap) GetDBFieldMap

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

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

func (*EntityMap) GetDBFieldMapKey added in v1.6.3

func (entity *EntityMap) GetDBFieldMapKey() []string

GetDBFieldMapKey 按照Set的先后顺序记录key值,也就是数据库字段,用于SQL排序 GetDBFieldMapKey records the key value, that is, the database field, in the order of the Set, which is used for SQL sorting

func (*EntityMap) GetEntityMapPkSequence added in v1.6.3

func (entity *EntityMap) GetEntityMapPkSequence() string

GetEntityMapPkSequence 主键序列,不能使用GetPkSequence方法名,避免默认实现了IEntityStruct接口 GetEntityMapPkSequence primary key sequence, you cannot use the GetPkSequence method name, to avoid the default implementation of IEntityStruct interface

func (*EntityMap) GetPKColumnName

func (entity *EntityMap) GetPKColumnName() string

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

func (*EntityMap) GetTableName

func (entity *EntityMap) GetTableName() string

GetTableName 获取表名称

func (*EntityMap) MarshalJSON added in v1.7.0

func (entityMap *EntityMap) MarshalJSON() ([]byte, error)

func (*EntityMap) Set

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

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

func (*EntityMap) UnmarshalJSON added in v1.7.0

func (entityMap *EntityMap) UnmarshalJSON(data []byte) error

type EntityStruct

type EntityStruct struct{}

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

func (*EntityStruct) GetDefaultValue added in v1.7.0

func (entity *EntityStruct) GetDefaultValue() map[string]interface{}

GetDefaultValue 获取列的默认值Map,仅用于Insert和InsertSlice Struct,对Update和UpdateNotZeroValue无效.返回map的key是Struct属性名,value是默认值,value可以是nil.不能是类型的默认值,比如int类型设置默认值为0 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

func (*EntityStruct) GetPKColumnName

func (entity *EntityStruct) GetPKColumnName() string

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

func (*EntityStruct) GetPkSequence

func (entity *EntityStruct) GetPkSequence() string

GetPkSequence 主键序列 GetPkSequence Primary key sequence

type Finder

type Finder struct {

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

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

func NewDeleteFinder

func NewDeleteFinder(tableName string) *Finder

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

func NewFinder

func NewFinder() *Finder

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

func NewSelectFinder

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

NewSelectFinder 根据表名初始化查询的Finder,strs 只取第一个字符串,用数组类型是为了可以不传入,默认为 * | Finder that initializes the query based on the table name NewSelectFinder("tableName") SELECT * FROM tableName NewSelectFinder("tableName", "id,name") SELECT id,name FROM tableName

func NewUpdateFinder

func NewUpdateFinder(tableName string) *Finder

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

func WrapUpdateStructFinder added in v1.6.1

func WrapUpdateStructFinder(ctx context.Context, entity IEntityStruct, onlyUpdateNotZero bool) (*Finder, error)

WrapUpdateStructFinder 返回更新IEntityStruct的Finder对象 ctx不能为nil,参照使用zorm.Transaction方法传入ctx.也不要自己构建DBConnection Finder为更新执行的Finder,更新语句统一使用Finder执行 updateStructFunc Update object ctx cannot be nil, refer to zorm.Transaction method to pass in ctx. Don't build DB Connection yourself Finder is the Finder that executes the update, and the update statement is executed uniformly using the Finder

func (*Finder) Append

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

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

func (*Finder) AppendFinder

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

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

func (*Finder) GetSQL

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

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

func (*Finder) GetValues added in v1.7.0

func (finder *Finder) GetValues() ([]interface{}, error)

GetValues 返回Finder封装的values值

func (*Finder) MarshalJSON added in v1.7.0

func (finder *Finder) MarshalJSON() ([]byte, error)

func (*Finder) UnmarshalJSON added in v1.7.0

func (finder *Finder) UnmarshalJSON(data []byte) error

type ICustomDriverValueConver added in v1.5.9

type ICustomDriverValueConver interface {
	// GetDriverValue 根据数据库列类型,返回driver.Value的实例,struct属性类型
	// map接收或者字段不存在,无法获取到structFieldType,会传入nil
	GetDriverValue(ctx context.Context, columnType *sql.ColumnType, structFieldType *reflect.Type) (driver.Value, error)

	// ConverDriverValue 数据库列类型,GetDriverValue返回的driver.Value的临时接收值,struct属性类型
	// map接收或者字段不存在,无法获取到structFieldType,会传入nil
	// 返回符合接收类型值的指针,指针,指针!!!!
	ConverDriverValue(ctx context.Context, columnType *sql.ColumnType, tempDriverValue driver.Value, structFieldType *reflect.Type) (interface{}, error)
}

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

type IEntityMap

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

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

	// GetEntityMapPkSequence 主键序列,不能使用GetPkSequence方法名,避免默认实现了IEntityStruct接口
	// GetEntityMapPkSequence primary key sequence, you cannot use the GetPkSequence method name, to avoid the default implementation of IEntityStruct interface
	GetEntityMapPkSequence() string

	// GetDBFieldMap 针对Map类型,记录数据库字段
	// GetDBFieldMap For Map type, record database fields.
	GetDBFieldMap() map[string]interface{}

	// GetDBFieldMapKey 按照Set的先后顺序记录key值,也就是数据库字段,用于SQL排序
	// GetDBFieldMapKey records the key value, that is, the database field, in the order of the Set, which is used for SQL sorting
	GetDBFieldMapKey() []string
	// 设置数据库字段的值
	// Set the value of a database field.
	Set(key string, value interface{}) map[string]interface{}
}

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

type IEntityStruct

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

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

	// GetPkSequence 主键序列
	// GetPkSequence Primary key sequence
	GetPkSequence() string

	// GetDefaultValue 获取列的默认值Map,仅用于Insert和InsertSlice Struct,对Update和UpdateNotZeroValue无效.返回map的key是Struct属性名,value是默认值,value可以是nil,不能是类型的默认值,比如int类型设置默认值为0
	// 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.
	GetDefaultValue() map[string]interface{}
}

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

type IGlobalTransaction added in v1.5.7

type IGlobalTransaction interface {
	// BeginGTX 开启全局分布式事务
	BeginGTX(ctx context.Context, globalRootContext context.Context) error

	// CommitGTX 提交全局分布式事务.不能命名为 Commit,不然就和gtx的Commit一致了,就递归调用自己了.......
	CommitGTX(ctx context.Context, globalRootContext context.Context) error

	// RollbackGTX 回滚全局分布式事务
	RollbackGTX(ctx context.Context, globalRootContext context.Context) error

	// GetGTXID 获取全局分布式事务的XID
	GetGTXID(ctx context.Context, globalRootContext context.Context) (string, error)
}

IGlobalTransaction 托管全局分布式事务接口

type Page

type Page struct {
	// 当前页码,从1开始
	// Current page number, starting from 1
	PageNo int `json:"pageNo,omitempty"`

	// 每页多少条,默认20条
	// How many items per page, 20 items by default
	PageSize int `json:"pageSize,omitempty"`

	// 数据总条数
	// Total number of data
	TotalCount int `json:"totalCount,omitempty"`

	// 共多少页
	// How many pages
	PageCount int `json:"pageCount,omitempty"`

	// 是否是第一页
	// Is it the first page
	FirstPage bool `json:"firstPage,omitempty"`

	// 是否有上一页
	// Whether there is a previous page
	HasPrev bool `json:"hasPrev,omitempty"`

	// 是否有下一页
	// Is there a next page
	HasNext bool `json:"hasNext,omitempty"`

	// 是否是最后一页
	// Is it the last page
	LastPage bool `json:"lastPage,omitempty"`
}

Page 分页对象 Page Pagination object

func NewPage

func NewPage() *Page

NewPage 创建Page对象 NewPage Create Page object

Directories

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

Jump to

Keyboard shortcuts

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