SpringBoot+JPA+EntityManageマルチデータソースマルチトランザクションの実装

10270 ワード

ターゲット
2つのデータソースは独自のEntityManagementを持ち、独自のTransactionManagerを持ち、Transactionは単独使用をサポートする
環境の紹介
  • Spring Boot v2.0.5
  • Spring JPAインタフェース(HibernateのEntityManagement実装)
  • Mysql 5.7

  • コードディレクトリ構造
    demo
        +com.xmasq.demo
            +first
            +main
            +second
            DataSourceConfig.java
            DemoApplication.java
            FirstConfig.java
            SecondConfig.java
        pom.xml
    

    部分コード
    pom.xml
    	
    	
    		org.springframework.boot
    		spring-boot-starter-data-jpa
    	
    	
    		org.springframework.boot
    		spring-boot-starter
    	
    	
    		org.springframework.boot
    		spring-boot-starter-web
    	
    	
    		org.projectlombok
    		lombok
    	
    	
    		mysql
    		mysql-connector-java
    		6.0.6
    	
    
    

    application.properties
    datasource.first.jdbc-url=jdbc:mysql://localhost:3306/first?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false&serverTimezone=Hongkong
    datasource.first.username=root
    datasource.first.password=
    datasource.first.driver-class-name=com.mysql.cj.jdbc.Driver
    
    datasource.second.jdbc-url=jdbc:mysql://localhost:3306/second?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false&serverTimezone=Hongkong
    datasource.second.username=root
    datasource.second.password=
    datasource.second.driver-class-name=com.mysql.cj.jdbc.Driver
    
    
    

    データソースの構成
    package com.xmasq.demo;
    
    import javax.sql.DataSource;
    
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    
    /**
     * 
     * @author guoyu.huang
     */
    @Configuration
    public class DataSourceConfig {
    
    	@Primary
    	@Bean(name = "firstDataSource")
    	@Qualifier("firstDataSource")
    	@ConfigurationProperties(prefix = "datasource.first")
    	public DataSource firstDataSource() {
    		System.out.println("primary db built");
    		return DataSourceBuilder.create().build();
    	}
    
    	@Bean(name = "secondDataSource")
    	@Qualifier("secondDataSource")
    	@ConfigurationProperties(prefix = "datasource.second")
    	public DataSource secondDataSource() {
    		System.out.println("secondary db built");
    		return DataSourceBuilder.create().build();
    	}
    }
    

    デフォルトのデータ・ソース構成
    package com.xmasq.demo;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.persistence.EntityManager;
    import javax.sql.DataSource;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    import org.springframework.orm.jpa.JpaTransactionManager;
    import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    /**
     * 
     * @author guoyu.huang
     */
    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(entityManagerFactoryRef = "firstEntityManagerFactory", transactionManagerRef = "firstTransactionManager", basePackages = {
    		"com.xmasq.demo.first" }) //   Repository    
    public class FirstConfig {
    
    	@Autowired
    	@Qualifier("firstDataSource")
    	private DataSource firstDataSource;
    
    	@Primary
    	@Bean(name = "firstEntityManager")
    	public EntityManager firstEntityManager(EntityManagerFactoryBuilder builder) {
    		return firstEntityManagerFactory(builder).getObject().createEntityManager();
    	}
    
    	@Primary
    	@Bean(name = "firstEntityManagerFactory")
    	public LocalContainerEntityManagerFactoryBean firstEntityManagerFactory(EntityManagerFactoryBuilder builder) {
    		Map map = new HashMap<>();
    		map.put("hibernate.hbm2ddl.auto", "update");
    		map.put("hibernate.show_sql", "true");
    		return builder.dataSource(firstDataSource).packages("com.xmasq.demo.first").properties(map) //          
    				.persistenceUnit("firstPersistenceUnit").build();
    	}
    
    	@Primary
    	@Bean(name = "firstTransactionManager")
    	public PlatformTransactionManager firstTransactionManager(EntityManagerFactoryBuilder builder) {
    		return new JpaTransactionManager(firstEntityManagerFactory(builder).getObject());
    	}
    
    }
    
    

    2番目のデータ・ソース構成
    package com.xmasq.demo;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.persistence.EntityManager;
    import javax.sql.DataSource;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.autoconfigure.domain.EntityScan;
    import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    import org.springframework.orm.jpa.JpaTransactionManager;
    import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    /**
     * 
     * @author guoyu.huang
     */
    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(entityManagerFactoryRef = "secondEntityManagerFactory", transactionManagerRef = "secondTransactionManager", basePackages = {
    		"com.xmasq.demo.second" }) //   Repository    
    @EntityScan(basePackages = "com.xmasq.demo.second")
    public class SecondConfig {
    
    	@Autowired
    	@Qualifier("secondDataSource")
    	private DataSource secondDataSource;
    
    	@Bean(name = "secondEntityManager")
    	public EntityManager secondEntityManager(EntityManagerFactoryBuilder builder) {
    		return secondEntityManagerFactory(builder).getObject().createEntityManager();
    	}
    
    	@Bean(name = "secondEntityManagerFactory")
    	public LocalContainerEntityManagerFactoryBean secondEntityManagerFactory(EntityManagerFactoryBuilder builder) {
    		Map map = new HashMap<>();
    		map.put("hibernate.hbm2ddl.auto", "update");
    		map.put("hibernate.show_sql", "true");
    
    		return builder.dataSource(secondDataSource).packages("com.xmasq.demo.second").properties(map) //          
    				.persistenceUnit("secondPersistenceUnit").build();
    	}
    
    	@Bean(name = "secondTransactionManager")
    	public PlatformTransactionManager secondTransactionManager(EntityManagerFactoryBuilder builder) {
    		return new JpaTransactionManager(secondEntityManagerFactory(builder).getObject());
    	}
    
    }
    
    

    クラスの開始
    package com.xmasq.demo;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
    
    /**
     * 
     * @author guoyu.huang
     */
    @SpringBootApplication
    public class DemoApplication extends SpringBootServletInitializer {
    
    	@Override
    	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
    		return builder.sources(DemoApplication.class);
    	}
    
    	public static void main(String[] args) {
    		SpringApplication.run(DemoApplication.class, args);
    	}
    
    }
    
    

    特定のトランザクション制御のサービスクラス
    package com.xmasq.demo.main;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.xmasq.demo.first.First;
    import com.xmasq.demo.first.repository.FirstRepository;
    import com.xmasq.demo.second.Second;
    import com.xmasq.demo.second.repository.SecondRepository;
    
    /**
     * 
     * @author guoyu.huang
     */
    @Service
    public class DemoService {
    
    	@Autowired
    	private FirstRepository firstRepository;
    	@Autowired
    	private SecondRepository secondRepository;
    
    	// secondTransactionManager      ,        JTA
    	@Transactional
    	public void save() {
    		First first = new First();
    		first.setName("first");
    		firstRepository.save(first);
    
    		Second second = new Second();
    		second.setCode("second");
    		secondRepository.save(second);
    
    		int i = 1 / 0;
    	}
    
    	public void find() {
    		List firsts = firstRepository.findAll();
    		System.out.println(firsts.get(0).getName());
    
    		List seconds = secondRepository.findAll();
    		System.out.println(seconds.get(0).getCode());
    	}
    
    	@Transactional
    	public void saveFirst() {
    		First first = new First();
    		first.setName("first");
    		firstRepository.save(first);
    
    		// int i = 1 / 0;
    	}
    
    	@Transactional(value = "secondTransactionManager", rollbackFor = Exception.class)
    	public void saveSecond() {
    		Second second = new Second();
    		second.setCode("second");
    		secondRepository.save(second);
    
    		// int i = 1 / 0;
    	}
    
    }
    
    

    コードクラウドソース:https://gitee.com/xmasq/study/tree/master/more-datasource
    この方法では、2つの問題が残されています.1つ目の問題は、同じ操作で異なるデータ・ソースの操作にトランザクションが必要な場合、デフォルトのトランザクションのみが有効になることです.つまり、DemoServices.saveメソッド(JTAを導入し、その後実装します).2つ目の質問:別のデータ・ソースのエンティティが現在のシステムにない場合、EntityManagementが存在しない場合、トランザクションをどのように実装するか(JdbcTemplateを使用して、次の手順で説明します).