Java多数据源如何切换

avatar
作者
猴君
阅读量:0

✅作者简介:热爱Java后端开发的一名学习者,大家可以跟我一起讨论各种问题喔。
🍎个人主页:Hhzzy99
🍊个人信条:坚持就是胜利!
💞当前专栏:项目实践
🥭本文内容:多数据源如何切换。

Java多数据源如何切换


文章目录


在现代企业级应用中,使用多个数据源来满足不同的需求是非常常见的。无论是为了提高性能、实现负载均衡、支持多租户架构,还是为了实现灾备恢复,动态切换数据源都是一项必备的技能。本文将详细介绍如何在Java中实现动态切换数据源,并通过具体的代码示例来讲解其实现原理。

前言

什么是数据源?

数据源(DataSource)是数据库连接的抽象,通常用于管理数据库连接池。通过数据源,应用程序可以轻松地获得数据库连接,而不必关心底层的数据库驱动和连接细节。在Java中,数据源接口由javax.sql.DataSource定义,常见的实现包括HikariCP、C3P0、DBCP等。

为什么需要动态切换数据源

动态切换数据源的需求来源于以下几个方面:

  1. 读写分离:在高并发环境中,通过将读操作和写操作分离到不同的数据库实例上,可以有效提高系统的性能和稳定性。
  2. 多租户支持:在多租户应用中,每个租户可能有独立的数据库实例,通过动态切换数据源,可以实现对不同租户数据的隔离。
  3. 故障恢复:在灾备场景下,当主数据库出现故障时,可以快速切换到备份数据库,确保系统的高可用性。

实现步骤

1. 引入依赖

在Maven项目的pom.xml文件中添加MyBatis、Spring Boot和数据源连接池的依赖:

<dependencies>     <!-- MyBatis-Spring-Boot-Starter -->     <dependency>         <groupId>org.mybatis.spring.boot</groupId>         <artifactId>mybatis-spring-boot-starter</artifactId>         <version>2.2.0</version>     </dependency>      <!-- Spring Boot Starter -->     <dependency>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter</artifactId>     </dependency>      <!-- HikariCP connection pool -->     <dependency>         <groupId>com.zaxxer</groupId>         <artifactId>HikariCP</artifactId>     </dependency>      <!-- MySQL driver -->     <dependency>         <groupId>mysql</groupId>         <artifactId>mysql-connector-java</artifactId>     </dependency> </dependencies>   

2. 配置数据源

在application.yml文件中配置多个数据源的信息:

spring:   datasource:     primary:       url: jdbc:mysql://localhost:3306/primary_db       username: root       password: password       driver-class-name: com.mysql.cj.jdbc.Driver     secondary:       url: jdbc:mysql://localhost:3306/secondary_db       username: root       password: password       driver-class-name: com.mysql.cj.jdbc.Driver mybatis:   mapper-locations: classpath*:mapper/*.xml   type-aliases-package: com.example.entity 

3 创建数据源配置类

创建一个配置类,定义两个数据源以及一个动态数据源:

import com.zaxxer.hikari.HikariDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;  import javax.sql.DataSource; import java.util.HashMap; import java.util.Map;  @Configuration @MapperScan(basePackages = "com.example.mapper", sqlSessionFactoryRef = "sqlSessionFactory") public class DataSourceConfig {      @Bean     @ConfigurationProperties("spring.datasource.primary")     public DataSource primaryDataSource() {         return new HikariDataSource();     }      @Bean     @ConfigurationProperties("spring.datasource.secondary")     public DataSource secondaryDataSource() {         return new HikariDataSource();     }      @Bean     public DataSource dynamicDataSource() {         DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();         Map<Object, Object> dataSourceMap = new HashMap<>();         dataSourceMap.put(DataSourceType.PRIMARY, primaryDataSource());         dataSourceMap.put(DataSourceType.SECONDARY, secondaryDataSource());         dynamicRoutingDataSource.setDefaultTargetDataSource(primaryDataSource());         dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);         return dynamicRoutingDataSource;     }      @Bean     public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) throws Exception {         SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();         sessionFactory.setDataSource(dynamicDataSource);         sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));         return sessionFactory.getObject();     } } 

4 定义数据源类型枚举

创建一个枚举类,用于标识不同的数据源:

public enum DataSourceType {     PRIMARY,     SECONDARY } 

5 实现动态路由数据源

创建一个继承AbstractRoutingDataSource的类,实现动态路由逻辑:

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;  public class DynamicRoutingDataSource extends AbstractRoutingDataSource {      private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<>();      public static void setDataSourceType(DataSourceType dataSourceType) {         contextHolder.set(dataSourceType);     }      public static DataSourceType getDataSourceType() {         return contextHolder.get();     }      public static void clearDataSourceType() {         contextHolder.remove();     }      @Override     protected Object determineCurrentLookupKey() {         return getDataSourceType();     } } 

6 配置数据源切换的AOP切面

使用AOP切面在方法调用前后进行数据源的切换:

import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component;  @Aspect @Component public class DataSourceAspect {      @Before("@annotation(dataSource)")     public void changeDataSource(JoinPoint joinPoint, DataSource dataSource) {         DataSourceType dataSourceType = dataSource.value();         DynamicRoutingDataSource.setDataSourceType(dataSourceType);     }      @After("@annotation(dataSource)")     public void clearDataSource(JoinPoint joinPoint, DataSource dataSource) {         DynamicRoutingDataSource.clearDataSourceType();     } } 

7 定义注解

创建一个自定义注解,用于标识需要切换数据源的方法:

import java.lang.annotation.*;  @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataSource {     DataSourceType value() default DataSourceType.PRIMARY; } 

8 配置MyBatis Mapper扫描

在数据源配置类中配置MyBatis的Mapper扫描:

@MapperScan(basePackages = "com.example.mapper") public class MyBatisConfig { } 

9 使用示例

在实际使用中,可以在需要切换数据源的方法上添加@DataSource注解。例如:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;  import java.util.List;  @Service public class UserService {      @Autowired     private UserMapper userMapper;      @DataSource(DataSourceType.SECONDARY)     public List<User> getUsersFromSecondary() {         return userMapper.findAll();     }      @DataSource(DataSourceType.PRIMARY)     public List<User> getUsersFromPrimary() {         return userMapper.findAll();     } } 

在Mapper接口中定义数据库操作:

import com.example.entity.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select;  import java.util.List;  @Mapper public interface UserMapper {     @Select("SELECT * FROM user")     List<User> findAll(); }  

在XML映射文件中配置SQL语句(如果你选择使用XML映射):

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  <mapper namespace="com.example.mapper.UserMapper">      <select id="findAll" resultType="com.example.entity.User">         SELECT * FROM user     </select>  </mapper> 

结语

通过上述步骤,我们使用MyBatis和Spring Boot实现了动态切换数据源的功能。MyBatis的灵活性和强大的SQL映射功能,使得我们可以在复杂的企业级应用中轻松地管理和操作数据库。同时,结合Spring的AbstractRoutingDataSource和AOP切面,我们可以实现更加灵活和高效的数据源切换方案。

希望本文对您有所帮助!如果有任何问题或建议,欢迎在评论区留言。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!