MyBatis Spring在多租户应用程序中的应用

4

你好,需要在多租户应用程序中使用MyBatis Spring,需要一些帮助...

这可行吗?特别是因为我不知道如何在运行时将“MapperScannerConfigurer”配置为sqlSessionFactory。


为什么不使用JPA呢?它非常强大。 - unludo
是的,我同意,JPA非常强大。然而,我正在创建一个CQRS风格的应用程序,并使用JPA进行写操作,MyBatis进行读操作。 - Sudarshan
你曾经解决过这个问题吗?我们的团队也面临着同样的挑战。我们有数百个相同的数据库可能需要从单个应用程序连接,但根据请求上下文,我们在运行时不知道连接哪个。 - Jason McClellan
1
我想我已经解决了这个问题:),我已经在这里写了我的方法(http://noobjuggler.blogspot.in/2012/03/multitenant-mode-for-dddcqrs-base.html),我基本上是利用了[AbstractRoutingDataSource](http://blog.springsource.org/2007/01/23/dynamic-datasource-routing/)。 - Sudarshan
3个回答

1
这里提供另一种方法,使用插件(也称为拦截器)来切换“模式”或“目录”。
根据您使用的数据库,每个租户都有自己的数据库或模式。以下是几个示例:
- 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();
  }
}
只是一个持有者。

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的演示

0

可以使用工厂创建租户范围的数据源,并将其连接到由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>

而 TenantDataSourceFactory:

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>> 数据结构,此结构是以租户名称为键,对应着租户作用域的映射。根据当前租户的概念,它会将请求分配给指定的租户。


租户作用域(scope="tenant")是从哪里来的? - Markus Pscheidt
这是自定义作用域实现,我已经添加了一些细节。 - Roman-Stop RU aggression in UA

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接