Transaction série SpringBoot de tutoriels ne prennent pas effet dans plusieurs cas
Bowen introduit l'avant de plusieurs transactions déclarative en @Transactional
utilisant des gestes, la posture ne connaissent que l'utilisation correcte peut ne pas être suffisant, vous devez savoir dans quel scénario ne prend pas effet, pour éviter la fosse. Cet article se concentrera sur les questions ne permettent pas l'entrée en vigueur de plusieurs cas
I. placement
Cas de cet article, se déclarative des transactions, d' abord , nous créons un projet SpringBoot, la version 2.2.1.RELEASE
, utilisez MySQL comme la sélection du moteur de stockage de base de données cible Innodb
, le niveau d'isolation des transactions RR
1. Configuration du projet
Dans le projet pom.xml
fichier, ensemble spring-boot-starter-jdbc
, nous allons injecter un DataSourceTransactionManager
haricot, fournit le support des transactions
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
2. Configuration de base de données
Dans le fichier de configuration du ressort application.properties
, sur les informations mis liées à db-
## DataSource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=
3. Base de données
Créer une structure de table simple, pour les tests
CREATE TABLE `money` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名',
`money` int(26) NOT NULL DEFAULT '0' COMMENT '钱',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0',
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=551 DEFAULT CHARSET=utf8mb4;
II. Ne prend effet le cas
Dans le tutoriel opérations déclaratives transaction déclarative transactionnelles série 200119-SpringBoot de tutoriels , mais aussi mentionné une certaine façon ne prend pas effet la transaction, tels que les billets de transaction déclaratives @Transactional
principalement une combinaison d'agents pour atteindre, combinée à la connaissance de l' AOP, au moins venir sur les méthodes privées, les classes internes appels prendront effet, entrez les détails suivants
1. Base de données
La prémisse de la transaction pour prendre effet est que vous devez soutenir les sources de données transactionnelles, telles que le moteur MySQL MyISAM ne supporte pas les transactions et les services de soutien InnoDB
Le cas suivant est basé sur MySQL + InnoDB
Pour le cas après la présentation, nous préparons des données comme suit
@Service
public class NotEffectDemo {
@Autowired
private JdbcTemplate jdbcTemplate;
@PostConstruct
public void init() {
String sql = "replace into money (id, name, money) values" + " (520, '初始化', 200)," + "(530, '初始化', 200)," +
"(540, '初始化', 200)," + "(550, '初始化', 200)";
jdbcTemplate.execute(sql);
}
}
2. Classe accès interne
Brièvement la méthode B se réfère aux marqueurs annotée accès non directement, mais par un A de classe de procédé ordinaire, B et accessible par l'A
Voici un cas simple,
/**
* 非直接调用,不生效
*
* @param id
* @return
* @throws Exception
*/
@Transactional(rollbackFor = Exception.class)
public boolean testCompileException2(int id) throws Exception {
if (this.updateName(id)) {
this.query("after update name", id);
if (this.update(id)) {
return true;
}
}
throw new Exception("参数异常");
}
public boolean testCall(int id) throws Exception {
return testCompileException2(id);
}
Les deux méthodes ci - dessus, appel direct testCompleException
méthode, la transaction fonctionnement normal, en appelant l' testCall
accès indirect, ne prend pas effet
Cas de test suit comme:
@Component
public class NotEffectSample {
@Autowired
private NotEffectDemo notEffectDemo;
public void testNotEffect() {
testCall(530, (id) -> notEffectDemo.testCall(530));
}
private void testCall(int id, CallFunc<Integer, Boolean> func) {
System.out.println("============ 事务不生效case start ========== ");
notEffectDemo.query("transaction before", id);
try {
// 事务可以正常工作
func.apply(id);
} catch (Exception e) {
}
notEffectDemo.query("transaction end", id);
System.out.println("============ 事务不生效case end ========== \n");
}
@FunctionalInterface
public interface CallFunc<T, R> {
R apply(T t) throws Exception;
}
}
sortie:
============ 事务不生效case start ==========
transaction before >>>> {id=530, name=初始化, money=200, is_deleted=false, create_at=2020-02-03 13:44:11.0, update_at=2020-02-03 13:44:11.0}
after update name >>>> {id=530, name=更新, money=200, is_deleted=false, create_at=2020-02-03 13:44:11.0, update_at=2020-02-03 13:44:11.0}
transaction end >>>> {id=530, name=更新, money=210, is_deleted=false, create_at=2020-02-03 13:44:11.0, update_at=2020-02-03 13:44:11.0}
============ 事务不生效case end ==========
Vous pouvez voir la sortie ci-dessus, la transaction ne sera pas annulée, principalement en raison de la classe interne appelée, n'est pas accessible par un moyen proxy
3. Méthode privée
Sur une méthode privée, d' ajouter des @Transactional
annotations ne prendront pas effet, les méthodes privées extérieures ne peuvent pas accéder, alors que l' accès interne, le cas ci - dessus ne prend pas effet, bien sûr, cela ne vient pas en vigueur
/**
* 私有方法上的注解,不生效
*
* @param id
* @return
* @throws Exception
*/
@Transactional
private boolean testSpecialException(int id) throws Exception {
if (this.updateName(id)) {
this.query("after update name", id);
if (this.update(id)) {
return true;
}
}
throw new Exception("参数异常");
}
L'utilisation directe, après ce scénario moins probable, car il y aura une idée de rappel, la copie à: Methods annotated with '@Transactional' must be overridable
4. Match d'anomalie
@Transactional
Remarques par défaut lorsqu'il s'agit d'une exception d'exécution, qui est, à quelques exceptions d'exécution, déclenchera la transaction est annulée, et non, sinon
/**
* 非运行异常,且没有通过 rollbackFor 指定抛出的异常,不生效
*
* @param id
* @return
* @throws Exception
*/
@Transactional
public boolean testCompleException(int id) throws Exception {
if (this.updateName(id)) {
this.query("after update name", id);
if (this.update(id)) {
return true;
}
}
throw new Exception("参数异常");
}
Cas de test suit comme
public void testNotEffect() {
testCall(520, (id) -> notEffectDemo.testCompleException(520));
}
les résultats de sortie sont les suivantes, la transaction ne retourne pas de rouleau (si vous avez besoin pour résoudre ce problème en mettant @Transactional
rollbackFor à la propriété)
============ 事务不生效case start ==========
transaction before >>>> {id=520, name=初始化, money=200, is_deleted=false, create_at=2020-02-03 13:44:11.0, update_at=2020-02-03 13:44:11.0}
after update name >>>> {id=520, name=更新, money=200, is_deleted=false, create_at=2020-02-03 13:44:11.0, update_at=2020-02-03 13:44:11.0}
transaction end >>>> {id=520, name=更新, money=210, is_deleted=false, create_at=2020-02-03 13:44:11.0, update_at=2020-02-03 13:44:11.0}
============ 事务不生效case end ==========
5. multithreading
Cette scène pourrait être inhabituelle, les affaires intérieures méthode de marquage, un autre thread exécute db opérations tournevis, où l'opération a également ne prendra effet
Voici deux positions différentes, un sous Lancers fil, le fil principal OK, un thread enfant est ok, le thread principal Lancers
une. cas 1
/**
* 子线程抛异常,主线程无法捕获,导致事务不生效
*
* @param id
* @return
*/
@Transactional(rollbackFor = Exception.class)
public boolean testMultThread(int id) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
updateName(id);
query("after update name", id);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
boolean ans = update(id);
query("after update id", id);
if (!ans) {
throw new RuntimeException("failed to update ans");
}
}
}).start();
Thread.sleep(1000);
System.out.println("------- 子线程 --------");
return true;
}
Le scénario ci - dessus ne prend pas effet cela est bien compris, l'exception ne sera pas capturé sous-fil du filetage extérieur, testMultThread
appelant cette méthode ne jette pas une exception, et par conséquent ne déclencheront pas de transaction rollback
public void testNotEffect() {
testCall(540, (id) -> notEffectDemo.testMultThread(540));
}
Les résultats de sortie sont comme suit
============ 事务不生效case start ==========
transaction before >>>> {id=540, name=初始化, money=200, is_deleted=false, create_at=2020-02-03 13:44:11.0, update_at=2020-02-03 13:44:11.0}
after update name >>>> {id=540, name=更新, money=200, is_deleted=false, create_at=2020-02-03 13:44:11.0, update_at=2020-02-03 13:44:11.0}
Exception in thread "Thread-3" java.lang.RuntimeException: failed to update ans
at com.git.hui.boot.jdbc.demo.NotEffectDemo$2.run(NotEffectDemo.java:112)
at java.lang.Thread.run(Thread.java:748)
after update id >>>> {id=540, name=更新, money=210, is_deleted=false, create_at=2020-02-03 13:44:11.0, update_at=2020-02-03 13:44:11.0}
------- 子线程 --------
transaction end >>>> {id=540, name=更新, money=210, is_deleted=false, create_at=2020-02-03 13:44:11.0, update_at=2020-02-03 13:44:11.0}
============ 事务不生效case end ==========
b. affaire2
/**
* 子线程抛异常,主线程无法捕获,导致事务不生效
*
* @param id
* @return
*/
@Transactional(rollbackFor = Exception.class)
public boolean testMultThread2(int id) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
updateName(id);
query("after update name", id);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
boolean ans = update(id);
query("after update id", id);
}
}).start();
Thread.sleep(1000);
System.out.println("------- 子线程 --------");
update(id);
query("after outer update id", id);
throw new RuntimeException("failed to update ans");
}
Cela ne semble pas regarder les problèmes ci-dessus, jeté fil, rollback transaction, mais modifier deux sous-fil et ne sera pas annulée
Code d'essai
public void testNotEffect() {
testCall(550, (id) -> notEffectDemo.testMultThread2(550));
}
De la sortie suivante peut savoir, il ne modifie pas le fil de l'enfant dans la même transaction ne sera pas annulée
============ 事务不生效case start ==========
transaction before >>>> {id=550, name=初始化, money=200, is_deleted=false, create_at=2020-02-03 13:52:38.0, update_at=2020-02-03 13:52:38.0}
after update name >>>> {id=550, name=更新, money=200, is_deleted=false, create_at=2020-02-03 13:52:38.0, update_at=2020-02-03 13:52:40.0}
after update id >>>> {id=550, name=更新, money=210, is_deleted=false, create_at=2020-02-03 13:52:38.0, update_at=2020-02-03 13:52:40.0}
------- 子线程 --------
after outer update id >>>> {id=550, name=更新, money=220, is_deleted=false, create_at=2020-02-03 13:52:38.0, update_at=2020-02-03 13:52:41.0}
transaction end >>>> {id=550, name=更新, money=210, is_deleted=false, create_at=2020-02-03 13:52:38.0, update_at=2020-02-03 13:52:40.0}
============ 事务不生效case end ==========
6. propriétés de propagation
L'article sur le blog des propriétés de propagation, il introduit plusieurs qui ne vont pas l' exécution de la transaction, donc il a besoin d' attention aux détails peut se référer Bowen Affaires série 200202-SpringBoot de tutoriels propriété de transfert
7. Résumé
Le résumé qui suit de plusieurs @Transactional
notes au cas où la transaction ne prend effet
- La base de données ne supporte pas les transactions
- Les notes placées sur des méthodes privées
- appels internes de classe
- Uncaught exception
- scène multithreading
- La diffusion des paramètres de propriété de problème
III. Autres
0. série Bowen & Source
série Bowen
- Utilisation de base de partie avancée de la DB 180926-SpringBoot
- La version avancée du 190407-SpringBoot données JdbcTemplate est inséré à l'aide d'un geste détaillé
- La version avancée de la requête de données JdbcTemplate 190412-SpringBoot sur les articles
- La version avancée des JdbcTemplate 190417-SpringBoot données suivante de la requête
- La version avancée de la mise à jour JdbcTemplate 190418-SpringBoot et des données de suppression
- série 200119-SpringBoot de tutoriels déclarative transaction transactionnelles
- Transaction série 200120-SpringBoot d'isolement des tutoriels connaissances de niveau Résumé
- Transaction 200202-SpringBoot série de didacticiels propriété de transfert
source
- Projet: https://github.com/liuyueyi/spring-boot-demo
- Exemples Source: https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/101-jdbctemplate-transaction
1. Un blog gris
Je pense que tout le livre est pas aussi bon, au-dessus, est purement un des mots, en raison de la capacité personnelle limitée, il est inévitable omissions et erreurs, par exemple trouver un bug ou ont de meilleures suggestions sont les bienvenus et critiques gratitude généreuse
Voici un blog personnel gris, enregistrer toutes les études et le travail dans le blog, bienvenue pour tout le monde
- Un gris Blog blog personnel https://blog.hhui.top
- Un gris Blog-printemps Blog thématique http://spring.hhui.top