2013-04-24 18 views
21

Veritabanı/sql ve sürücü paketlerini ve Tx'i kullanarak, bir işlemin başka bir işlem yapmadan mı, yoksa geri alınıp alınmayacağını ve bir hata olarak algılandığını algılamayabilir. Sonuç ve daha sonra hatanın türünü belirlemek için hatayı incelemek. Tx nesnesinden kararlı olup olmadığına karar vermek istiyorum. Elbette, Tx kullanan fonksiyonda bir başka değişken tanımlayabilir ve ayarlayabilirim, fakat bende bunların bir numarası var ve her seferinde 2 kere (değişken ve atama). Ayrıca gerekirse Geri Alma işlemini yapmak için ertelenmiş bir işlevim var ve boole değişkenini geçirmesi gerekiyor.

Tx değişkeninin bir Kombinasyon veya Geri Alma işleminden sonra sıfırlanması ve GC'nin herhangi bir belleği kurtarması mı, yoksa hayır yok mu yoksa daha iyi bir alternatif mi olduğu kabul edilebilir mi?database/sql Tx - Tespit veya Geri Alma Algılama

+1

Sorunu anlamak eğer emin değilim. Bir işlemi Commit veya Rollback ile bitirmelisiniz, böylece ne yaptığınızı biliyorsunuz ama bunu fazladan bir değişkende hatırlamak istemiyorsunuz? Tx ve bool'u kendi RememberingTx'inize sarabilirsiniz, bu durum satır sayısını biraz azaltacaktır. GC sorusu ile ilgili olarak: Nil olsun ya da olmasın farketmez: Bir referans bırakılmadığında bellek geri alınacaktır. Yani: Evet, var tx * Tx'e sahip olabilirsiniz; makasla kesme; eğer koşul varsa {tx.Commit; tx = nil} else {tx.Rollback}; makasla kesme; tx == nil {commited oldu} başka {rollbacked} oldu ama çirkin hissediyor. – Volker

+0

Bu tür bir şeydir, ancak Tx sıfır değilse geri alma yapan ertelenmiş bir fon var. Bir işlem gerçekleştirildikten sonra, Tx yine de kullanılamaz, bu yüzden onu nil olarak ayarlamayı planlıyorum. Güzel değil, ancak geri alma girişiminde bulunmak ve hata mesajını test etmek de hoş değil. Sorun şu ki AFAIK, işlemin Tx'den "tamamlandı" olup olmadığını test etmenin bir yolu yok. Bu şekilde neden yapıldığından emin değilim, belki de performans. –

cevap

73

Bunu neden yapmak zorunda kaldınız? Begin() numaralı çağrı, Commit() veya Rollback() numaralı telefonu çağırmalı ve uygun bir hata döndürmelidir. Ben kaydetme veya geri gerekip gerekmediğini görmek için error kontrol ediyorum nasıl

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 
} 

Bildirimi:

Örneğin, bu kod, bir kaydetme veya geri bir hata döndürdü olup olmamasına bağlı olarak yapar. Yukarıdaki örnek, ancak panik işlemez.

Her veritabanı rutininde commit/rollback mantığı yapmayı sevmiyorum, bu yüzden bunları genellikle bir işlem işleyicisine sardım. Bunun şey satırları: Bu bana yerine bunu sağlar

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 
} 

: my işlem içinde birşey panikler eğer otomatik işlem işleyici tarafından işlenir oldu o

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 
     } 
    }) 
} 

dikkat edin.

Gerçek uygulamamda, istenmeyen aramaları Commit() veya Rollback() önlemek için * sql.Tx yerine bir arabirim iletiyorum.

İşte göstermek için basit bir parçacık İşte nasıl defer eserleri (baskılar 4 değil, 5):

package main 

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

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

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

+0

güzel cevap! Sanırım ikinci doSomething() uygulamanızın sonuna doğru bir "dönüş sıfır" kaçırdınız. – splinter123

+0

Luke, err nasıl ve ne zaman değerlendirilir? Dokümantasyona göre "err", erteleme çağrısında ilk ilan edildiğinde değeri almalıdır.Yani bu aslında benim için biraz kafa karıştırıcı, çünkü ertelemede kullanılan "err" değeri değişiyor gibi görünüyor. – mirage

+0

err, ertelemeden önce: = (kolon eşit) ile bildirilir. Anon func onu yakalar. Erteleme değeri döndürülmeden hemen önce çağrılır. Bu ayarlanmasına izin veriyor. Bir panik meydana geldiğinde, kurtarılır, bir hataya dönüştürülür, daha sonra iade edilir. Bir hata herhangi bir şekilde olursa, geri dönüş gerçekleşir. Sonunda, bir hata yoksa ve hata (hata) durumunda err (şu anda sıfır) Komutlar döndürme değeri olarak ayarlanmışsa bir taahhüt gerçekleşir. – Luke