github.com/unionj-cloud/go-doudou/v2@v2.3.5/toolkit/sqlext/wrapper/wrapper.go (about) 1 package wrapper 2 3 import ( 4 "context" 5 "database/sql" 6 "github.com/jmoiron/sqlx" 7 "github.com/lithammer/shortuuid/v4" 8 "github.com/pkg/errors" 9 "github.com/unionj-cloud/go-doudou/v2/toolkit/cache" 10 "github.com/unionj-cloud/go-doudou/v2/toolkit/caller" 11 "github.com/unionj-cloud/go-doudou/v2/toolkit/sqlext/logger" 12 "time" 13 ) 14 15 // DB wraps sqlx.Tx and sqlx.DB https://github.com/jmoiron/sqlx/issues/344#issuecomment-318372779 16 type DB interface { 17 Querier 18 BeginTxx(ctx context.Context, opts *sql.TxOptions) (GddTx, error) 19 Close() error 20 } 21 22 // Tx transaction 23 type Tx interface { 24 Querier 25 Commit() error 26 Rollback() error 27 } 28 29 // Querier common operations for sqlx.Tx and sqlx.DB 30 type Querier interface { 31 NamedExecContext(ctx context.Context, query string, arg interface{}) (sql.Result, error) 32 ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) 33 GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error 34 Rebind(query string) string 35 BindNamed(query string, arg interface{}) (string, []interface{}, error) 36 SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error 37 //RefreshCache() 38 } 39 40 // GddDB wraps sqlx.DB 41 type GddDB struct { 42 *sqlx.DB 43 logger logger.SqlLogger 44 cacheStore *cache.Cache 45 redisKeyTTL time.Duration 46 } 47 48 type GddDBOption func(*GddDB) 49 50 func WithLogger(logger logger.SqlLogger) GddDBOption { 51 return func(g *GddDB) { 52 g.logger = logger 53 } 54 } 55 56 func WithCache(store *cache.Cache) GddDBOption { 57 return func(g *GddDB) { 58 g.cacheStore = store 59 } 60 } 61 62 func WithRedisKeyTTL(ttl time.Duration) GddDBOption { 63 return func(g *GddDB) { 64 g.redisKeyTTL = ttl 65 } 66 } 67 68 func NewGddDB(db *sqlx.DB, options ...GddDBOption) GddDB { 69 g := &GddDB{ 70 DB: db, 71 logger: logger.NewSqlLogger(), 72 redisKeyTTL: time.Hour, 73 } 74 for _, opt := range options { 75 opt(g) 76 } 77 return *g 78 } 79 80 func (g GddDB) NamedExecContext(ctx context.Context, query string, arg interface{}) (ret sql.Result, err error) { 81 var ( 82 q string 83 args []interface{} 84 ) 85 defer func() { 86 g.logger.LogWithErr(ctx, err, nil, q, args...) 87 }() 88 q, args, err = g.DB.BindNamed(query, arg) 89 err = errors.Wrap(err, caller.NewCaller().String()) 90 if err != nil { 91 return nil, err 92 } 93 ret, err = g.DB.NamedExecContext(ctx, query, arg) 94 err = errors.Wrap(err, caller.NewCaller().String()) 95 return 96 } 97 98 func (g GddDB) ExecContext(ctx context.Context, query string, args ...interface{}) (ret sql.Result, err error) { 99 defer func() { 100 g.logger.LogWithErr(ctx, err, nil, query, args...) 101 }() 102 ret, err = g.DB.ExecContext(ctx, query, args...) 103 err = errors.Wrap(err, caller.NewCaller().String()) 104 return 105 } 106 107 func (g GddDB) GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) (err error) { 108 hit := true 109 defer func() { 110 g.logger.LogWithErr(ctx, err, &hit, query, args...) 111 }() 112 if g.cacheStore != nil { 113 err = g.cacheStore.Once(&cache.Item{ 114 Key: shortuuid.NewWithNamespace(logger.PopulatedSql(query, args...)), 115 Value: dest, 116 TTL: g.redisKeyTTL, 117 Do: func(*cache.Item) (interface{}, error) { 118 hit = false 119 err = g.DB.GetContext(ctx, dest, query, args...) 120 err = errors.Wrap(err, caller.NewCaller().String()) 121 return dest, err 122 }, 123 }) 124 return 125 } 126 hit = false 127 err = g.DB.GetContext(ctx, dest, query, args...) 128 err = errors.Wrap(err, caller.NewCaller().String()) 129 return 130 } 131 132 func (g GddDB) SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) (err error) { 133 hit := true 134 defer func() { 135 g.logger.LogWithErr(ctx, err, &hit, query, args...) 136 }() 137 if g.cacheStore != nil { 138 err = g.cacheStore.Once(&cache.Item{ 139 Key: shortuuid.NewWithNamespace(logger.PopulatedSql(query, args...)), 140 Value: dest, 141 TTL: g.redisKeyTTL, 142 Do: func(*cache.Item) (interface{}, error) { 143 hit = false 144 err = g.DB.SelectContext(ctx, dest, query, args...) 145 err = errors.Wrap(err, caller.NewCaller().String()) 146 return dest, err 147 }, 148 }) 149 return 150 } 151 hit = false 152 err = g.DB.SelectContext(ctx, dest, query, args...) 153 err = errors.Wrap(err, caller.NewCaller().String()) 154 return 155 } 156 157 // BeginTxx begins a transaction 158 func (g GddDB) BeginTxx(ctx context.Context, opts *sql.TxOptions) (GddTx, error) { 159 tx, err := g.DB.BeginTxx(ctx, opts) 160 if err != nil { 161 return GddTx{}, err 162 } 163 return GddTx{tx, g.logger, g.cacheStore, g.redisKeyTTL}, nil 164 } 165 166 // GddTx wraps sqlx.Tx 167 type GddTx struct { 168 *sqlx.Tx 169 logger logger.SqlLogger 170 cacheStore *cache.Cache 171 redisKeyTTL time.Duration 172 } 173 174 func (g GddTx) NamedExecContext(ctx context.Context, query string, arg interface{}) (ret sql.Result, err error) { 175 var ( 176 q string 177 args []interface{} 178 ) 179 defer func() { 180 g.logger.LogWithErr(ctx, err, nil, q, args...) 181 }() 182 q, args, err = g.Tx.BindNamed(query, arg) 183 err = errors.Wrap(err, caller.NewCaller().String()) 184 if err != nil { 185 return nil, err 186 } 187 ret, err = g.Tx.NamedExecContext(ctx, query, arg) 188 err = errors.Wrap(err, caller.NewCaller().String()) 189 return 190 } 191 192 func (g GddTx) ExecContext(ctx context.Context, query string, args ...interface{}) (ret sql.Result, err error) { 193 defer func() { 194 g.logger.LogWithErr(ctx, err, nil, query, args...) 195 }() 196 ret, err = g.Tx.ExecContext(ctx, query, args...) 197 err = errors.Wrap(err, caller.NewCaller().String()) 198 return 199 } 200 201 func (g GddTx) GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) (err error) { 202 hit := true 203 defer func() { 204 g.logger.LogWithErr(ctx, err, &hit, query, args...) 205 }() 206 if g.cacheStore != nil { 207 err = g.cacheStore.Once(&cache.Item{ 208 Key: shortuuid.NewWithNamespace(logger.PopulatedSql(query, args...)), 209 Value: dest, 210 TTL: g.redisKeyTTL, 211 Do: func(*cache.Item) (interface{}, error) { 212 hit = false 213 err = g.Tx.GetContext(ctx, dest, query, args...) 214 err = errors.Wrap(err, caller.NewCaller().String()) 215 return dest, err 216 }, 217 }) 218 return 219 } 220 hit = false 221 err = g.Tx.GetContext(ctx, dest, query, args...) 222 err = errors.Wrap(err, caller.NewCaller().String()) 223 return 224 } 225 226 func (g GddTx) SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) (err error) { 227 hit := true 228 defer func() { 229 g.logger.LogWithErr(ctx, err, &hit, query, args...) 230 }() 231 if g.cacheStore != nil { 232 err = g.cacheStore.Once(&cache.Item{ 233 Key: shortuuid.NewWithNamespace(logger.PopulatedSql(query, args...)), 234 Value: dest, 235 TTL: g.redisKeyTTL, 236 Do: func(*cache.Item) (interface{}, error) { 237 hit = false 238 err = g.Tx.SelectContext(ctx, dest, query, args...) 239 err = errors.Wrap(err, caller.NewCaller().String()) 240 return dest, err 241 }, 242 }) 243 return 244 } 245 hit = false 246 err = g.Tx.SelectContext(ctx, dest, query, args...) 247 err = errors.Wrap(err, caller.NewCaller().String()) 248 return 249 }