SpringBoot通过MyBatis实现基于Schema的多租户模式?

SpringBoot通过MyBatis实现基于Schema的多租户模式?

精选文章moguli202024-12-25 11:40:1022A+A-

使用MyBatis来实现基于Schema模式的多租户应用,可以结合之前博客中分享的内容做一些调整,多租户实现,需要基于MyBatis的配置进行一系列的调整配置数据源和多租户。下面我们就来详细介绍一下如何通过MyBatis实现多租户处理。

项目依赖

在项目的POM文件中添加相关的依赖配置,如下所示。

<dependencies>
    <!-- Spring Boot MyBatis -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.3.0</version>
    </dependency>

    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <!-- HikariCP for connection pool -->
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
    </dependency>
    
    <!-- PostgreSQL driver -->
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
    </dependency>
</dependencies>

配置数据库

在配置文件中添加连接数据库配置,如下所示。

spring.datasource.url=jdbc:postgresql://localhost:5432/multitenantdb
spring.datasource.username=your_db_user
spring.datasource.password=your_db_password
spring.datasource.driver-class-name=org.postgresql.Driver
mybatis.type-aliases-package=com.example.demo.domain
mybatis.mapper-locations=classpath:mapper/*.xml

实现多租户功能

创建TenantContext类,与通过JPA技术实现的方式类似,我们需要创建一个TenantContext类来保存当前请求的租户标识符,如下所示。

public class TenantContext {
    private static final ThreadLocal<String> CURRENT_TENANT = new ThreadLocal<>();

    public static void setTenant(String tenantId) {
        CURRENT_TENANT.set(tenantId);
    }

    public static String getTenant() {
        return CURRENT_TENANT.get();
    }

    public static void clear() {
        CURRENT_TENANT.remove();
    }
}

创建一个自定义的数据源MultiTenantDataSource来管理多租户数据源。这个数据源会根据当前租户设置连接到对应的Schema。

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

public class MultiTenantDataSource extends AbstractRoutingDataSource {

    public MultiTenantDataSource(DataSource defaultDataSource, Map<Object, Object> tenantDataSources) {
        setDefaultTargetDataSource(defaultDataSource);
        setTargetDataSources(tenantDataSources);
        afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return TenantContext.getTenant();
    }
}

配置DataSource和MyBatis

在DataSourceConfig类中配置数据源和MyBatis,如下所示。

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@MapperScan(basePackages = "com.example.demo.mapper")
public class DataSourceConfig {

    @Autowired
    private DataSource defaultDataSource;

    @Bean
    public MultiTenantDataSource dataSource() {
        Map<Object, Object> tenantDataSources = new HashMap<>();

        // 示例: 配置多个租户的数据源
        tenantDataSources.put("tenant1", createTenantDataSource("schema1"));
        tenantDataSources.put("tenant2", createTenantDataSource("schema2"));

        return new MultiTenantDataSource(defaultDataSource, tenantDataSources);
    }

    private DataSource createTenantDataSource(String schema) {
        // 这里可以基于当前schema创建一个新的DataSource
        // 并且配置连接到这个schema(例如通过连接URL中指定search_path)
        return defaultDataSource; // 简化处理,实际应返回基于schema的新DataSource
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(MultiTenantDataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        return sessionFactory.getObject();
    }

    @Bean
    public PlatformTransactionManager transactionManager(MultiTenantDataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

配置租户信息的解析

可以在Spring的过滤器中设置 TenantContext,在每个请求中解析租户信息,通过请求头或URL参数传递租户ID,如下所示。

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
public class TenantFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String tenantId = request.getHeader("X-TenantID");
        if (tenantId != null) {
            TenantContext.setTenant(tenantId);
        }
        try {
            filterChain.doFilter(request, response);
        } finally {
            TenantContext.clear();
        }
    }
}

使用MyBatis的Mapper

通过定义Mapper接口和对应的XML文件来操作数据库,在执行SQL语句时,MyBatis会根据当前租户自动选择对应的Schema。

总结

这种方法通过MyBatis和Spring Boot实现了基于Schema的多租户架构。通过在每个请求开始时解析并设置当前租户信息,可以动态切换到不同的Schema,实现数据的隔离和独立管理。

点击这里复制本文地址 以上内容由莫古技术网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

莫古技术网 © All Rights Reserved.  滇ICP备2024046894号-2