Spring Boot+Jpa多数据源配置的完整步骤

下面是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 可以设置多组数据源。这里,我们配置了两个数据源,分别为 firstsecond

配置类

其次,需要创建数据源配置类,继承自 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技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Java源码刨析之ArrayDeque

    Java源码刨析之ArrayDeque Java中的ArrayDeque是一种基于动态数组的双端队列数据结构。本篇文章将与读者一起深入分析Java中ArrayDeque的源代码,从中学习这种数据结构的实现原理。 容量扩充 由于使用动态数组来存储队列中的元素,因此在添加元素时,需要判断是否需要扩展数组的容量。容量扩充的代码实现如下: private void …

    Java 2023年5月26日
    00
  • hackathon 复盘:niche 海外软件工具正确的方法 6 个步骤

    上周末,去参加了北京思否 hackathon,两天时间内从脑暴 & 挖掘软件 IDEA -> Demo 研发路演,这次经历让我难忘。这里我的看法是每个开发者圈友,都应该去参加一次 hackathon ~ 做 niche 软件正确的方法 这边先说结论,如图。我认为 做 niche 软件正确的方法 或 6 个步骤 是: 发现用户的问题或痛点 明确问…

    Java 2023年4月22日
    00
  • 基于hibernate实现的分页技术实例分析

    下面我来详细讲解“基于hibernate实现的分页技术实例分析”的完整攻略。 什么是Hibernate分页技术? Hibernate分页实际上就是在一个查询语句中指定起始位置和查询数量,获取查询结果的过程。在大多数情况下,我们不可能将整张数据表中的所有数据都查询出来,这样不仅浪费时间和空间,而且会影响系统响应速度。所以,分页查询就成了一个很常见的需求。 如何…

    Java 2023年5月20日
    00
  • MySQL Packet for query is too large 问题及解决方法

    MySQL Packet for query is too large 是 MySQL 服务器返回的错误信息,意味着 MySQL 的查询语句太大,超出了 MySQL 服务器和客户端之间约定的协议数据包大小(默认为 16MB),导致服务器无法处理该查询请求。此时,我们需要进行以下措施来解决问题。 解决方法一:增加 max_allowed_packet 配置项的…

    Java 2023年6月16日
    00
  • JSP 不能解析EL表达式的解决办法

    JSP 是一种在 Java Web 应用程序中广泛使用的技术,它可以将文本、HTML、XML 和 Java 代码混合在同一个文件中。EL 表达式是 JSP 技术中一个重要的特性,它允许在 JSP 页面上轻松访问和操作 Java 对象。但是,在一些情况下,JSP 无法正确解析 EL 表达式,这会导致页面无法正确渲染。接下来,我们将介绍一些解决 JSP 无法解析…

    Java 2023年6月15日
    00
  • java如何用正则表达式匹配与提取字符串

    Java中使用正则表达式匹配和提取子字符串可以通过Java标准库中的Pattern和Matcher类实现。以下是使用正则表达式进行匹配和提取的攻略: 步骤1:创建正则表达式 首先需要创建一个正则表达式来匹配和提取字符串。正则表达式是由一些特殊字符和普通字符组成的模式,用于描述要匹配的字符串的形式。例如,正则表达式\d+ 可以匹配一个或多个数字。 步骤2:编译…

    Java 2023年5月27日
    00
  • Spring Boot集成Sorl搜索客户端的实现代码

    Spring Boot集成Solr搜索客户端的实现代码 Solr是一个开源的搜索引擎,可以用于全文检索、数据分析等场景。在使用Spring Boot进行开发时,我们可以使用Solr搜索客户端来实现搜索功能。本文将详细讲解Spring Boot集成Solr搜索客户端的实现代码的完整攻略,并提供两个示例。 1. 集成Solr搜索客户端 以下是集成Solr搜索客户…

    Java 2023年5月15日
    00
  • Spring与Struts整合之让Spring管理控制器操作示例

    首先介绍一下Spring和Struts的整合。在传统的Struts应用中,Struts DispatchAction负责将不同的请求分发给对应的Action进行处理。而在整合了Spring之后,Spring的IoC容器能够负责管理Struts的Action类,将这些Action类作为Spring的Bean进行管理,从而赋予了Struts更强大的扩展能力和灵活…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部