// Package database ... // // Author : go_developer@163.com<白茶清欢> // // Date : 8:04 下午 2021/8/8 package database import ( "errors" "reflect" "git.zhangdeman.cn/zhangdeman/database/define" "gorm.io/gorm" "gorm.io/gorm/clause" ) func NewBaseDao[DatabaseDataType any, DatabaseTableColumns any]() *BaseDao[DatabaseDataType, DatabaseTableColumns] { res := &BaseDao[DatabaseDataType, DatabaseTableColumns]{} res.tableName = res.TableName() res.columns = res.Columns() res.isSetColumns = true res.columnComment = res.ColumnComment() return res } // BaseDao 基础dao层 type BaseDao[DatabaseDataType any, DatabaseTableColumns any] struct { tableName string // 继承BaseDao,需要指定表名,后续调用不用传递表名进来,如果分表了,使用SetOption设置表名, 这里的优先级更高 isSetColumns bool columns DatabaseTableColumns columnComment map[string]string } // Create 创建新的数据 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) Create(dbInstance *gorm.DB, data any, optionList ...define.SetOption) error { o := &define.Option{} for _, itemFunc := range optionList { itemFunc(o) } useTableName := b.tableName if len(o.Table) > 0 { useTableName = o.Table } return dbInstance.Table(useTableName).Create(data).Error } // Update 更新数据 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) Update(dbInstance *gorm.DB, updateData any, optionFuncList ...define.SetOption) (int64, error) { dbInstance = b.setTxCondition(dbInstance, optionFuncList...) r := dbInstance.Updates(updateData) return r.RowsAffected, r.Error } // UpdateOne 更新一条数据 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) UpdateOne(dbInstance *gorm.DB, updateData any, optionFuncList ...define.SetOption) (int64, error) { optionFuncList = append(optionFuncList, WithLimit(1, 0)) return b.Update(dbInstance, updateData, optionFuncList...) } // List 查询数据列表 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) List(dbInstance *gorm.DB, result *[]DatabaseDataType, optionFuncList ...define.SetOption) error { dbInstance = b.setTxCondition(dbInstance, optionFuncList...) return dbInstance.Find(result).Error } // ListAndTotal 同时查询数据列表和数据总数 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) ListAndTotal(dbInstance *gorm.DB, listRes *[]DatabaseDataType, totalRes *int64, disableTotal bool, optionFuncList ...define.SetOption) error { var ( err error ) dbInstance = b.setTxCondition(dbInstance, optionFuncList...) if err = dbInstance.Find(listRes).Error; nil != err { // 列表查询失败 return err } if disableTotal { // 禁用查询总数 *totalRes = int64(reflect.ValueOf(listRes).Elem().Len()) return nil } optionFuncList = append(optionFuncList, WithClearLimit()) dbInstance = b.setTxCondition(dbInstance, optionFuncList...) if err = dbInstance.Count(totalRes).Error; nil != err { return err } return nil } // Delete 删除数据, 硬删除, 对应 delete语句 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) Delete(dbInstance *gorm.DB, dataModel *DatabaseDataType, optionFuncList ...define.SetOption) (int64, error) { dbInstance = dbInstance.Model(dataModel) dbInstance = b.setTxCondition(dbInstance, optionFuncList...).Delete(dataModel) return dbInstance.RowsAffected, dbInstance.Error } // Detail 查询详情 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) Detail(dbInstance *gorm.DB, result *DatabaseDataType, optionFuncList ...define.SetOption) error { dbInstance = b.setTxCondition(dbInstance, optionFuncList...) return dbInstance.First(result).Error } // DetailByPrimaryID ... func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) DetailByPrimaryID(dbInstance *gorm.DB, result *DatabaseDataType, primaryID any, primaryKey ...string) error { primaryKeyField := "id" if len(primaryKey) > 0 { primaryKeyField = primaryKey[0] } return b.Detail(dbInstance, result, WithWhere(map[string]any{ primaryKeyField: primaryID, })) } // IsNotFound 增加结果是否为数据不存在的判断 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) IsNotFound(err error) bool { return errors.Is(err, gorm.ErrRecordNotFound) } // Count 查询数量 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) Count(dbInstance *gorm.DB, optionFuncList ...define.SetOption) (int64, error) { optionFuncList = append(optionFuncList, WithClearLimit()) dbInstance = b.setTxCondition(dbInstance, optionFuncList...) var cnt int64 return cnt, dbInstance.Count(&cnt).Error } // Tx 执行事务 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) Tx(dbInstance *gorm.DB, txFunc func(dbInstance *gorm.DB) error) error { if nil == dbInstance { return errors.New("db instance is null") } tx := b.Begin(dbInstance) if err := txFunc(tx); nil != err { _ = b.Rollback(tx) return err } return b.Commit(tx) } // Begin 开启事务 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) Begin(dbInstance *gorm.DB) *gorm.DB { return dbInstance.Begin() } // Commit 提交事务 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) Commit(db *gorm.DB) error { return db.Commit().Error } // Rollback 回滚 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) Rollback(db *gorm.DB) error { return db.Rollback().Error } // setTxCondition 设置查询条件 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) setTxCondition(inputTx *gorm.DB, optionFuncList ...define.SetOption) *gorm.DB { // 构建查询条件 o := &define.Option{} for _, fn := range optionFuncList { fn(o) } tx := inputTx.Session(&gorm.Session{ NewDB: true, Initialized: true, }) // 指定查询的表 if len(o.Table) > 0 { tx = tx.Table(o.Table) } else { tx = tx.Table(b.tableName) } if nil != o.Model { tx = tx.Model(o.Model) } // 设置 limit offset if o.Limit > 0 { tx = tx.Limit(o.Limit) if o.Offset >= 0 { tx = tx.Offset(o.Offset) } } // in 语句 if nil != o.In { tx = tx.Where(o.In) } // not in 语句 if nil != o.NotIn { for field, value := range o.NotIn { tx = tx.Where(field+" NOT IN ? ", value) } } // like 语句 if nil != o.Like { for field, value := range o.Like { tx = tx.Where(field+" LIKE ? ", "%"+value+"%") } } // NOT LIKE 语句 if nil != o.NotLike { for field, value := range o.NotLike { tx = tx.Where(field+" NOT LIKE ? ", "%"+value+"%") } } // >= if nil != o.Start { for field, value := range o.Start { tx = tx.Where(field+" >= ?", value) } } // < if nil != o.End { for field, value := range o.End { tx = tx.Where(field+" < ?", value) } } if len(o.Where) > 0 { tx = tx.Where(o.Where) } if o.ForUpdate { tx = tx.Clauses(clause.Locking{Strength: "UPDATE"}) } // between for field, betweenVal := range o.Between { tx = tx.Where("`"+field+"` BETWEEN ? AND ?", betweenVal[0], betweenVal[1]) } // not between for field, notBetweenVal := range o.NotBetween { tx = tx.Where("`"+field+"` NOT BETWEEN ? AND ?", notBetweenVal[0], notBetweenVal[1]) } // 排序 if len(o.Order) == 2 { tx = tx.Order(o.Order[0] + " " + o.Order[1]) } // or 语句 if nil != o.OR { for _, itemOr := range o.OR { orOption := &define.Option{} for _, fn := range itemOr { fn(orOption) } orSql, orBindVal := optionToSql(orOption) tx.Or(orSql, orBindVal) } } return tx } // TableName 获取数据表名 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) TableName() string { val := reflect.ValueOf(*new(DatabaseDataType)).MethodByName("TableName").Call(nil)[0] return val.Interface().(string) } // ColumnComment 表字段注释 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) ColumnComment() map[string]string { if len(b.columnComment) > 0 { return b.columnComment } val := reflect.ValueOf(new(DatabaseDataType)).MethodByName("ColumnComment").Call(nil)[0] b.columnComment = val.Interface().(map[string]string) return b.columnComment } // Columns 表字段枚举 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) Columns() DatabaseTableColumns { if b.isSetColumns { return b.columns } b.columns = reflect.ValueOf(new(DatabaseDataType)).MethodByName("Columns").Call(nil)[0].Interface().(DatabaseTableColumns) b.isSetColumns = true return b.columns } // DetailForAny 查询任意表数据详情 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) DetailForAny(tx *gorm.DB, tableName string, result any, optionList ...define.SetOption) error { tx = tx.Table(tableName) tx = b.setTxCondition(tx, optionList...) return tx.First(result).Error } // ListForAny 查询任意表数据列表 func (b *BaseDao[DatabaseDataType, DatabaseTableColumns]) ListForAny(tx *gorm.DB, tableName string, data any, optionList ...define.SetOption) error { tx = tx.Table(tableName) tx = b.setTxCondition(tx, optionList...) return tx.Find(data).Error }