Transaction série SpringBoot de tutoriels ne prennent pas effet dans plusieurs cas

Transaction série SpringBoot de tutoriels ne prennent pas effet dans plusieurs cas

Bowen introduit l'avant de plusieurs transactions déclarative en @Transactionalutilisant 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.xmlfichier, ensemble spring-boot-starter-jdbc, nous allons injecter un DataSourceTransactionManagerharicot, 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 @Transactionalprincipalement 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 testCompleExceptionméthode, la transaction fonctionnement normal, en appelant l' testCallaccè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 @Transactionalannotations 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

@TransactionalRemarques 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 @TransactionalrollbackFor à 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, testMultThreadappelant 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 @Transactionalnotes 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

source

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 blog gris

Publié 206 articles originaux · louange gagné 57 · vues 160 000 +

Je suppose que tu aimes

Origine blog.csdn.net/liuyueyi25/article/details/104178269
conseillé
Classement