Scénarios d'échec de la transaction Spring

Dans une même classe, un appel d'une méthode transactionnelle à une méthode non transactionnelle peut fonctionner correctement. En revanche, le cas contraire où la transaction échoue est illustré par le point 4 ci-dessous (appel interne dans une même classe).

@Autowired
private XXXMapper xxxMapper;

@Autowired
private YYYMapper yyyMapper;

@Transactional
public ResultVO<AssetChangeVO> modifyAssetDetail() {
    xxxMapper.execute();
}

private void copyYYY() {
    yyyMapper.copy("src", "dest");
}

  • Introduction
    1. La classe de service n'est pas gérée par Spring
    1. Aucun gestionnaire de transactions configuré dans le fichier Spring
    1. Méthode transactionnelle marquée final ou static
    1. Appel interne dans une même classe
    1. Visibilité de la méthode autre que public
    1. Moteur de stockage de la base ne supportant pas les transactions
    1. Annotation @Transactional mal configurée
    1. Délai d'expiration trop court
    1. Mécanisme de propagation erroné
    1. Attribut rollbackFor incorrect
    1. Annotation de transaction écrasée
    1. Piège des transactions imbriquées
    1. Appel multi-thread dans une transaction
    1. Expection capturée sans être relancée
    1. Exception différente lancée manuellement

Introduction

Dans le développement quotidien, nous utilisons fréquemment les transactions Spring. Récemment, un ami a été interrogé lors d'un entretien chez Meituan sur le sujet : Dans quels cas une transaction Spring ne fonctionne-t-elle pas ? Aujourd'hui, nous allons passer en revue 15 scénarios où les transactions Spring échouent.

1. La classe de service n'est pas gérée par Spring

// @Service (commenté)
public class DemoServiceImpl implements DemoService {

    @Autowired
    private DemoMapper demoMapper;
    
    @Autowired
    private LogMapper logMapper;

    @Transactional
    public void addDemo(Demo demo) {
        demoMapper.insert(demo);
        logMapper.insert(buildLog(demo));
    }
}

  • Cause de l'échec : Dans l'exemple ci-dessus, l'annotation @Service est commentée. La transaction Spring (@Transactional) ne s'applique pas car Spring utilise l'AOP pour créer un proxy de la classe cible afin de supporter les transactions. Or, si la classe n'est pas gérée par Spring (pas de @Service), aucun proxy n'est créé.
  • Solution : Ajouter l'annotation @Service.

2. Aucun getsionnaire de transactions configuré dans le fichier Spring

@Configuration
public class AppConfig {
    // Pas de gestionnaire de transactions
}

@Service
public class MyService {
    @Transactional
    public void doSomething() {
        // ...
    }
}

  • Cause de l'échec : Aucun gestionnaire de transactions n'est défini dans AppConfig. Spring ne peut donc pas créer l'objet proxy transactionnel, et l'annotation @Transactional reste ignorée.
  • Solution : Configurer un PlatformTransactionManager dans la classe de configuraton :
@Configuration
public class AppConfig {
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}

@Service
public class MyService {
    @Transactional
    public void doSomething() {
        // ...
    }
}

Pour un projet Spring Boot, la configuration automatique active généralement le gestionnaire de transactions par défaut.

3. Méthode transactionnelle marquée final ou static

@Service
public class DemoServiceImpl {

    @Autowired
    private DemoMapper demoMapper;
    
    @Autowired
    private LogMapper logMapper;

    @Transactional
    public final void addDemo(Demo demo) {
        demoMapper.insert(demo);
        logMapper.insert(buildLog(demo));
    }
}

  • Cause de l'échec : Une méthode déclarée final ou static ne peut pas être redéfinie dans une sous-classe. Or, le proxy dynamique de Spring nécessite la redéfinition pour intercepter l'appel et ajouter le comportement transactionnel. Par conséquent, la transaction est ignorée.
  • Solution : Supprimer les modificateurs final et static des méthodes transactionnelles.

Étiquettes: Spring transaction Proxy AOP transactional

Publié le 25 juin à 16h07