一、JPA配置多数据源
上一节讲解了jdbcTemplate配置多数据源,但其实实际中使用jdbcTemplate的情况还是比较少的,这一节我们来看看JPA的数据源怎么配置。
该章节是在JPA的使用基础上讲解的,并不会过多讲解JPA的使用,如果不熟悉JPA的使用的话,可以看看之前的JPA讲解:Spring Boot集成Spring Data JPA。
首先是依赖,除了JPA及数据库的相关依赖外,多数据源并不会增加额外依赖。
<!--mysql连接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--jpa依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa
</artifactId>
</dependency>
在配置文件中,声明使用jpa,并指明数据库方言,这里使用的是mysql,然后数据源部分,配置master及slave两个数据库,此处根据实际情况进行配置即可。
server:
port: 10900
spring:
datasource:
master:
# 新版驱动从com.mysql.jdbc.Driver变更为com.mysql.cj.jdbc.Driver
driver-class-name: com.mysql.cj.jdbc.Driver
# 数据源需要添加时间标准和指定编码格式解决乱码 You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: 1234
slave:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: 1234
jpa:
show-sql: true
# 配置jpa数据库类型
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
配置好数据源后,我们创建DataSourceConfig配置类加载数据源,这个配置类非常简单,就是加载了spring.datasource节点下配置的master与slave,生成对应的masterDataSource与slaveDataSource数据源。需要注意的事,如果启动类上没有额外排除数据源的话,需要在其中一个数据源上注解@Primary以表示该数据源为默认数据源,否则启动会出现无法加载数据源的错误。
@Configuration
public class DataSourceConfig {
@Bean
@Primary // 表示默认的数据源
@ConfigurationProperties(prefix = "spring.datasource.master")
DataSource masterDataSource(){
// DataSourceBuilder.create().build();
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.slave")
DataSource slaveDataSource(){
return DruidDataSourceBuilder.create().build();
}
}
然后创建MasterJpaConfig的配置类,主要对masterDataSource进行属性配置。@EnableTransactionManagement用于声明式事务的开启,而@EnableJpaRepositories用于指定事物管理器及指定实体管理器工厂entityManagerFactory和重要的basePackages。因为JPA是Entry实体与数据库映射的,多数据源的操作本质是通过不同Dao将实体映射到不同的数据源上,basePackages用来声明该JPA数据源配置生效的包,也即com.yanger.jpa.dao下的JPA操作会作用到master数据库。
因为masterDataSource为默认数据源,所以同样的,在注入masterEntityManagerFactory、masterEntityManager以及masterTransactionManager时,我们同样使用了@Primary注解。
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "masterEntityManagerFactory", //配置连接工厂 entityManagerFactory
transactionManagerRef = "masterTransactionManager", //配置事物管理器 transactionManager
basePackages = {"com.yanger.jpa.dao"}
)
public class MasterJpaConfig {
@Autowired
@Qualifier("masterDataSource")
private DataSource masterDataSource;
@Autowired
private JpaProperties jpaProperties;
@Bean("masterEntityManagerFactory")
@Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(EntityManagerFactoryBuilder builder) {
return builder.dataSource(masterDataSource)
.properties(jpaProperties.getProperties())
.packages("com.yanger.jpa")
//持久化单元名称,当存在多个EntityManagerFactory时,需要制定此名称
.persistenceUnit("masterPersistenceUnit")
.build();
}
@Bean("masterEntityManager")
@Primary
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactoryBean(builder).getObject().createEntityManager();
}
@Bean("masterTransactionManager")
@Primary
public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactoryBean(builder).getObject());
}
}
配置好了masterDataSource数据源的JPA属性,接下来就是slaveDataSource的了。slave相关配置和master差不多,使用了不同的数据源,不需要@Primary修饰,当然作用的包basePackages需要不一样。
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "slaveEntityManagerFactory", //配置连接工厂 entityManagerFactory
transactionManagerRef = "slaveTransactionManager", //配置事物管理器 transactionManager
basePackages = {"com.yanger.jpa.sdao"}
)
public class SlaveJpaConfig {
@Autowired
@Qualifier("slaveDataSource")
private DataSource slaveDataSource;
@Autowired
private JpaProperties jpaProperties;
@Bean("slaveEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(EntityManagerFactoryBuilder builder) {
return builder.dataSource(slaveDataSource)
.properties(jpaProperties.getProperties())
.packages("com.yanger.jpa")
//持久化单元名称,当存在多个EntityManagerFactory时,需要制定此名称
.persistenceUnit("slavePersistenceUnit")
.build();
}
@Bean("slaveEntityManager")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactoryBean(builder).getObject().createEntityManager();
}
@Bean("slaveTransactionManager")
public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactoryBean(builder).getObject());
}
}
这样接下来就可以使用JPA进行不用的数据源操作了,因为对应不同包下的dao会采用不同的数据源及JPA配置。在该演示项目中,master作用的包为com.yanger.jpa.dao,有IUserDao,而slave作用的则是com.yanger.jpa.sdao,创建ISlaveUserDao。
package com.yanger.jpa.dao;
import com.yanger.jpa.po.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface IUserDao extends JpaRepository<User, Integer> {
/**
* 根据用户名和密码查询,根据方法名来生成实体类
* @param username
* @param password
* @return
*/
User findByUsernameAndPassword(String username, String password);
/**
* 根据用户名和密码查询
* @param username
* @param password
* @return
*/
@Query(nativeQuery = true, value = "select * from user where username = :username and password = :password")
User find(@Param("username") String username, @Param("password") String password);
}
package com.yanger.jpa.sdao;
import com.yanger.jpa.po.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ISlaveUserDao extends JpaRepository<User, Integer> {
}
在UserService里,我们分别注入IUserDao和ISlaveUserDao,addUser方法和addUserSlave使用了不同的Dao对象,则可以将数据存储到不同的数据源。
@Service
public class UserService {
@Autowired
private IUserDao userDao;
@Autowired
private ISlaveUserDao iSlaveUserDao;
/**
* 添加用户
* @param user
*/
// 需要指明value属性,否则会使用默认的事务管理,即@Primary声明的那一个
@Transactional(value = "masterTransactionManager")
public void addUser(User user){
userDao.save(user);
// 除零异常,测试事务
int a =1/0;
}
/**
* 添加用户
* @param user
*/
// 需要指明value属性,否则会使用默认的事务管理,即@Primary声明的那一个
@Transactional(value = "slaveTransactionManager")
public void addUserSlave(User user){
iSlaveUserDao.save(user);
// 除零异常,测试事务
int a =1/0;
}
}
源码地址:https://github.com/imyanger/springboot-project/tree/master/p13-springboot-muti-jpa