两个数据源拥有自己的EntityManage,拥有自己的TransactionManager,Transaction支持单独使用
demo
+com.xmasq.demo
+first
+main
+second
DataSourceConfig.java
DemoApplication.java
FirstConfig.java
SecondConfig.java
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
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,下一步介绍)。