簡體   English   中英

多租戶應用程序中的 MyBatis spring

[英]MyBatis spring in a multi tenant application

嗨,在多租戶應用程序中使用 MyBatis spring 需要一些幫助......

是否有可能 ? 特別是因為我沒有看到如何在運行時使用 sqlSessionFactory 配置“MapperScannerConfigurer”。

可以使用工廠創建租戶范圍的數據源並將其連接到由 mybatis-spring 生成的映射器使用的 SqlSessionFactory。 這是相關的 app-context.xml 部分

<bean id="dataSourceFactory" class="com.myapp.TenantDataSourceFactory"
      depends-on="tenant" scope="singleton"/>

<bean id="dataSource" factory-bean="dataSourceFactory" factory-method="getObject"
      destroy-method="close" scope="tenant" >
    <aop:scoped-proxy/>
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="configLocation" value="classpath:myBatisConfig.xml" />
</bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.myapp" />
    <property name="annotationClass" value="com.myapp.mapper.Mapper"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

和租戶數據源工廠:

public class TenantDataSourceFactory {

@Autowired Tenant tenant;

  public DataSource getObject() throws Exception {
    BasicDataSource ds = new BasicDataSource();
    ds.setDriverClassName("org.postgresql.Driver");
    ds.setUrl(String.format("jdbc:postgresql://%s:%s/%s",
            tenant.getDbHost(), tenant.getDbPort(), tenant.getDbName()));
    ds.setUsername(tenant.getDbUser());
    ds.setPassword(tenant.getDbPassword());
    return ds;
  }

}

tenant范圍是包含Map<String, Map<String, Object>>自定義范圍,它是范圍 bean 映射的租戶名稱。 它根據當前租戶的概念分派給給定的租戶。

Spring 有 AbstractRoutingDataSource 來處理這個問題

http://spring.io/blog/2007/01/23/dynamic-datasource-routing/

這是使用插件(又名攔截器)切換“模式”或“目錄”的另一種方法。

根據您使用的數據庫,每個租戶都有自己的數據庫或架構。 幾個例子:

  • MySQL :每個租戶都有自己的“數據庫”,插件應該調用setCatalog
  • Oracle :每個租戶都有自己的“架構”,插件應該調用setSchema
  • SQL Server:每個租戶都有自己的“數據庫”,插件應該調用setCatalog

假設您通過 ThreadLocal 傳遞租戶 ID,這里是一個示例插件實現。

import java.sql.Connection;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;

@Intercepts(
  @Signature(
    type = StatementHandler.class,
    method = "prepare",
    args = { Connection.class, Integer.class }))
public class MultiTenantInterceptor implements Interceptor {
  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    Object[] args = invocation.getArgs();
    Connection con = (Connection) args[0];
    con.setSchema(TenantIdHolder.getTenantId());
    // con.setCatalog(TenantIdHolder.getTenantId());
    return invocation.proceed();
  }
}

TenantIdHolder只是一個ThreadLocal持有者。

public class TenantIdHolder {
  private static ThreadLocal<String> value = new ThreadLocal<>();

  public static void setTenantId(String tenantId) {
    value.set(tenantId);
  }

  public static String getTenantId() {
    return value.get();
  }
}

這是一個使用 HSQLDB 的演示

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM