多数据源整合springdatJPA问题
多数据源整合springdatJPA问题
JavaConfig方式的多数据源(教程中多是这种解决方式)
这种方式的整体思路就是通过javaconfig读配置文件加载两个数据源,然后springdataJPA扫描不同的包,加载不同的数据源达到两种JPA配置。
pom文件
<!-- 引入 持久层框架Spring Data Jpa-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 引入mysql的jdbc驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<!-- 引入lombok,使用一些注解如@Data,可实现Bean的getter和setter方法-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 引入单元测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
配置文件
server:
port: 10088
spring:
datasource:
db1:
jdbc-url: jdbc:mysql://IP:3306/datasource01?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
db2:
jdbc-url: jdbc:mysql://IP:3306/datasource02?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
配置两个数据源
package com.config;
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;
import javax.sql.DataSource;
@Configuration
public class datasourceConfig {
@Primary
@Bean(value = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.db1")
public DataSource dataSourceOne(){
return DataSourceBuilder.create().build();
}
@Bean(value = "secondDataSource")
@ConfigurationProperties(prefix = "spring.datasource.db2")
public DataSource dataSourceTwo(){
return DataSourceBuilder.create().build();
}
}
配置JPAPrimaryConfig
package com.config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
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;
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
import java.util.Map;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = {"com.datasourceOnce.dao"},
entityManagerFactoryRef = "entityManagerFactoryPrimary",
transactionManagerRef = "transactionManagerPrimary"
)
public class JPAPrimaryConfig {
@Resource
private JpaProperties jpaProperties;
@Resource
private HibernateProperties hibernateProperties;
@Resource
@Qualifier("primaryDataSource")
private DataSource primaryDataSource;
@Primary
@Bean("entityManagerPrimary")
public EntityManager entityManager(EntityManagerFactoryBuilder builder){
return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
}
@Primary
@Bean(name = "entityManagerFactoryPrimary")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary (EntityManagerFactoryBuilder builder){
Map<String , Object> properties =
hibernateProperties.determineHibernateProperties(
jpaProperties.getProperties(),
new HibernateSettings()
);
return builder.dataSource(primaryDataSource)
.properties(properties)
.packages("com.datasourceOnce.entity") // 第一个数据源的 domain实体类包 所在位置
.persistenceUnit("primaryPersistenceUnit")
.build();
}
@Primary
@Bean(name = "transactionManagerPrimary")
public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder){
return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
}
}
配置JPASecondaryConfig
package com.config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
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;
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
import java.util.Map;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactorySecondary",
transactionManagerRef = "transactionManagerSecondary",
basePackages = {"com.datasourceTwo.dao"} // 第二个数据源的 repository包 所在位置
)
public class JPASecondaryConfig {
@Resource
private JpaProperties jpaProperties;
@Resource
private HibernateProperties hibernateProperties;
@Resource
@Qualifier("secondDataSource")
private DataSource secondaryDataSource;
@Bean(name = "entityManagerSecondary")
public EntityManager entityManager(EntityManagerFactoryBuilder builder){
return entityManagerFactorySecondary(builder).getObject().createEntityManager();
}
@Bean(name = "entityManagerFactorySecondary")
public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary (EntityManagerFactoryBuilder builder){
Map<String , Object> properties =
hibernateProperties.determineHibernateProperties(
jpaProperties.getProperties(),
new HibernateSettings()
);
return builder.dataSource(secondaryDataSource)
.properties(properties)
.packages("com.datasourceTwo.entity") // 第二个数据源的 domain实体类包 所在位置
.persistenceUnit("secondaryPersistenceUnit")
.build();
}
@Bean(name = "transactionManagerSecondary")
public PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder){
return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
}
}
UserEntity
package com.datasourceOnce.entity;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
@Data
@Entity(name = "User")
public class User {
@Id
private String id;
private String name;
}
UserDao
package com.datasourceOnce.dao;
import com.datasourceOnce.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserDao extends JpaRepository<User,String>{
User findByName(String name);
}
另一个TeacherEntity和TeacherDao和User的基本相同这里就不加了
测试
package com;
import com.datasourceOnce.dao.*;
import com.datasourceOnce.entity.User;
import com.datasourceTwo.dao.TeacherDao;
import com.datasourceTwo.entity.Teacher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.xml.transform.Source;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class TestTheApi {
@Autowired
private TeacherDao teacherDao;
@Autowired
private UserDao userDao;
@Test
public void getUserAndTeacher(){
User user = userDao.findByName("123");
System.out.println(user);
Teacher teacher = new Teacher();
teacher.setPassword("123");
teacher.setId("1");
teacherDao.saveAndFlush(teacher);
Teacher teacher1 = teacherDao.findByPassword("123");
System.out.println(teacher1);
}
}
项目整体结构如图所示
dynamic-datasource-spring-boot-starter
是一个基于 springboot 的快速集成多数据源的启动器。使用mybatis-plus是同一个开发者,可以通过@DS注解切换不同的数据库,同时多数据源还可以有很多组合方式,数据源还可以分组,调配的时候会根据负载均衡自动选择数据库处理。
dynamic作为一种非常方便的多数据源的方式,可以在配置不同的JPA的时候根据DynamicRoutingDataSource
这个类拿到不同的数据源,等同于第一种方式。
POM文件
只需要在原来的基础上加上
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
配置文件
server:
port: 10088
spring:
datasource:
dynamic:
primary: db1 #设置默认的数据源或者数据源组,默认值即为master
strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源。
datasource:
db1:
jdbc-url: jdbc:mysql://IP:3306/datasource01?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
db2:
jdbc-url: jdbc:mysql://IP:3306/datasource02?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
jpa:
show-sql: true
hibernate:
ddl-auto: update
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
JPAPrimaryConfig
package com.config;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.ApplicationContext;
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;
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import java.util.Map;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = {"com.datasourceOnce.dao"},
entityManagerFactoryRef = "entityManagerFactoryPrimary",
transactionManagerRef = "transactionManagerPrimary"
)
public class JPAPrimaryConfig {
@Resource
private JpaProperties jpaProperties;
@Resource
private HibernateProperties hibernateProperties;
@Autowired
private ApplicationContext applicationContext;
@Primary
@Bean("entityManagerPrimary")
public EntityManager entityManager(EntityManagerFactoryBuilder builder){
return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
}
@Primary
@Bean(name = "entityManagerFactoryPrimary")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary (EntityManagerFactoryBuilder builder){
Map<String , Object> properties =
hibernateProperties.determineHibernateProperties(
jpaProperties.getProperties(),
new HibernateSettings()
);
DynamicRoutingDataSource routingDataSource = applicationContext.getBean(DynamicRoutingDataSource.class);
return builder.dataSource(routingDataSource.getDataSource("db1"))
.properties(properties)
.packages("com.datasourceOnce.entity") // 第一个数据源的 domain实体类包 所在位置
.persistenceUnit("primaryPersistenceUnit")
.build();
}
@Primary
@Bean(name = "transactionManagerPrimary")
public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder){
return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
}
}
JPASecondaryConfig
package com.config;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.ApplicationContext;
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;
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import java.util.Map;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactorySecondary",
transactionManagerRef = "transactionManagerSecondary",
basePackages = {"com.datasourceTwo.dao"} // 第二个数据源的 repository包 所在位置
)
public class JPASecondaryConfig {
@Resource
private JpaProperties jpaProperties;
@Resource
private HibernateProperties hibernateProperties;
@Autowired
private ApplicationContext applicationContext;
@Bean(name = "entityManagerSecondary")
public EntityManager entityManager(EntityManagerFactoryBuilder builder){
return entityManagerFactorySecondary(builder).getObject().createEntityManager();
}
@Bean(name = "entityManagerFactorySecondary")
public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary (EntityManagerFactoryBuilder builder){
Map<String , Object> properties =
hibernateProperties.determineHibernateProperties(
jpaProperties.getProperties(),
new HibernateSettings()
);
DynamicRoutingDataSource routingDataSource = applicationContext.getBean(DynamicRoutingDataSource.class);
return builder.dataSource(routingDataSource.getDataSource("db2"))
.properties(properties)
.packages("com.datasourceTwo.entity") // 第二个数据源的 domain实体类包 所在位置
.persistenceUnit("secondaryPersistenceUnit")
.build();
}
@Bean(name = "transactionManagerSecondary")
public PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder){
return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
}
}
整体项目结构
其他相同
OAQ:
jdbcUrl is required with driverClassName问题
spring.datasource.primary.url=jdbc:mysql://localhost:3306/study?useUnicode=true&characterEncoding=utf-8&useSSL=true
改为
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/study?useUnicode=true&characterEncoding=utf-8&useSSL=true
'hibernate.dialect' not set
Hibernate SQL方言没有设置导致的,在properties文件中增加对应方言,我这里是mysqlInnoDB:
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect