web-dev-qa-db-fra.com

Rollback sur chaque exception vérifiée, chaque fois que je dis @Transactional

Étant donné que le programmeur est obligé d'attraper toute l'exception vérifiée, je dois jeter une exception vérifiée en cas de problème. Je voudrais renter sur l'une de ces attentes. Écriture rollbackFor=Exception.classon Chaque annotation @Transactional est très sujette à l'erreur, alors je voudrais dire printemps, que: "Chaque fois que j'écris @Transactional, je veux dire @Transactional(rollbackFor=Exception.class)".

Je sais que je pourrais créer une annotation personnalisée, mais cela semble non naturel.

Il y a donc un moyen de dire printemps comment il devrait gérer des excpètes vérifiés globalement?

37
pihentagy

Annotations de raccourci personnalisées

Je sais que je pourrais créer une annotation personnalisée, mais cela semble non naturel.

Non, c'est exactement le cas d'utilisation pour une annotation personnalisée. Voici une citation de Annotations de raccourci personnalisées dans la référence de printemps:

Si vous trouvez que vous utilisez à plusieurs reprises des mêmes attributs avec @TransAderal sur de nombreuses méthodes différentes, le support méta-annotation de Spring vous permet de définir des annotations de raccourci personnalisées pour vos cas d'utilisation spécifiques.

Code

Et voici un exemple d'annotation pour votre cas d'utilisation:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(rollbackFor=Exception.class)
public @interface MyAnnotation {
}

Annotez maintenant vos services et/ou méthodes avec @MyAnnotation (vous penserez à un meilleur nom). Ceci est une fonctionnalité bien testée qui fonctionne par défaut. Pourquoi réinventer la roue?

57
Sean Patrick Floyd

L'approche avec une annotation personnalisée semble bonne et directe, mais si vous ne voulez réellement pas l'utiliser, vous pouvez créer une personnalisation TransactionAttributeSource pour modifier l'interprétation de @Transactional:

public class RollbackForAllAnnotationTransactionAttributeSource 
    extends AnnotationTransactionAttributeSource {
    @Override
    protected TransactionAttribute determineTransactionAttribute(
            AnnotatedElement ae) {
        TransactionAttribute target = super.determineTransactionAttribute(ae);
        if (target == null) return null;
        else return new DelegatingTransactionAttribute(target) {
            @Override
            public boolean rollbackOn(Throwable ex) {
                return true;
            }
        };
    }
}

Et au lieu de <tx:annotation-driven/> Vous le configurez manuellement comme suit (voir la source de AnnotationDrivenBeanDefinitionParser):

<bean id = "txAttributeSource"
    class = "RollbackForAllAnnotationTransactionAttributeSource" />

<bean id = "txInterceptor"
    class = "org.springframework.transaction.interceptor.TransactionInterceptor">
    <property name = "transactionManagerBeanName" value = "transactionManager" />
    <property name = "transactionAttributeSource" ref = "txAttributeSource" />
</bean>

<bean
    class = "org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor">
    <property name="transactionAttributeSource" ref = "txAttributeSource" />
    <property name = "adviceBeanName" value = "txInterceptor" />
</bean>

Aussi vous avez besoin <aop:config/> ou <aop:aspectj-autoproxy />.

Notez cependant que de telles substitutions peuvent être déroutantes pour les autres développeurs qui s'attendent à ce qu'un comportement normal de @Transactional.

17
axtavt

On dirait que vous pouvez configurer un conseil transactionnel basé sur un nom de méthode comme celui-ci: (à partir de The Spring 2.0 Docs )

Les types d'exception marquent exactement une transaction pour la restauration peuvent être configurés. Retrouvez ci-dessous un extrait de configuration XML qui démontre comment on configurait la restauration d'un type d'exception vérifié et spécifique à l'application.

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
      <tx:method name="get*" read-only="false" rollback-for="NoProductInStockException"/>
      <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
3
oksayt

Vous pouvez:

  • attrapez et enveloppez l'exception vérifiée dans une exception non cochée - throw new RuntimeException(checkedException)
  • créez un proxy autour de la méthode, en utilisant MethodInterceptor (ou @AroundInvoke, ou tout autre moyen de créer des aspects au printemps), déclarez-le à être exécuté avant la <tx:annotation-driven /> en précisant order="1" et order="2" et attraper et envelopper là-bas.
3
Bozho