​​​​​​​Spring多租户数据源管理 AbstractRoutingDataSource

下面我就来详细讲解一下“Spring多租户数据源管理 AbstractRoutingDataSource”的完整攻略。

什么是多租户数据源管理

在多租户系统中,一份应用程序服务多个用户,每个用户有自己独立的数据。多租户数据源解决了这个问题,通过配置多个数据源,根据不同的用户请求来动态选取对应的数据源,从而实现对多租户数据的支持。

AbstractRoutingDataSource

Spring提供了一个实现多租户数据源管理的类AbstractRoutingDataSource,它是一个抽象类,通过继承该类并实现determineCurrentLookupKey方法,就能实现自己的动态数据源。

determineCurrentLookupKey()方法作用是获取当前的数据源key,然后根据key去获取对应的数据源,每个key对应一个对应的数据源。

具体来说,动态数据源管理需要以下两步:

  1. 在项目中,自定义一个类继承AbstractRoutingDataSource;

  2. 实现determineCurrentLookupKey()方法,该方法根据请求参数、session或者其他规则来确定对应的数据源key。

简单来说,AbstractRoutingDataSource 负责管理一组数据源,并提供了一种机制,使得在访问数据源时,能够动态选择其中的一个来使用。

AbstractRoutingDataSource代码示例

下面是一个使用 AbstractRoutingDataSource 的示例代码。

  1. 自定义类继承 AbstractRoutingDataSource
public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceType();
    }
}
  1. 配置 AbstractRoutingDataSource
@Configuration
public class DynamicDataSourceConfig {

    @Bean
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        // 配置 targetDataSources,key 为数据源 key,value 为对应的数据源 DataSource
        targetDataSources.put("data1", dataSource1());
        targetDataSources.put("data2", dataSource2());
        dynamicDataSource.setTargetDataSources(targetDataSources);
        // 设置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(dataSource1());
        return dynamicDataSource;
    }

    @Bean(name = "dataSource1")
    @ConfigurationProperties(prefix = "spring.datasource.db1")
    public DataSource dataSource1() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "dataSource2")
    @ConfigurationProperties(prefix = "spring.datasource.db2")
    public DataSource dataSource2() {
        return DataSourceBuilder.create().build();
    }

}

在上面的示例中,我们定义了一个 DynamicDataSource 类,它继承了 AbstractRoutingDataSource 类,然后实现了该类的 determineCurrentLookupKey() 抽象方法。在该方法中,我们使用了一个名为"DataSourceContextHolder"的类来获取数据源 key。

DataSourceContextHolder是一个自定义的类,它使用 ThreadLocal 来存储当前线程使用的数据源 key。当需要切换数据源时,只需要调用 DataSourceContextHolder.setCurrentDataSource(key) 方法即可。

另外,我们在 DynamicDataSourceConfig 配置类中,定义了 dataSource1 和 dataSource2 两个数据源,并将它们注册到了动态数据源 DynamicDataSource 中。

AbstractRoutingDataSource示例2

下面是一个更加详细的示例,它演示了如何使用 Annotation 实现动态数据源的在不同方法中进行切换。

1. 添加注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String value() default "data1";
}

2. 自定义类继承 AbstractRoutingDataSource

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceType();
    }
}

3. 自定义ContextHolder类

public class DataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    public static String getDataSourceType() {
        return contextHolder.get();
    }

    public static void clearDataSourceType() {
        contextHolder.remove();
    }
}

4. 切面

@Aspect
@Component
public class DynamicDataSourceAspect {

    @Around("@annotation(ds)")
    public Object around(ProceedingJoinPoint point, DataSource ds) throws Throwable {
        String datasourceKey = ds.value();
        if (!DynamicDataSourceContextHolder.containsDataSource(datasourceKey)) {
            System.err.println("数据源[{}]不存在,使用默认数据源 > {}" + ds.value() + point.getSignature());
        } else {
            System.out.println("Use DataSource : {} > {}"+ ds.value() + point.getSignature());
            DynamicDataSourceContextHolder.setDataSourceType(datasourceKey);
        }

        try {
            return point.proceed();
        } finally {
            DynamicDataSourceContextHolder.clearDataSourceType();
            System.out.println("清空数据源信息 > {}");
        }
    }
}

5. 工具类

public class DataSourceEnum {
    public enum DataSourceType {
        DataSource1("data1"),
        DataSource2("data2");

        private String type;

        DataSourceType(String type) {
            this.type = type;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }
    }
}
public class DynamicDataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    public static String getDataSourceType() {
        return contextHolder.get();
    }

    public static void clearDataSourceType() {
        contextHolder.remove();
    }

    public static boolean containsDataSource(String dataSourceId) {
        return dataSourceIds.contains(dataSourceId);
    }

    public static List<String> getDataSourceIds() {
        return dataSourceIds;
    }

    public static void addDataSourceIds(String dataSourceId) {
        LOG.info("Register datasource : {}", dataSourceId);
        dataSourceIds.add(dataSourceId);
    }

    public static void addDataSourceIds(Collection<String> dataSourceIds) {
        DynamicDataSourceContextHolder.dataSourceIds.addAll(dataSourceIds);
    }

    public static void removeDataSourceIds(String dataSourceId) {
        LOG.info("Remove datasource : {}", dataSourceId);
        dataSourceIds.remove(dataSourceId);
    }

    public static void clearDataSourceIds() {
        dataSourceIds.clear();
    }

    public static void setDataSourceIds(Collection<String> dataSourceIds) {
        clearDataSourceIds();
        addDataSourceIds(dataSourceIds);
    }
}

6. 测试代码

@SpringBootTest(classes = Springboot2MybatisplusApplication.class)
@RunWith(SpringRunner.class)
public class DynamicDataSourceTest {

    @Autowired
    private DynamicDataSourceMapper mapper;

    @Test
    public void testInsert() {
        User user = new User();
        user.setName("name");
        userMapper.insert(user);
    }

    @Test
    @DataSource(DataSourceEnum.DataSourceType.DataSource1)
    public void testInsertWithDataSource1() {
        User user = new User();
        user.setName("name");
        userMapper.insert(user);
    }

    @Test
    @DataSource(DataSourceEnum.DataSourceType.DataSource2)
    public void testInsertWithDataSource2() {
        User user = new User();
        user.setName("name");
        userMapper.insert(user);
    }
}

在上面的示例代码中,我们使用了两个数据库:DataSource1 和 DataSource2。在使用 DynamicDataSourceContextHolder 类的 addDataSourceIds 方法注册需要使用的 DataSource 的 key。

在测试代码中,我们通过使用已经定义好的 DataSource 注解,告诉 Spring 当前需要使用哪个数据源。比如,在 testInsertWithDataSource1 方法上添加注解@DataSource(DataSourceEnum.DataSourceType.DataSource1),那么在该方法执行时,就会使用 DataSource1 数据源。

总结

这就是使用 AbstractRoutingDataSource 实现动态数据源的完整攻略。通过使用此方法,可以支持在一个应用程序中使用多个数据库,并且允许在应用程序运行时切换不同的数据源,从而实现多租户数据源管理的功能。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:​​​​​​​Spring多租户数据源管理 AbstractRoutingDataSource - Python技术站

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

相关文章

  • 安装IDEA和配置Maven的步骤详解

    安装IDEA和配置Maven的步骤详解 一、安装IDEA 下载IntelliJ IDEA安装包:在JetBrains官网下载对应操作系统的IntelliJ IDEA Ultimate版本或Community版本的安装包,地址为:https://www.jetbrains.com/idea/download/#section=windows 安装Intelli…

    Java 2023年5月20日
    00
  • java中JDBC实现往MySQL插入百万级数据的实例代码

    我将为你详细介绍Java中JDBC实现往MySQL插入百万级数据的攻略,包括以下内容: JDBC简介 JDBC连接MySQL数据库的步骤 插入百万级数据的实现步骤 两条示例 1. JDBC简介 JDBC(Java Database Connectivity)是Java平台中用于执行SQL语句的一组API。使用JDBC可以连接各种各样的关系型数据库,如MySQ…

    Java 2023年5月20日
    00
  • Sprint Boot @EnableScheduling使用方法详解

    Spring Boot的@EnableScheduling注解 在Spring Boot中,@EnableScheduling注解用于启用定时任务的支持。使用@EnableScheduling注解可以将带有@Scheduled注解的方法标记为定时任务,并在应用程序启动时自动注册这些任务。本文将详细介绍@EnableScheduling注解的作用和使用方法,并…

    Java 2023年5月5日
    00
  • 使用Java获取Json中的数据简单示例

    下面是使用Java获取Json中的数据简单示例的完整攻略: 什么是Json? Json(全称JavaScript Object Notation)是一种轻量级的数据交换格式。Json格式数据可以使用在不同的编程语言中进行数据传递,包括Java。 Json数据可以被编码为一个字符串,并在各种网络上传输或存储。在Java中可以使用Json库来解析和生成Json数…

    Java 2023年5月28日
    00
  • SpringMvc静态资源访问实现方法代码实例

    在SpringMVC中,我们可以通过配置来实现静态资源的访问。下面我们将详细介绍SpringMVC静态资源访问的实现方法,并提供两个示例来说明这些方法的使用。 配置静态资源访问 在SpringMVC中,我们可以通过配置ResourceHandlerRegistry对象来实现静态资源的访问。以下是一个简单的示例: @Configuration @EnableW…

    Java 2023年5月17日
    00
  • java 利用HttpClient PostMethod提交json数据操作

    下面是详细讲解Java利用HttpClient PostMethod提交JSON数据操作的完整攻略: 1. 导入HttpClient依赖 首先需要在项目中使用HttpClient库,可以使用Maven等方式导入依赖,例如: <dependency> <groupId>org.apache.httpcomponents</grou…

    Java 2023年5月26日
    00
  • Java Spring Boot实战练习之单元测试篇

    以下是”Java Spring Boot实战练习之单元测试篇”的完整攻略,包含了步骤和示例。 1. 什么是单元测试 单元测试是一种测试方法,它用于测试整个系统或应用程序的一个单独模块或方法。单元测试是一种自动化测试方法,它能够验证代码、避免错误和缺陷,并将问题隔离到特定的代码块层面上。 2. 创建一个Spring Boot项目 在开始单元测试之前,需要先创建…

    Java 2023年5月19日
    00
  • Spring MVC中@Controller和@RequestMapping注解使用

    在Spring MVC中,@Controller和@RequestMapping是两个重要的注解,它们用于定义控制器和请求映射。本文将详细介绍@Controller和@RequestMapping注解的使用方法,并提供两个示例来说明这些方法的使用。 @Controller注解 @Controller注解用于定义控制器类。在Spring MVC中,控制器类负责…

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