@Transactional ek açıklamayı nereye koymalıyım: bir arabirim tanımında mı yoksa bir uygulama sınıfında mı?


91

Koddaki başlıktaki soru:

@Transactional (readonly = true)
public interface FooService {
   void doSmth ();
}


public class FooServiceImpl implements FooService {
   ...
}

vs

public interface FooService {
   void doSmth ();
}

@Transactional (readonly = true)
public class FooServiceImpl implements FooService {
   ...
}

Yanıtlar:


121

Gönderen http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

Spring ekibinin önerisi, @Transactionalarayüzlere açıklama eklemek yerine, somut sınıflara yalnızca açıklamayla açıklama eklemenizdir. @TransactionalAçıklamayı kesinlikle bir arabirime (veya bir arabirim yöntemine) yerleştirebilirsiniz, ancak bu yalnızca arabirim tabanlı proxy'ler kullanıyorsanız beklediğiniz gibi çalışacaktır. Ek açıklamaların miras alınmadığı gerçeği, sınıf tabanlı proxy'ler kullanıyorsanız işlem ayarlarının sınıf tabanlı proxy altyapısı tarafından tanınmayacağı ve nesnenin işlemsel bir proxy'ye sarılmayacağı anlamına gelir (kesinlikle kötü olur ) . Bu yüzden lütfen Bahar ekibinin tavsiyesine uyun ve sadece somut sınıfları (ve somut sınıfların yöntemlerini) @Transactionalaçıklama ile birlikte not edin.

Not: Bu mekanizma proxy'lere dayandığından, yalnızca proxy aracılığıyla gelen 'harici' yöntem çağrıları durdurulacaktır. Bu, 'kendi kendine çağırmanın', yani hedef nesne içinde hedef nesnenin başka bir yöntemini çağıran bir yöntemin, çağrılan yöntem ile işaretlenmiş olsa bile çalışma zamanında gerçek bir işleme yol açmayacağı anlamına gelir @Transactional!

(İlk cümleye vurgu, orijinalinden başka vurgular eklenmiştir.)


Big +1 BTW, alıntıyı bir alıntı yapma özgürlüğünü aldım, böylece insanların kafası karışmasın ve italik ve benzerlerini orijinalden geri ekledim.
TJ Crowder

Bu bildirimi daha önce Spring docu'da okudum, ancak yine de işlem ayarlarının neden sınıf tabanlı proxy altyapısı tarafından tanınmayacağını anlayamıyorum ? Ben şahsen bunun sadece Spring uygulama sınırlaması olduğunu düşünüyorum, temelde yatan proxy altyapısı sorunu değil.
dma_k

Spring kaynaklarına bakıldığında, JdkDynamicAopProxyher bir yöntem çağrısında tüm fasulye danışmanlarından geçtiği öğrenilebilir (ayrıca bkz. DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice()), Bildirime dayalı işlem kurulumu durumunda bunu içerir BeanFactoryTransactionAttributeSourceAdvisor. Sırasıyla TransactionAttributeSourcePointcut#matches(), işlemle ilgili bilgileri toplaması gereken çağrılır. Hedef sınıfa geçilir ve bu sınıfın uyguladığı tüm arabirimleri her zaman geçebilir. Şimdi: Bu neden güvenilir bir şekilde çalışmıyor?
dma_k

2
@dma_k - Java çalışma zamanı, yalnızca üst sınıflardan miras alınan sınıf açıklamalarına veya hiçbir miras içermeyen yöntem açıklamalarına doğrudan erişim sağlar. Yani Pointcut.matches () aslında tüm arayüzleri yinelemeli olarak dolaşmalı, yansıma ile "gerçek" geçersiz kılınmış yöntemi bulmalı, köprü yöntemlerini kullanmalı, aşırı yükleme, vararjlar, jenerikler, proxy'ler ve tabii ki bunu hızlı yapmalıdır . Öyleyse elmas kalıtımıyla uğraşmak zorundasınız - muhtemelen birçok kalıtsal @Transactionalek açıklamadan hangisi geçerlidir? Yani, hepsi mümkün olsa da , Spring'i suçlamıyorum. jira.springsource.org/browse/SPR-975
Peter Davis

9

Bunları arayüze koyabilirsiniz, ancak bazı durumlarda işlemlerin gerçekleşmeyebileceği konusunda uyarabilirsiniz. Bahar belgelerinden Secion 10.5.6'daki ikinci ipucuna bakın :

Spring, açıklama arabirimlerinin aksine, yalnızca somut sınıfları (ve somut sınıfların yöntemlerini) @Transactional açıklama ile açıklama eklemenizi önerir. @Transactional ek açıklamasını bir arabirime (veya bir arabirim yöntemine) kesinlikle yerleştirebilirsiniz, ancak bu yalnızca arabirim tabanlı proxy'ler kullanıyorsanız beklediğiniz gibi çalışır. Java notlarının arabirimlerden miras alınmaması gerçeği, sınıf tabanlı proxy'ler (proxy-target-class = "true") veya dokuma tabanlı yön (mode = "boyj") kullanıyorsanız, işlem ayarlarının proxy ve dokuma altyapısı tarafından tanınmaz ve nesne işlemsel bir proxy'ye sarılmaz, bu kesinlikle kötü olur.

Bu nedenle onları uygulamaya koymanızı tavsiye ederim.

Ayrıca bana göre işlemler bir uygulama detayı gibi görünüyor, bu yüzden uygulama sınıfında olması gerekiyor. İşlemsel olması gerekmeyen günlük kaydı veya test uygulamaları (taklitler) için sarmalayıcı uygulamalarına sahip olduğunuzu hayal edin.


9

Spring'in tavsiyesi , bir arayüz yerine somut uygulamalara açıklama eklemenizdir. Ek açıklamayı bir arayüzde kullanmak yanlış değildir, sadece bu özelliği kötüye kullanmak ve yanlışlıkla @Transaction bildiriminizi atlamak mümkündür.

Bir arayüzde işlemsel bir şeyi işaretlediyseniz ve daha sonra baharın başka bir yerindeki uygulama sınıflarından birine başvurduysanız, baharın oluşturduğu nesnenin @Transactional ek açıklamasına saygı göstermeyeceği pek açık değildir.

Pratikte şuna benzer:

public class MyClass implements MyInterface { 

    private int x;

    public void doSomethingNonTx() {}

    @Transactional
    public void toSomethingTx() {}

}

1

@Transactional'ı somut sınıflarda desteklemek:

Genel olarak 3 bölümde bir çözüm oluşturmayı tercih ediyorum: bir API, bir Uygulama ve bir Web (gerekirse). Bağımlılıkları en aza indirerek API'yi olabildiğince hafif / basit / POJO olarak tutmak için elimden gelenin en iyisini yapmaya çalışıyorum. API'leri çok paylaşmanız gereken dağıtılmış / entegre bir ortamda oynuyorsanız özellikle önemlidir.

@Transactional koymak, IMHO'nun etkili olmadığı API bölümünde Spring kitaplıklarını gerektirir. Bu yüzden, işlemin devam ettiği Uygulamaya eklemeyi tercih ediyorum.


0

IFC'nizin öngörülebilir tüm uygulayıcıları TX verilerini önemsediği sürece arayüze yerleştirmek iyidir (işlemler sadece veritabanlarının uğraştığı sorunlar değildir). Eğer yöntem TX ile ilgilenmiyorsa (ama onu Hibernate için oraya koymanız gerekiyorsa veya her neyse), impl.

Ayrıca, @Transactionalarayüzdeki yöntemleri yerleştirmek biraz daha iyi olabilir :

public interface FooService {
    @Transactional(readOnly = true)
    void doSmth();
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.