go-ozzo / ozzo-dbx

A Go (golang) package that enhances the standard database/sql package by providing powerful data retrieval methods as well as DB-agnostic query building capabilities.
MIT License
634 stars 90 forks source link

How to use transactions? #25

Closed kolkov closed 8 years ago

kolkov commented 8 years ago

Hi! How to use transactions when database queries located in different methods? Like in my case? Or I must using this in handler functions?

func (m *Actor) beforeInsert(){
    m.CreatedAt = time.Now()
    m.UpdatedAt = m.CreatedAt
    m.AvatarUrl = "/img/face.png"
    p := m.Person
    p.Create()
    m.PersonId = p.Id
}

func (m *Actor) Create() error {
    err := db.Transactional(func(tx *dbx.Tx) error {
        var err error
        m.beforeInsert()
        err = tx.Model(m).Exclude().Insert("Alias", "PersonId")
        if err != nil {
            fmt.Println("Exec err:", err.Error())
        }
        return err
    })
    return err
}
qiangxue commented 8 years ago

This is the code I wrote for a project. You can use this handler (middleware) for any request that needs transaction. GetContext() retrieves some application-specific context information from routing.Context. You will need to pass along this context through all methods that need to do DB operations (this is because transaction is something per request).

import (
    "github.com/go-ozzo/ozzo-dbx"
    "github.com/go-ozzo/ozzo-routing"
    "github.com/go-ozzo/ozzo-routing/fault"
)

// Transactional starts a DB transaction.
// If a nested handler returns an error or a panic happens, it will rollback the transaction.
// Otherwise it will commit the transaction after the nested handlers finish execution.
// By calling app.Context.SetRollback(true), you may also explicitly request to rollback the transaction.
func Transactional(db *dbx.DB) routing.Handler {
    return func(c *routing.Context) error {
        tx, err := db.Begin()
        if err != nil {
            return err
        }

        ac := GetContext(c)
        ac.SetTx(tx)

        err = fault.PanicHandler(ac.Errorf)(c)

        var e error
        if err != nil || ac.Rollback() {
            // rollback if a handler returns an error or rollback is explicitly requested
            e = tx.Rollback()
        } else {
            e = tx.Commit()
        }

        if e != nil {
            if err == nil {
                // the error will be logged by an error handler
                return e
            }
            // log the tx error only
            ac.Error(e)
        }

        return err
    }
}
qiangxue commented 8 years ago

For your reference, I just published this repo: https://github.com/qiangxue/golang-restful-starter-kit

kolkov commented 8 years ago

Great thanks! Very interesting, with DAO layer and errors handling! ))