下面是Spring Boot+Jpa多数据源配置的完整攻略:
配置文件
首先需要在application.properties
或者 application.yml
配置文件中进行多数据源的配置。示例如下:
# 数据源 1
spring.datasource.first.url=jdbc:mysql://localhost:3306/first_database?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.first.username=root
spring.datasource.first.password=root
# 数据源 2
spring.datasource.second.url=jdbc:mysql://localhost:3306/second_database?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.second.username=root
spring.datasource.second.password=root
其中,spring.datasource
可以设置多组数据源。这里,我们配置了两个数据源,分别为 first
和 second
。
配置类
其次,需要创建数据源配置类,继承自 AbstractRoutingDataSource
,实现 determineCurrentLookupKey()
方法,以决定使用哪个数据源。示例如下:
@Configuration
public class DynamicDataSourceConfig {
// 第一种方法:配置多个DataSource,并将其注入到 DynamicDataSource 中
@Bean(name = "firstDataSource")
@ConfigurationProperties(prefix = "spring.datasource.first")
public DataSource dataSourceOne() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondDataSource")
@ConfigurationProperties(prefix = "spring.datasource.second")
public DataSource dataSourceTwo() {
return DataSourceBuilder.create().build();
}
@Bean(name = "dynamicDataSource")
public DynamicDataSource dataSource(@Qualifier("firstDataSource") DataSource firstDataSource,
@Qualifier("secondDataSource") DataSource secondDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("first", firstDataSource);
targetDataSources.put("second", secondDataSource);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);
// 设置默认数据源为 first 数据源
dataSource.setDefaultTargetDataSource(firstDataSource);
return dataSource;
}
// 第二种方法:通过@Bean注解和AOP切面来动态切换数据源
/**
* 注册动态数据源
*/
@Bean(name = "dynamicDataSource")
public DynamicDataSource dynamicDataSource(@Qualifier("firstDataSource") DataSource firstDataSource,
@Qualifier("secondDataSource") DataSource secondDataSource) {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>(2);
dataSourceMap.put(DataSourceType.FIRST.getDbType(), firstDataSource);
dataSourceMap.put(DataSourceType.SECOND.getDbType(), secondDataSource);
// 将 main 数据源作为默认指定的数据源
dynamicDataSource.setDefaultTargetDataSource(firstDataSource);
// 将一组数据源注册到该数据源当中
dynamicDataSource.setTargetDataSources(dataSourceMap);
return dynamicDataSource;
}
/**
* 配置 AOP 切面类,通过该 AOP 切面类进行动态切换数据源
*/
@Bean
public DataSourceAspect dataSourceAspect() {
return new DataSourceAspect();
}
/**
* 配置 JPA
*/
@Bean
public JpaProperties jpaProperties() {
return new JpaProperties();
}
}
其中,DynamicDataSource
类是自定义的动态数据源类,DataSourceAspect
类是用于动态切换数据源的 AOP 切面类。
动态切换数据源
最后,需要根据业务需求,在对应的方法上添加 @TargetDataSource
注解,实现动态切换数据源。示例如下:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@TargetDataSource(dataSourceType = DataSourceType.FIRST)
@Override
public User save(User user) {
return userRepository.save(user);
}
@TargetDataSource(dataSourceType = DataSourceType.SECOND)
@Override
public User findByName(String name) {
return userRepository.findByName(name);
}
}
其中,@TargetDataSource
是自定义的注解,用于指示数据源类型。
示例
下面,给出两个示例:
示例1:基于 DataSourceBuilder 和 AbstractRoutingDataSource 的配置方式
// 定义 DynamicRoutingDataSourceConfig 类,用于注册多个数据源并将其注入到 DynamicRoutingDataSource 中
@Configuration
public class DynamicRoutingDataSourceConfig {
/**
* 定义两个数据源,分别为 master 和 slave
*/
@Bean(name = "master")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource master() {
return DataSourceBuilder.create().build();
}
@Bean(name = "slave")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slave() {
return DataSourceBuilder.create().build();
}
/**
* 定义 DynamicRoutingDataSource 类,用于实现多数据源动态路由
*/
@Bean(name = "dynamicDataSource")
public DynamicRoutingDataSource dynamicRoutingDataSource(@Qualifier("master") DataSource master,
@Qualifier("slave") DataSource slave) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER, master);
targetDataSources.put(DataSourceType.SLAVE, slave);
DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
// 设置数据源映射
dataSource.setTargetDataSources(targetDataSources);
// 设置默认数据源为 master 数据源
dataSource.setDefaultTargetDataSource(master);
return dataSource;
}
/**
* 配置事务管理器
*/
@Bean(name = "transactionManager")
public DataSourceTransactionManager dataSourceTransactionManager(
@Qualifier("dynamicDataSource") DynamicRoutingDataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
/**
* 配置 JPA 特定属性
*/
@Bean(name = "jpaProperties")
public JpaProperties jpaProperties() {
return new JpaProperties();
}
}
// 定义 DynamicRoutingDataSource 类,继承自 AbstractRoutingDataSource 类,实现动态路由功能
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbType();
}
}
// 定义 DbContextHolder 类,用于动态切换数据源
public class DbContextHolder {
private static final ThreadLocal<String> local = new ThreadLocal<>();
public static ThreadLocal<String> getLocal() {
return local;
}
public static void setDbType(String dbType) {
local.set(dbType);
}
public static String getDbType() {
return local.get();
}
public static void clearDbType() {
local.remove();
}
}
// 定义 DataSourceType 枚举值,用于标记数据源类型
public enum DataSourceType {
MASTER("master"),
SLAVE("slave");
private final String dbType;
DataSourceType(String dbType) {
this.dbType = dbType;
}
public String getDbType() {
return dbType;
}
}
// 定义 UserService 接口和其实现类 UserServiceImpl,用于测试
public interface UserService {
User save(User user);
User findById(Long id);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
@TargetDataSource(dataSourceType = DataSourceType.MASTER)
public User save(User user) {
return userRepository.save(user);
}
@Override
@TargetDataSource(dataSourceType = DataSourceType.SLAVE)
public User findById(Long id) {
return userRepository.findById(id).get();
}
}
示例2:基于 AOP 和 BeanPostProcessor 的配置方式
// 定义 DataSource 开头的类,用于指定数据源类型
public class DataSource {
public static final String MASTER = "master";
public static final String SLAVE = "slave";
}
// 定义 DataSourceContextHolder 类,用于动态切换数据源
public class DataSourceContextHolder {
private static final ThreadLocal<String> local = new ThreadLocal<>();
public static void setDataSourceType(String dataSourceType) {
local.set(dataSourceType);
}
public static String getDataSourceType() {
return local.get();
}
public static void clearDataSourceType() {
local.remove();
}
}
// 定义 DataSourceAspect 类,实现 AOP 切面类
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("execution(* cn.giteasy.blog.service.impl.*.*(..))")
public void dataSourcePointcut() {}
@Before("dataSourcePointcut()")
public void switchDataSource(JoinPoint point) {
String dataSource = DataSource.MASTER; // 默认为主库
if (point.getSignature() instanceof MethodSignature) {
MethodSignature signature = (MethodSignature) point.getSignature();
if (signature.getMethod().isAnnotationPresent(TargetDataSource.class)) { // 方法上有该注解,则获取注解中的数据源类型
TargetDataSource targetDataSource = signature.getMethod().getAnnotation(TargetDataSource.class);
dataSource = targetDataSource.dataSource();
}
}
DataSourceContextHolder.setDataSourceType(dataSource); // 设置当前线程使用的数据源类型
}
@After("dataSourcePointcut()")
public void restoreDataSource(JoinPoint point) {
DataSourceContextHolder.clearDataSourceType(); // 销毁当前线程使用的数据源类型
}
}
// 定义 DynamicDataSourceConfig 类,用于注册多个数据源和动态数据源,并将动态数据源注入到 JPA 中使用
@Configuration
public class DynamicDataSourceConfig {
/**
* 定义主库和从库数据源
*/
@Bean(name = "master")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "slave")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
/**
* 定义注册动态数据源
*/
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource(@Qualifier("master") DataSource masterDataSource, @Qualifier("slave") DataSource slaveDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSource.MASTER, masterDataSource);
targetDataSources.put(DataSource.SLAVE, slaveDataSource);
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
return dynamicDataSource;
}
/**
* 定义 JPA 配置
*/
@Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(JpaProperties jpaProperties,
@Qualifier("dynamicDataSource") DataSource dynamicDataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dynamicDataSource);
entityManagerFactory.setPackagesToScan(PACKAGE_PREFIX);
entityManagerFactory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactory.setJpaPropertyMap(jpaProperties.getProperties());
return entityManagerFactory;
}
/**
* 配置 JPA 事务管理器
*/
@Bean(name = "transactionManager")
public JpaTransactionManager transactionManager(@Qualifier("entityManagerFactory") LocalContainerEntityManagerFactoryBean entityManagerFactory) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory.getObject());
return jpaTransactionManager;
}
}
// 定义 DynamicDataSource 类,继承自 AbstractRoutingDataSource 类,实现动态路由
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
// 定义 TargetDataSource 注解,用于标记数据源类型
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String dataSource() default DataSource.MASTER;
}
// 定义 UserService 接口和其实现类 UserServiceImpl,用于测试
public interface UserService {
User save(User user);
User findById(Long id);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
@Transactional
@TargetDataSource(dataSource = DataSource.SLAVE)
public User save(User user) {
return userRepository.save(user);
}
@Override
@TargetDataSource(dataSource = DataSource.MASTER)
public User findById(Long id) {
return userRepository.findById(id).get();
}
}
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Boot+Jpa多数据源配置的完整步骤 - Python技术站