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 Agent有哪些?

    常见的Java Agent有如下几种: ByteBuddy:基于Java字节码增强库,可以实现类加载的字节码增强。 ASM:一个轻量级Java字节码操作库,ASM可以动态生成类、方法或 Field,或者对现有类进行操作。 Javassist:Java字节码操作库,它可以在字节码层面上修改Java程序。 Instrument:Java的一个API,可以在运行时…

    Java 2023年5月11日
    00
  • Java实现时间日期格式转换示例

    我来为你详细讲解Java实现时间日期格式转换的完整攻略。 什么是时间日期格式转换? 在Java开发中,经常需要对时间日期进行格式转化。例如,将日期对象转化为指定格式的字符串,或将字符串转化为日期对象,然后才能进行后续的业务处理。时间日期格式转换涉及到Java中日期类库的使用,如java.util.Date和java.time.LocalDateTime等。 …

    Java 2023年5月20日
    00
  • 详解Tomcat中查看JVM内存使用情况

    当我们使用Tomcat作为Web服务器时,我们需要时刻关注JVM内存的使用情况。在Tomcat的管理界面中提供了一个内置的功能,能够帮助我们查看JVM内存的使用情况。下面是更详细的操作步骤。 第一步:进入Tomcat的管理页面 通过浏览器进入Tomcat服务器的管理页面,一般情况下,默认情况下地址为:http://localhost:8080/manager…

    Java 2023年5月19日
    00
  • java 内部类(匿名类,匿名对象,静态内部类)详解及实例

    Java内部类(匿名类,匿名对象,静态内部类)详解及实例 Java内部类是一个嵌套在其他类中的类,内部类可以访问外部类的所有成员(包括私有成员),并且可以用来实现一些特殊的功能。Java内部类通常分为四种类型:成员内部类、局部内部类、匿名内部类和静态内部类。 成员内部类 成员内部类是定义在外部类的内部,并且不是 static 的内部类。成员内部类可以访问外部…

    Java 2023年5月26日
    00
  • Spring session 获取当前账户登录数的实例代码

    Spring Session 是 Spring 提供的用于在分布式系统中管理用户会话信息的解决方案。通过使用 Spring Session,我们可以将用户的会话信息存储在外部存储中,实现会话状态在多个应用之间的共享,从而解决多个应用之间无法共享会话状态的问题。 在 Spring Session 中,我们可以使用 SessionRegistry 接口来获取当前…

    Java 2023年6月16日
    00
  • 如何用java编写一个rmi

    下面是详细讲解如何用Java编写一个RMI服务的完整攻略: 1. 什么是RMI RMI(Remote Method Invocation)是Java中一种远程机制,允许在不同的Java虚拟机间,通过网络传输调用远程对象的方法。 2. 实现RMI服务的步骤 2.1 编写业务接口 首先需要编写业务接口,声明远程调用时需要使用的方法,例如: public inte…

    Java 2023年5月19日
    00
  • 一文搞懂Java JDBC中的SQL注入问题

    一文搞懂Java JDBC中的SQL注入问题 什么是SQL注入? SQL注入是指攻击者利用客户端向服务器传递的SQL语句中的漏洞,注入恶意的查询语句或其他可执行的操作,从而获得非法的访问或者篡改数据的行为。SQL注入攻击是应用程序中最常见的攻击之一。 为什么要防止SQL注入? SQL注入攻击会使应用程序不按照设计来执行SQL语句,因而破坏了应用程序的安全性。…

    Java 2023年6月16日
    00
  • 5分钟快速学会spring boot整合Mybatis的方法

    5分钟快速学会Spring Boot整合MyBatis的方法 Spring Boot是一个流行的Java框架,可以帮助开发人员快速构建和部署应用程序。MyBatis是一个流行的Java持久化框架,可以帮助开发人员管理数据库。在本文中,将详细讲解如何使用Spring Boot整合MyBatis,以便在开发过程中更加高效和便捷。 步骤1:添加依赖关系 首先,我们…

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