Question, j'ai ajouté des annotations au service comme d'habitude
@Transactional
, pourquoi est-ce que je trouve encore des incohérences de données lors de l'interrogation de la base de données ? Pensez-y, il faut que la transaction n'ait pas fonctionné, et les données ? n'a pas été renvoyé lorsqu'un jet d'exception s'est produit. J'ai donc testé le code concerné et j'ai découvert que j'étais tombé dans deux pièges. Il s'agissait bien d'une incohérence des données causée par la non-annulation de la transaction. Résumons ci-dessous les enseignements tirés :
Gestion programmatique des transactions
Rarement utilisé dans les applications réelles
Gérer manuellement les transactions à l'aide de TransactionTemplate
Gestion déclarative des transactions
Recommandé pour le développement (intrusion minimale de code)
Les transactions déclaratives de Spring sont implémentées via AOP
Maîtriser principalement la gestion déclarative des transactions.
Pour résumer les deux raisons pour lesquelles les transactions ne sont pas annulées, l'une est l'appel de méthode interne de la classe Service et l'autre est l'essai ... attraper l'exception.
Il existe probablement une méthode A dans Service, qui appellera la méthode B en interne. La méthode A n'a pas de gestion des transactions et la méthode B utilise des transactions déclaratives. Déclarez l'annotation Transactionnelle sur la méthode de gestion des transactions. L'exemple de code est le suivant :
@Servicepublic class RabbitServiceImpl implements RabbitService { @Autowiredprivate RabbitDao rabbitDao; @Autowiredprivate TortoiseDao tortoiseDao; @Overridepublic Rabbit methodA(String name){return methodB(name); } @Transactional(propagation = Propagation.REQUIRED)public boolean methodB(String name){ rabbitDao.insertRabbit(name); tortoiseDao.insertTortoise(name);return true; } }
Le code du test unitaire est le suivant :
public class RabbitServiceImplTest { @Autowiredprivate RabbitService rabbitService;// 事务未开启 @Testpublic void testA(){ rabbitService.methodA("rabbit"); }// 事务开启 @Testpublic void testB(){ rabbitService.methodB("rabbit"); } }
Comme vous pouvez le voir sur Dans la section précédente, les transactions déclaratives sont implémentées via un proxy dynamique AOP, qui générera une classe proxy pour la gestion des transactions, mais la classe cible (service) elle-même ne peut pas percevoir l'existence de la classe proxy.
Pour les méthodes annotées avec @Transactional, lors de l'appel de la méthode de la classe proxy, la transaction sera lancée via l'intercepteur TransactionInterceptor, puis la méthode de la classe cible sera enfin appelée une fois l'appel terminé. terminé, le TransactionInterceptor soumettra ou annulera une transaction, le processus général est le suivant :
En résumé, lors de l'appel de la méthode B dans la méthode A, c'est en fait via la référence de "this", c'est-à-dire appeler directement la méthode de la classe cible au lieu de la classe proxy obtenue via le contexte Spring, donc la transaction ne sera pas démarrée.
gère l'exception de base de données dans un élément de logique métier, utilise la clause try...catch pour capturer l'exception et lève une exception personnalisée, ceci La situation n'a pas permis d'annuler la transaction. L'exemple de code est le suivant :
@Transactional(propagation = Propagation.REQUIRED)public boolean methodB(String name) throws BizException {try { rabbitDao.insertRabbit(name); tortoiseDao.insertTortoise(name); } catch (Exception e) {throw new BizException(ReturnCode.EXCEPTION.code, ReturnCode.EXCEPTION.msg); }return true; }
La définition de BizException est la suivante :
public class BizException extends Exception {// 自定义异常}
La transaction déclarative dans le code ci-dessus ne sera pas annulée lorsqu'une exception se produit. Bien que j'ai détecté l'exception dans le code, j'ai également lancé une exception en même temps. Pourquoi la transaction n'a-t-elle pas été annulée ? J'ai deviné que le type d'exception était erroné, alors j'ai commencé à chercher la raison, j'ai parcouru la documentation officielle de Spring et j'ai trouvé la réponse. Ce qui suit est traduit du site officiel de Spring.
La section précédente a présenté comment configurer les transactions Spring, qui sont généralement définies dans le code de la couche Service de votre application. Cette section présentera comment contrôler. restauration des transactions dans les transactions déclaratives simples et populaires.
La méthode d'annulation de transaction recommandée dans le cadre de transaction Spring FrameWork consiste à lever une exception dans le contexte de transaction en cours d'exécution. Si l'exception n'est pas gérée, le code du framework de transaction de Spring FrameWork interceptera toute exception non gérée lorsque l'exception est levée dans la pile d'appels, puis décidera s'il convient de marquer la transaction pour restauration.
Dans la configuration par défaut, le code du framework de transaction de Spring FrameWork marquera uniquement les transactions avec runtime, unchecked
exceptions comme annulées, c'est-à-dire que les exceptions levées dans la transaction sont RuntimeException ; ou Est-ce sa sous-classe, de sorte que la transaction sera annulée (une erreur entraînera également l'annulation de la transaction par défaut). Avec la configuration par défaut, toutes les exceptions vérifiées n'entraîneront pas d'annulation de transaction.
Remarque : les exceptions non vérifiées incluent Error et RuntimeException. Toutes les sous-classes de RuntimeException appartiennent également à cette catégorie. L'autre type est coché Exception.
Vous pouvez configurer précisément le type d'exception et spécifier l'annulation des transactions pour ce type d'exception, y compris les exceptions vérifiées. L'extrait de code XML suivant montre comment configurer les exceptions vérifiées pour provoquer l'annulation des transactions et appliquer des types d'exceptions personnalisés :
<advice> <attributes> <method></method> <method></method> </attributes> </advice>
formulaire d'annotation avec effet équivalent comme suit :
@Transactional(rollbackForClassName={"Exception"}) 或者 @Transactional(rollbackFor={Exception.class})
在你遇到异常不想回滚事务的时候,同样的你也可指定不回滚的规则,下面的一个例子告诉你,即使遇到未处理的 InstrumentNotFoundException
异常时,Spring FrameWork 的事务框架同样会提交事务,而不回滚。
<advice> <attributes> <method></method> <method></method> </attributes> </advice>
与其有同样作用的注解形式如下:
@Transactional(noRollbackForClassName={"InstrumentNotFoundException"}) 或者 @Transactional(noRollbackFor={InstrumentNotFoundException.class})
还有更灵活的回滚规则配置方法,同时指定什么异常回滚,什么异常不回滚。当Spring FrameWork 的事务框架捕获到一个异常的时候,会去匹配配置的回滚规则来决定是否标记回滚事务,使用匹配度最强的规则结果。因此,下面的配置例子表达的意思是,除了异常 InstrumentNotFoundException
之外的任何异常都会导致事务回滚。
<advice> <attributes> <method></method> </attributes> </advice>
你也可以通过编程式的方式回滚一个事务,尽管方法非常简单,但是也有非常强的代码侵入性,使你的业务代码和Spring FrameWork 的事务框架代码紧密的绑定在一起,示例代码如下:
public void resolvePosition() { try { // some business logic... } catch (NoProductInStockException ex) { // trigger rollback programmatically TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } }
如果可能的话,强烈推荐您使用声明式事务方式回滚事务,对于编程式事务,如果你强烈需要它,也是可以使用的,but its usage flies in the face of achieving a clean POJO-based architecture.(没懂...)
看完官方文档这节内容找到了问题的答案,原来是因为我们自定义的异常不是 RuntimeException
。我的解决办法是,在注解@Transactional
中添加 rollbackFor={BizException.class}
。可能你会问我为什么不将自定义异常修改为继承RuntimeException,因为我需要BizException是一个checked 异常。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!