SpringBoot+JPA+EntityManage实现多数据源多事务处理

目标

两个数据源拥有自己的EntityManage,拥有自己的TransactionManager,Transaction支持单独使用

环境介绍

  • Spring Boot v2.0.5
  • Spring JPA 接口(Hibernate的EntityManage实现)
  • 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());
	}

}

第二个数据源配置

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个问题,第一个问题:同一个操作对不同数据源操作需要事务时,仅默认事务生效,也就是DemoService.save方法(引入JTA,后续实现);第二个问题:如果另一个数据源的实体在当前系统没有,也就不存在EntityManage,怎么实现事务(使用JdbcTemplate,下一步介绍)。

你可能感兴趣的:(SpringBoot+JPA+EntityManage实现多数据源多事务处理)