From 4a9fb23c4e8264b01063f36c38d210f65b2143a7 Mon Sep 17 00:00:00 2001 From: luhengyu <792526287@qq.com> Date: Thu, 17 Aug 2023 15:50:38 +0800 Subject: [PATCH] fix: fix inseart id auto increment(#2167) --- engine.go | 35 ++++++++++---- engine_group.go | 10 ++++ interface.go | 2 + session_insert.go | 115 +++++++++++++++++++++++++++++++++++++--------- 4 files changed, 130 insertions(+), 32 deletions(-) diff --git a/engine.go b/engine.go index aa9d8050..108515d4 100644 --- a/engine.go +++ b/engine.go @@ -47,6 +47,9 @@ type Engine struct { DatabaseTZ *time.Location // The timezone of the database logSessionID bool // create session id + + autoIncrementIncrement int64 // auto increment increment default 1 + lastInsertIDReversed bool // create id reversed } // NewEngine new a db manager according to the parameter. Currently support four @@ -71,16 +74,18 @@ func newEngine(driverName, dataSourceName string, dialect dialects.Dialect, db * tagParser := tags.NewParser("xorm", dialect, mapper, mapper, cacherMgr) engine := &Engine{ - dialect: dialect, - driver: dialects.QueryDriver(driverName), - TZLocation: time.Local, - defaultContext: context.Background(), - cacherMgr: cacherMgr, - tagParser: tagParser, - driverName: driverName, - dataSourceName: dataSourceName, - db: db, - logSessionID: false, + dialect: dialect, + driver: dialects.QueryDriver(driverName), + TZLocation: time.Local, + defaultContext: context.Background(), + cacherMgr: cacherMgr, + tagParser: tagParser, + driverName: driverName, + dataSourceName: dataSourceName, + db: db, + logSessionID: false, + autoIncrementIncrement: 1, + lastInsertIDReversed: false, } if dialect.URI().DBType == schemas.SQLITE { @@ -144,6 +149,16 @@ func (engine *Engine) SetQuotePolicy(quotePolicy dialects.QuotePolicy) { engine.dialect.SetQuotePolicy(quotePolicy) } +// SetAutoIncrementIncrement set insert id auto increment increment +func (engine *Engine) SetAutoIncrementIncrement(increment int64) { + engine.autoIncrementIncrement = increment +} + +// SetLastInsertIDReversed set insert id reversed +func (engine *Engine) SetLastInsertIDReversed(reversed bool) { + engine.lastInsertIDReversed = reversed +} + // BufferSize sets buffer size for iterate func (engine *Engine) BufferSize(size int) *Session { session := engine.NewSession() diff --git a/engine_group.go b/engine_group.go index f2fe913d..75e4dc3d 100644 --- a/engine_group.go +++ b/engine_group.go @@ -206,6 +206,16 @@ func (eg *EngineGroup) SetQuotePolicy(quotePolicy dialects.QuotePolicy) { } } +// SetAutoIncrementIncrement set insert id auto increment increment +func (eg *EngineGroup) SetAutoIncrementIncrement(increment int64) { + eg.autoIncrementIncrement = increment +} + +// SetLastInsertIDReversed set insert id reversed +func (eg *EngineGroup) SetLastInsertIDReversed(reversed bool) { + eg.lastInsertIDReversed = reversed +} + // SetTableMapper set the table name mapping rule func (eg *EngineGroup) SetTableMapper(mapper names.Mapper) { eg.Engine.SetTableMapper(mapper) diff --git a/interface.go b/interface.go index d10abe9e..a11febfb 100644 --- a/interface.go +++ b/interface.go @@ -117,6 +117,8 @@ type EngineInterface interface { SetTableMapper(names.Mapper) SetTZDatabase(tz *time.Location) SetTZLocation(tz *time.Location) + SetAutoIncrementIncrement(increment int64) + SetLastInsertIDReversed(reversed bool) AddHook(hook contexts.Hook) ShowSQL(show ...bool) Sync(...interface{}) error diff --git a/session_insert.go b/session_insert.go index cfa26d39..dd0c077f 100644 --- a/session_insert.go +++ b/session_insert.go @@ -210,33 +210,104 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e _ = session.cacheInsert(tableName) - lenAfterClosures := len(session.afterClosures) - for i := 0; i < size; i++ { - elemValue := reflect.Indirect(sliceValue.Index(i)).Addr().Interface() + var id int64 + if table.AutoIncrement != "" { + id, err = res.LastInsertId() + if err != nil || id <= 0 { + return res.RowsAffected() + } + } - // handle AfterInsertProcessor - if session.isAutoCommit { - // !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi?? - for _, closure := range session.afterClosures { - closure(elemValue) - } - if processor, ok := elemValue.(AfterInsertProcessor); ok { - processor.AfterInsert() - } - } else { - if lenAfterClosures > 0 { - if value, has := session.afterInsertBeans[elemValue]; has && value != nil { - *value = append(*value, session.afterClosures...) - } else { - afterClosures := make([]func(interface{}), lenAfterClosures) - copy(afterClosures, session.afterClosures) - session.afterInsertBeans[elemValue] = &afterClosures + lenAfterClosures := len(session.afterClosures) + if session.engine.lastInsertIDReversed { + for i := size - 1; i >= 0; i-- { + elemValue := reflect.Indirect(sliceValue.Index(i)).Addr().Interface() + + // handle AfterInsertProcessor + if session.isAutoCommit { + // !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi?? + for _, closure := range session.afterClosures { + closure(elemValue) + } + if processor, ok := elemValue.(AfterInsertProcessor); ok { + processor.AfterInsert() } } else { - if _, ok := elemValue.(AfterInsertProcessor); ok { - session.afterInsertBeans[elemValue] = nil + if lenAfterClosures > 0 { + if value, has := session.afterInsertBeans[elemValue]; has && value != nil { + *value = append(*value, session.afterClosures...) + } else { + afterClosures := make([]func(interface{}), lenAfterClosures) + copy(afterClosures, session.afterClosures) + session.afterInsertBeans[elemValue] = &afterClosures + } + } else { + if _, ok := elemValue.(AfterInsertProcessor); ok { + session.afterInsertBeans[elemValue] = nil + } } } + + // handle auto increment + aiValue, err := table.AutoIncrColumn().ValueOf(elemValue) + if err != nil { + session.engine.logger.Errorf("%v", err) + } + + if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() { + continue + } + + if err := convert.AssignValue(*aiValue, id); err != nil { + return 0, err + } + + id -= session.engine.autoIncrementIncrement + } + } else { + for i := 0; i < size; i++ { + elemValue := reflect.Indirect(sliceValue.Index(i)).Addr().Interface() + + // handle AfterInsertProcessor + if session.isAutoCommit { + // !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi?? + for _, closure := range session.afterClosures { + closure(elemValue) + } + if processor, ok := elemValue.(AfterInsertProcessor); ok { + processor.AfterInsert() + } + } else { + if lenAfterClosures > 0 { + if value, has := session.afterInsertBeans[elemValue]; has && value != nil { + *value = append(*value, session.afterClosures...) + } else { + afterClosures := make([]func(interface{}), lenAfterClosures) + copy(afterClosures, session.afterClosures) + session.afterInsertBeans[elemValue] = &afterClosures + } + } else { + if _, ok := elemValue.(AfterInsertProcessor); ok { + session.afterInsertBeans[elemValue] = nil + } + } + } + + // handle auto increment + aiValue, err := table.AutoIncrColumn().ValueOf(elemValue) + if err != nil { + session.engine.logger.Errorf("%v", err) + } + + if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() { + continue + } + + if err := convert.AssignValue(*aiValue, id); err != nil { + return 0, err + } + + id += session.engine.autoIncrementIncrement } }