2013-04-24 16 views
21

Sử dụng cơ sở dữ liệu/sql và gói trình điều khiển và Tx, nó không thể xuất hiện để phát hiện xem giao dịch đã được cam kết hay cuộn lại mà không thử một giao dịch khác và nhận lỗi và sau đó kiểm tra lỗi để xác định loại lỗi. Tôi muốn có thể xác định từ đối tượng Tx dù có cam kết hay không. Chắc chắn, tôi có thể xác định và thiết lập một biến khác trong hàm sử dụng Tx, nhưng tôi có khá nhiều biến, và đó là lần 2 mỗi lần (biến và gán). Tôi cũng có một chức năng trì hoãn để thực hiện một Rollback nếu cần, và nó cần phải được chuyển qua biến bool.

Có thể chấp nhận để đặt biến Tx thành không sau một Cam kết hoặc Rollback, và GC sẽ phục hồi bất kỳ bộ nhớ nào, hoặc là không có, hoặc là có một lựa chọn tốt hơn?cơ sở dữ liệu/sql Tx - phát hiện Cam kết hoặc Rollback

+1

Không chắc nếu tôi hiểu được vấn đề. Bạn phải kết thúc một giao dịch với Commit hoặc Rollback để bạn biết bạn đã làm gì nhưng bạn không muốn nhớ điều này trong một biến phụ? Bạn có thể quấn Tx và bool trong RememberingTx của riêng bạn, điều này sẽ làm giảm số lượng dòng một chút. Về câu hỏi GC: Không quan trọng nếu bạn đặt thành 0 hoặc không: Bộ nhớ sẽ được xác nhận lại khi không có tham chiếu nào được để lại. Vì vậy: Có, bạn có thể có 'var tx * Tx; snip; nếu cond {tx.Commit; tx = nil} else {tx.Rollback}; snip; nếu tx == nil {được commit} else {was rollbacked} 'nhưng nó cảm thấy xấu xí. – Volker

+0

Đó là loại của nó về, nhưng có một func hoãn lại mà không một rollback nếu Tx không phải là không. Khi một giao dịch được cam kết, Tx không thể được sử dụng anyway, vì vậy tôi có kế hoạch để thiết lập nó để nil. Nó không phải là khá, tuy nhiên cố gắng một rollback và thử nghiệm các thông báo lỗi không phải là khá hoặc. Vấn đề là AFAIK không có cách nào để kiểm tra xem giao dịch có được thực hiện từ Tx hay không. Tôi không chắc tại sao nó lại được thực hiện theo cách đó, có lẽ là hiệu suất. –

Trả lời

73

Tại sao bạn thậm chí cần thực hiện việc này? Chức năng gọi Begin() cũng phải gọi số Commit() hoặc Rollback() và trả lại lỗi thích hợp.

Ví dụ, đoạn mã này thực hiện một cam kết hoặc rollback tuỳ thuộc vào việc một lỗi được trả về:

func (s Service) DoSomething() (err error) { 
    tx, err := s.db.Begin() 
    if err != nil { 
     return 
    } 
    defer func() { 
     if err != nil { 
      tx.Rollback() 
      return 
     } 
     err = tx.Commit() 
    }() 
    if _, err = tx.Exec(...); err != nil { 
     return 
    } 
    if _, err = tx.Exec(...); err != nil { 
     return 
    } 
    // ... 
    return 
} 

Chú ý làm thế nào tôi đang kiểm tra error để xem có hay không tôi nên cam kết hoặc rollback. Ví dụ trên không, tuy nhiên, xử lý các vấn đề.

Tôi không thích thực hiện logic cam kết/rollback trên mọi thường trình cơ sở dữ liệu, vì vậy tôi thường quấn chúng trong trình xử lý giao dịch. Một cái gì đó dọc theo dòng này:

func Transact(db *sql.DB, txFunc func(*sql.Tx) error) (err error) { 
    tx, err := db.Begin() 
    if err != nil { 
     return 
    } 
    defer func() { 
     if p := recover(); p != nil { 
      tx.Rollback() 
      panic(p) // re-throw panic after Rollback 
     } else if err != nil { 
      tx.Rollback() 
     } else { 
      err = tx.Commit() 
     } 
    }() 
    err = txFunc(tx) 
    return err 
} 

Điều này cho phép tôi làm điều này thay vì:

func (s Service) DoSomething() error { 
    return Transact(s.db, func (tx *sql.Tx) error { 
     if _, err := tx.Exec(...); err != nil { 
      return err 
     } 
     if _, err := tx.Exec(...); err != nil { 
      return err 
     } 
    }) 
} 

Chú ý rằng nếu bất cứ điều gì bên trong giao dịch của tôi hoảng sợ nó tự động xử lý bởi bộ xử lý giao dịch.

Khi triển khai thực tế, tôi chuyển giao diện thay vì * sql.Tx để ngăn các cuộc gọi không mong muốn đến Commit() hoặc Rollback().

Dưới đây là một đoạn mã đơn giản để chứng minh như thế nào defer công trình (in 4, không phải 5):

package main 

func test() (i int) { 
    defer func() { 
     i = 4 
    }() 
    return 5 
} 

func main() { 
    println(test()) 
} 

http://play.golang.org/p/0OinYDWFlx

+0

câu trả lời hay! Tôi nghĩ rằng bạn đã bỏ lỡ một "trở lại nil" vào cuối thực hiện doSomething() thứ hai của bạn. – splinter123

+0

Luke, làm thế nào và khi nào sai được đánh giá? Theo tài liệu, "err" sẽ nhận được giá trị của nó khi nó được khai báo lần đầu tiên trong cuộc gọi trì hoãn.Vì vậy, điều này thực sự là một chút bối rối đối với tôi, kể từ khi giá trị cho "err", như được sử dụng trong trì hoãn, dường như thay đổi. – mirage

+0

err được khai báo trước khi trì hoãn thông qua: = (dấu hai chấm bằng nhau). Anon func bắt giữ nó. Trì hoãn được gọi ngay trước khi giá trị được trả về. Điều đó cho phép nó được thiết lập. Khi một hoảng sợ xảy ra nó được phục hồi, biến thành một lỗi, sau đó quay trở lại. Nếu một lỗi xảy ra theo bất kỳ cách nào xảy ra thì cuộn ngược lại xảy ra. Cuối cùng, một cam kết sẽ xảy ra nếu không có lỗi và err (hiện tại nil) được đặt thành Cam kết trả về giá trị trong trường hợp lỗi. – Luke

Các vấn đề liên quan