2012-11-20 10 views
9

Aşağıdaki kod var: ServiceA.save() denilen ve bir özel durum oluşur olduğunda geri dönmek çalıştığındaGrails UnexpectedRollbackException oluştu: Emin Değil Neden

class ServiceA { 

    def save(Object object) { 
     if (somethingBadComesBack) { 
     throw new CustomRuntimeException(data) 
     } 
    } 
} 

class ServiceB { 

    def serviceA 

    def save(Object object) { 
     try { 
     serviceA.save(object) 
     // do more stuff if good to go 
     } catch(CustomRuntimeException e) { 
     // populate some objects with errors based on exception 
     } 
    } 
} 

class ServiceC { 

    def serviceB 

    def process(Object object) { 
     serviceB.save(object) 
     if (object.hasErrors() { 
      // do some stuff 
     }else{ 
     // do some stuff 
     } 

     def info = someMethod(object) 
     return info 
    } 
} 

class SomeController { 

    def serviceC 

    def process() { 

    def object = ..... 
    serviceC.save(object) // UnexpectedRollbackException is thrown here 

    } 
} 

, ServiceC.save() bir UnexpectedRollbackException atıyor.

try { 
    serviceC.process(object) 
}catch(UnexpectedRollbackException e) { 
    println e.getMostSpecificCause() 
} 

ve ben alıyorum:

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only 

Bunu düzeltmek için nasıl arayan nereden başlayacağınızı emin değilim

aşağıdaki yaptım.

cevap

11

İşlemi geri almak için bir çalışma zamanı istisnası kullanıyorsunuz, ancak bu hile - bir yan etkisinden yararlanıyor. Çalışma zamanı istisnaları, onları yakalamanıza gerek olmadığından otomatik olarak geri alma işlemlerini gerçekleştirir; böylece, bir tanesi atılırsa, beklenmedik ve varsayılan davranışın geri alınacağı varsayılır. Belirli beklenen çalışma zamanı istisnaları için geri alma yöntemlerini yapılandırabilirsiniz, ancak bu biraz nadirdir. Kontrol edilen istisnalar istisnaları geri almaz çünkü Java'da throws'da yakalanmaları veya bildirilmeleri gerekir, bu nedenle ya açıkça atmanız veya boğulmanız gerekir; Her iki şekilde de tekrar denemek için bir şansın vardı.

kasıtlı bir işlem geri almak doğru yolu şimdiki TransactionStatus üzerinde setRollbackOnly() aramaya ama bu bir hizmet yöntemi (o kapanmasına argüman olduğundan bu withTransaction blokta ise) doğrudan erişilemez. Ancak şu adrese ulaşmak kolay: org.springframework.transaction.interceptor.TransactionAspectSupport numaralı telefonu içe aktarın ve TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() numaralı telefonu arayın. Bu, kodunuzun yeniden işlenmesini gerektirecektir çünkü yakalanması gereken bir istisna olmayacaktır, bu nedenle TransactionAspectSupport.currentTransactionStatus().isRollbackOnly() ile geri çekildiğini kontrol etmeniz gerekir.

Grails sorunu veya standart davranış olup olmadığından emin değilim, ancak hata ayıklama yaparken 3 farklı TransactionStatus örneğiyle 3 yanıtlama çağrısı yapıldı. Sadece ilk geri alma bayrağı ayarlanmıştı, ancak ikincisi ilkinin farkındaydı ve iyiydi. Üçüncüsü yeni bir işlem olarak kabul edildi ve gördüğünüz aynı istisnayı tetikleyen oldu. Bu nedenle, bunu tersine çevirmek için, 2. ve 3. hizmet yöntemlerini şu şekilde ekledim: geri sarma bayrağını zincirlemek için

. Bu çalıştı ve ben UnexpectedRollbackException alamadım.

Bunu, kontrol edilen bir istisna ile birleştirmek daha kolay olabilir. Stacktrace'i gereksiz yere dolduracağı için hala aşırı pahalı, ancak setRollbackOnly() numaralı telefonu arayarak ve kontrol edilmiş bir istisna atarsanız, şu anda sahip olduğunuz genel genel iş akışını kullanabileceksiniz.

+0

Teşekkürler Burt. Bu yaklaşımla etrafta dolaşıp ne alabileceğimi görüyorum. Geri rapor edeceğim ... – Gregg

+2

Grails 2.3.7, bu durumu varsayılan olarak ele almak için bir özellik içerir: http://jira.grails.org/browse/GRAILS-11145 –

+0

@FlareCoder - Bunu paylaştığınız için teşekkür ederiz! Ben sadece 2.3.6 ile 2.3.7 arasındaki notlardan yükselttim ve sorun çözüldü. – arcseldon

0

default transactionality of services sizi ısırıyor gibi görünüyor ve Hizmet A'da atılan denetlenmeyen istisna, yakalandığında bile yalnızca geri alma işlemine devam ediyor.

Yukarıdaki dokümanlar, txn yayılma düzeyinin PROPAGATION_REQUIRED olduğunu söylüyor; bu, belleğimin bana sunulması durumunda, aynı işlemin C'den en çok A'ya paylaşıldığı anlamına gelir. Hizmet A'nın save yönteminin, ikincisinden otomatik geri alma işlemlerini önlemek için RuntimeException yerine kontrol edilmiş bir istisna atabilmeniz mümkün mü? Ya da hizmetlerinizdeki işlemleri devre dışı bırakırsanız, bu sizin için bir seçenek midir?