Am learning about implementing DB transactions in golang and for that, the instructor was creating a query object which will be having access to all the CRUD methods. But he also have a object which is having the Query as a data member in the struct. Why cant we use that?
type Store struct {
*Queries
db *sql.DB // Required for creating transactions
}
// Method to generate a store object
func NewStore(db *sql.DB) *Store {
return &Store{
db: db,
Queries: New(db),
}
}
// executeTx execute the function given to it
func (store *Store) executeTx(ctx context.Context, fn func(*Queries) error) error {
// Initiate the transaction
tx, err := store.db.BeginTx(ctx, nil)
if err != nil {
return err
}
// The New() is decalred for DBTX inteface and both sql.DB and sql.Tx satisfy them
// This will return a query object which can be used for the execution of the function.
// We can also use the query object of the store too but thats just for composition
q := New(tx)
err = fn(q)
if err != nil {
rbError := tx.Rollback()
if rbError != nil {
return fmt.Errorf("tx err: %v, rollback err: %v", err, rbError)
}
return err
}
return tx.Commit()
}
Here, the Queries is already a struct which is declared as:
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
db: tx,
}
}
So my question is,
q := New(tx) err = fn(q)
being used but can we use it like this too?
err = fn(store.Queries)
I tried executing the code with the Query from the store struct and it works but will there be any difference between the two.