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实现读取、删除文件夹下的文件

    关于Java实现读取、删除文件夹下的文件的攻略,可以分为两个步骤:读取和删除文件。 1. 读取文件 Java中读取文件需要使用File类,它提供了各种方法来处理文件和文件夹。使用File类的方法之一是listFiles(),该方法用于获取在文件夹中的所有文件和文件夹的列表。我们可以使用该方法获得要操作的文件夹下面的所有文件或文件夹。 以下是一个读取文件夹下所…

    Java 2023年5月20日
    00
  • Java数据库连接池之c3p0简介_动力节点Java学院整理

    Java数据库连接池之c3p0简介 Java数据库连接池之c3p0简介_动力节点Java学院整理是一篇介绍Java数据库连接池技术的文章,其中以c3p0作为具体实现工具进行详细阐述。本文将对该文进行一些补充说明和总结。 1. 什么是数据库连接池? 数据库连接池是实现高效、可靠、可扩展的数据库访问的一种重要技术。在应用系统中,不同的客户端请求需要访问数据库,每…

    Java 2023年6月1日
    00
  • 一文带你了解SpringBoot的启动原理

    一文带你了解SpringBoot的启动原理 1. 介绍 Spring Boot是Spring团队开发的一套快速构建Spring应用的框架,它致力于简化Spring应用的开发、单元测试和部署等工作。而Spring Boot的启动原理在其快速构建的应用背后扮演着至关重要的角色。 本文将讲解一些Spring Boot中启动原理的细节,帮助读者更好的理解Spring…

    Java 2023年5月31日
    00
  • Java实现简单局域网聊天室

    Java实现简单局域网聊天室攻略 在本文中,我将向您展示如何使用Java语言实现一个简单的局域网聊天室。我们将使用Java的Socket API进行通信。 第一步:创建服务器 我们将从创建服务器开始。服务器将超时等待客户端的连接。一旦客户端连接,服务器将创建一个新的线程对该客户端进行处理。 import java.io.IOException; import…

    Java 2023年5月19日
    00
  • Java集合之Set接口及其实现类精解

    Java集合之Set接口及其实现类精解 Set接口是Java集合框架中的一种无序集合,它只能包含不重复的元素。本文将会详细讲解Set接口及其实现类的特点和使用方法。 Set接口 Set接口是Java集合框架中的一个接口,它继承了Collection接口,表示一个不允许重复元素的无序集合。Set接口中定义了以下常用的方法: add(E e):添加指定元素到集合…

    Java 2023年5月18日
    00
  • Java编程构造方法与对象的创建详解

    Java编程构造方法与对象的创建详解 在Java编程中,构造方法和对象是非常重要的概念。本文将详细介绍Java编程中的构造方法和对象的创建过程。 构造方法 构造方法是一种特殊的方法,用于在创建对象时初始化对象。每个类都有一个构造方法,如果没有显式地定义,则会有一个默认构造方法。构造方法的名称必须与类名相同,其没有返回值类型,并且不能用于返回值。 定义构造方法…

    Java 2023年5月26日
    00
  • Spring Boot 添加MySQL数据库及JPA实例

    下面是详细的“Spring Boot 添加MySQL数据库及JPA实例”的攻略。 1. 准备工作 安装Java和MySQL 新建Spring Boot项目(可使用IntelliJ IDEA等集成开发环境) 2. 添加MySQL依赖 在pom.xml文件中添加mysql-connector-java和spring-boot-starter-data-jpa依赖…

    Java 2023年5月20日
    00
  • 老生常谈java中的数组初始化

    下面是关于Java中数组初始化的完整攻略: 数组的定义与声明 在Java中,数组需要先定义后使用。数组的定义语法如下: type[] arrayName; 其中,type 表示数组中元素的数据类型,大括号 [] 表示数组类型,arrayName 是数组的变量名。例如,定义一个整型数组变量的代码如下: int[] nums; 定义好数组变量之后,需要声明数组的…

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