[JAVA] Comment contrôler les transactions dans Spring Boot sans utiliser @Transactional

Oh, c'est aujourd'hui la veille de Noël ~: Santa :: flocon de neige: Ne vous inquiétez pas pour cela ... Cette fois, je vais vous montrer comment contrôler les transactions sur Spring Boot sans utiliser @ Transactional (gestion des transactions par annotation).

Certaines personnes peuvent penser que vous devriez utiliser @ Transactional, mais comme vous ne pouvez spécifier @ Transaction que pour les composants que vous créez, la bibliothèque OSS créée par le tiers ne dépend pas de Spring. Il n'est pas possible d'appliquer une transaction à une méthode telle que (évidemment ...: sweat_smile :).

Que devrais-je faire?

En plus de la méthode d'utilisation des annotations pour spécifier la méthode à échanger au printemps,

Les méthodes, etc. sont prises en charge.

Si vous utilisez Spring depuis longtemps, vous avez peut-être vu la déclaration de contrôle de transaction suivante dans la définition de Bean à l'aide de XML. Dans cet exemple, toutes les méthodes publiques de la classe TxDemoApplication sont soumises au contrôle des transactions.

src/resources/transactionContext.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:tx="http://www.springframework.org/schema/tx"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xsi:schemaLocation="
	   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
	   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
	   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">

	<tx:advice id="transactionAdvisor">
		<tx:attributes>
			<tx:method name="*" />
		</tx:attributes>
	</tx:advice>

	<aop:config>
		<aop:pointcut id="txDemoApplicationPointcut" expression="execution(* com.example.TxDemoApplication.*(..))"/>
		<aop:advisor advice-ref="transactionAdvisor" pointcut-ref="txDemoApplicationPointcut"/>
	</aop:config>

</beans>

Même sur Spring Boot, si vous créez un fichier XML comme ↑ et le chargez en utilisant @ ImportResource comme indiqué ci-dessous, cela fonctionnera.

@SpringBootApplication
@ImportResource("classpath:/transactionContext.xml")
public class TxDemoApplication implements CommandLineRunner {
    // ...
}

Mais ... je ne veux plus revenir au fichier XML ...: sweat_smile:, Si vous utilisez Spring from Spring Boot (ceux qui ne connaissent que Java Config), je me demande comment définir les beans en utilisant XML ~ Par conséquent, il y a des gens: wink:

Faites de même avec Java Config! !!

Si vous voulez exprimer la même chose en utilisant Java Config ... Créez simplement la configuration Java suivante.

@Configuration
public class TransactionConfig {
	@Bean
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
			PlatformTransactionManager transactionManager) {

		//Spécifiez la méthode de gestion des transactions et les valeurs d'attribut requises pour le contrôle des transactions.
		MethodMapTransactionAttributeSource source = new MethodMapTransactionAttributeSource();
		source.addTransactionalMethod(TxDemoApplication.class, "*", new RuleBasedTransactionAttribute());

		//Générer un conseiller AOP qui contrôle les transactions
		//Conseils pour le contrôle des transactions(TransactionInteceptor)Le point de coupe qui spécifie l'emplacement d'application de est lié à la méthode spécifiée dans ↑.
		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		advisor.setTransactionAttributeSource(source);
		advisor.setAdvice(new TransactionInterceptor(transactionManager, source));
		return advisor;
	}

}

Note:

Si vous voulez utiliser ce mécanisme, vous avez besoin du JAR de ʻorg.aspectj: aspectjweaver dans le chemin de classe, alors ajoutez ʻorg.springframework.boot: spring-boot-starter-aop comme bibliothèque dépendante. ..

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

Warning:

Avec Spring Boot, le mécanisme de configuration automatique permet également un mécanisme de contrôle des transactions en utilisant @ Transactional (gestion des transactions par annotations). Par conséquent ... Si @ Transactional est ajouté à la méthode spécifiée dans Java Config dans ↑, Advice ( TransactionInteceptor) pour le contrôle des transactions sera appliqué deux fois, donc soyez prudent.

Type de TransactionAttributeSource

Dans l'exemple ci-dessus, MethodMapTransactionAttributeSource est utilisé, mais Spring fournit des classes d'implémentation pour TransactionAttributeSource.

nom de la classe La description
NameMatchTransactionAttributeSource Appliquer le contrôle des transactions aux méthodes qui correspondent au nom de méthode spécifié (modèle)
MethodMapTransactionAttributeSource Appliquer le contrôle de transaction au nom de méthode spécifié (modèle) de la classe spécifiée
MatchAlwaysTransactionAttributeSource Appliquer le contrôle des transactions à toutes les méthodes
AnnotationTransactionAttributeSource Appliquer le contrôle des transactions aux méthodes de classe annotées Spring, JTA et EJB (cette classe est utilisée avec la configuration automatique de Spring Boot)
CompositeTransactionAttributeSource plurielTransactionAttributeSourceAgréger et appliquer le contrôle des transactions

Filtres pour les classes applicables

BeanFactoryTransactionAttributeSourceAdvisor vous donne la possibilité de filtrer les classes auxquelles s'applique l'AOP. Par exemple ... Si vous souhaitez appliquer uniquement les beans auxquels @ Service est affecté, vous pouvez définir les beans suivants.

@Configuration
public class TransactionConfig {
	@Bean
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
			PlatformTransactionManager transactionManager) {

		NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
		source.addTransactionalMethod("*", new RuleBasedTransactionAttribute());

		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		advisor.setTransactionAttributeSource(source);
		advisor.setAdvice(new TransactionInterceptor(transactionManager, source));
		advisor.setClassFilter(clazz -> AnnotationUtils.findAnnotation(clazz, Service.class) != null); //Mettre en œuvre les conditions de filtrage et`setClassFilter`Appel
		return advisor;
	}

}

Application de vérification

Vous trouverez ci-dessous un exemple d'application créé pour la vérification des opérations.

Environnement de vérification

Créer un projet

Téléchargez le projet en sélectionnant "JDBC", "H2" et "AOP" comme bibliothèques dépendantes sur "SPRING INITIALIZR".

Modifier le niveau de journal

Définissez le mode de sortie du journal Spring JDBC sur débogage pour voir si la transaction a été appliquée.

src/resources/application.properties


logging.level.org.springframework.jdbc=debug

Créer une application et définir Bnea

Ensuite, implémentez CommandRunner dans l'application Spring Boot et définissez le bean pour le contrôle des transactions.

package com.example;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor;
import org.springframework.transaction.interceptor.MethodMapTransactionAttributeSource;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;

@SpringBootApplication
public class TxDemoApplication implements CommandLineRunner {

	public static void main(String[] args) {
		SpringApplication.run(TxDemoApplication.class, args);
	}

	private final JdbcOperations jdbcOperations;

	public TxDemoApplication(JdbcOperations jdbcOperations) {
		this.jdbcOperations = jdbcOperations;
	}

	@Override //Cette méthode est soumise au contrôle des transactions, mais ...@Transactionnel n'est pas accordé! !!
	public void run(String... args) throws Exception {
		Integer value = jdbcOperations.queryForObject("SELECT 1", Integer.class);
		System.out.println(value);
	}

	@Configuration
	static class TransactionConfig {
		@Bean
		public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
				PlatformTransactionManager transactionManager) {

			MethodMapTransactionAttributeSource source = new MethodMapTransactionAttributeSource();
			source.addTransactionalMethod(TxDemoApplication.class, "*", new RuleBasedTransactionAttribute());

			BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
			advisor.setTransactionAttributeSource(source);
			advisor.setAdvice(new TransactionInterceptor(transactionManager, source));
			return advisor;
		}

	}

}

Exécutez l'application

Maintenant, exécutons l'application Spring Boot.

$ ./mvnw spring-boot:run
...

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.4.3.RELEASE)

2016-12-24 16:20:30.713  INFO 58327 --- [           main] com.example.TxDemoApplication            : Starting TxDemoApplication on Kazuki-no-MacBook-Pro.local with PID 58327 (/Users/shimizukazuki/Downloads/tx-demo/target/classes started by shimizukazuki in /Users/shimizukazuki/Downloads/tx-demo)
2016-12-24 16:20:30.715  INFO 58327 --- [           main] com.example.TxDemoApplication            : No active profile set, falling back to default profiles: default
2016-12-24 16:20:30.748  INFO 58327 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1e2e2b3: startup date [Sat Dec 24 16:20:30 JST 2016]; root of context hierarchy
2016-12-24 16:20:31.454  INFO 58327 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2016-12-24 16:20:31.469 DEBUG 58327 --- [           main] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [com.example.TxDemoApplication$$EnhancerBySpringCGLIB$$9f83f17d.run]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2016-12-24 16:20:31.609 DEBUG 58327 --- [           main] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [ProxyConnection[PooledConnection[conn9: url=jdbc:h2:mem:testdb user=SA]]] for JDBC transaction
2016-12-24 16:20:31.611 DEBUG 58327 --- [           main] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [ProxyConnection[PooledConnection[conn9: url=jdbc:h2:mem:testdb user=SA]]] to manual commit
2016-12-24 16:20:31.618 DEBUG 58327 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing SQL query [SELECT 1]
1
2016-12-24 16:20:31.638 DEBUG 58327 --- [           main] o.s.j.d.DataSourceTransactionManager     : Initiating transaction commit
2016-12-24 16:20:31.638 DEBUG 58327 --- [           main] o.s.j.d.DataSourceTransactionManager     : Committing JDBC transaction on Connection [ProxyConnection[PooledConnection[conn9: url=jdbc:h2:mem:testdb user=SA]]]
2016-12-24 16:20:31.639 DEBUG 58327 --- [           main] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [ProxyConnection[PooledConnection[conn9: url=jdbc:h2:mem:testdb user=SA]]] after transaction
2016-12-24 16:20:31.639 DEBUG 58327 --- [           main] o.s.jdbc.datasource.DataSourceUtils      : Returning JDBC Connection to DataSource
2016-12-24 16:20:31.642  INFO 58327 --- [           main] com.example.TxDemoApplication            : Started TxDemoApplication in 1.106 seconds (JVM running for 3.466)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.612 s
[INFO] Finished at: 2016-12-24T16:20:31+09:00
[INFO] Final Memory: 24M/315M
[INFO] ------------------------------------------------------------------------
2016-12-24 16:20:31.742  INFO 58327 --- [       Thread-1] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1e2e2b3: startup date [Sat Dec 24 16:20:30 JST 2016]; root of context hierarchy
2016-12-24 16:20:31.744  INFO 58327 --- [       Thread-1] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown

Si vous regardez le journal de la console, vous pouvez voir que la transaction est démarrée, validée et terminée avant et après l'implémentation de SQL: clap :: clap :: clap:

Résumé

Il peut ne pas être utilisé souvent, mais ... Vous pouvez contrôler les transactions pour les méthodes qui n'ont pas @ Transactional. En gros, je pense qu'il vaut mieux ajouter @ Transactional pour contrôler les transactions des composants que vous créez vous-même, mais selon les exigences de l'application, la méthode introduite cette fois peut être efficace dans certains cas. Hmm.

à la fin

Happy Christmas 2016 !! :wave:

Recommended Posts

Comment contrôler les transactions dans Spring Boot sans utiliser @Transactional
Comment ajouter un chemin de classe dans Spring Boot
Comment se lier avec un fichier de propriétés dans Spring Boot
Comment créer un projet Spring Boot dans IntelliJ
Comment utiliser CommandLineRunner dans Spring Batch of Spring Boot
Testez la classe injectée sur le terrain dans le test de démarrage Spring sans utiliser le conteneur Spring
Comment utiliser Lombok au printemps
Comment modifier la valeur de paramètre de application.properties au moment du démarrage dans Spring Boot
Déployez des applications Spring Boot sur Heroku sans utiliser la CLI Heroku
Comment configurer Spring Boot + PostgreSQL
Comment appeler et utiliser l'API en Java (Spring Boot)
Comment utiliser ModelMapper (Spring boot)
Comment créer un hinadan pour un projet Spring Boot à l'aide de SPRING INITIALIZR
Téléchargement et téléchargement de fichiers à l'aide d'Ajax avec Spring Boot (sans JQuery)
Comment inclure Spring Tool dans Eclipse 4.6.3?
Comment utiliser MyBatis2 (iBatis) avec Spring Boot 1.4 (Spring 4)
Comment utiliser h2db intégré avec Spring Boot
Comment réduire l'image de Spring Boot Docker
Comment utiliser les attributs de session Spring Boot (@SessionAttributes)
Appliquer Twitter Bootstrap 4 à Spring Boot 2 à l'aide de Webjars
Comment définir plusieurs orm.xml dans Spring4, JPA2.1
[Spring Boot] Comment se référer au fichier de propriétés
Spring Boot - Comment définir le délai d'expiration de la session
Lorsque vous souhaitez notifier une erreur quelque part lors de l'utilisation de graphql-spring-boot avec Spring Boot
Comment définir des variables d'environnement dans le fichier de propriétés de l'application Spring Boot
Comment définir l'injection de dépendance Spring Boot (DI)
Comment envoyer une valeur en HTML sans transition d'écran
Comment écrire un test unitaire pour Spring Boot 2
[Spring Boot] Comment créer un projet (pour les débutants)
[Introduction à Spring Boot] Soumettez un formulaire à l'aide de thymeleaf
Comment convertir A en A et A en A en utilisant le produit logique et la somme en Java
Comment démarrer le serveur local de Tomcat sans utiliser eclipse
Présentez swagger-ui à l'API REST implémentée dans Spring Boot
Comment démarrer par environnement avec Spring Boot de Maven
Comment utiliser le référentiel de jobs en mémoire avec Spring Batch
Comment implémenter le processus d'authentification en spécifiant le nom d'utilisateur et le mot de passe dans Spring Boot
Définir le paramètre contextuel dans Spring Boot
Essayez Spring Boot de 0 à 100.
Comment passer un objet à Mapper dans MyBatis sans passer par un argument
Multi-projets Spring Boot 2 avec Gradle
Comment utiliser la même classe Mapper dans plusieurs sources de données avec Spring Boot + MyBatis
Empêcher les opérations! Méthode de mise à jour manuelle sûre des données pour Rails utilisant des transactions
Comment développer dans des fichiers Xib sans utiliser Storyboard
Introduction à Spring Boot ① ~ DI ~
Comment déployer jQuery dans les applications Rails à l'aide de Webpacker
Comment générer des valeurs de tableau sans utiliser d'instruction for
Comment installer JDK 8 sur Windows sans utiliser le programme d'installation
Utilisez thymeleaf3 avec le parent sans spécifier spring-boot-starter-parent dans Spring Boot
Changements majeurs dans Spring Boot 1.5
NoHttpResponseException dans Spring Boot + WireMock
Comment joindre une table sans utiliser DBFlute et SQL
Introduction à Spring Boot, partie 1
Essayez d'utiliser Spring Boot Security
J'ai essayé de démarrer avec Swagger en utilisant Spring Boot
8 choses à insérer dans DB en utilisant Spring Boot et JPA
Découvrons comment recevoir avec Request Body avec l'API REST de Spring Boot
Utilisez @ControllerAdvice, @ExceptionHandler, HandlerExceptionResolver dans Spring Boot pour intercepter les exceptions
Comment autoriser à l'aide de graphql-ruby
[Sprint Boot] Comment utiliser les trois types de SqlParameterSource définis dans org.springframework.jdbc.core.namedparam