- public
所有客户之间共享的数据,例如用户帐户,因为我们不知道用户属于哪个客户 - customer_1
- customer_2
- ...
public class TenantProviderImpl implements CurrentTenantIdentifierResolver {
private static final ThreadLocal<String> tenant = new ThreadLocal<>();
public static void setTenant(String tenant){
TenantProviderImpl.tenant.set(tenant);
}
@Override
public String resolveCurrentTenantIdentifier() {
return tenant.get();
}
@Override
public boolean validateExistingCurrentSessions() {
return false;
}
/**
* Initialize a tenant by storing the tenant identifier in both the HTTP session and the ThreadLocal
*
* @param String tenant Tenant identifier to be stored
*/
public static void initTenant(String tenant) {
HttpServletRequest req = ((ServletWebRequest) RequestCycle.get().getRequest()).getContainerRequest();
req.getSession().setAttribute("tenant", tenant);
TenantProviderImpl.setTenant(tenant);
}
}
initTenant
方法由servlet过滤器在每个请求中调用。此过滤器在打开到数据库的连接之前进行处理。我们还实现了
AbstractDataSourceBasedMultiTenantConnectionProviderImpl
,将其设置为我们的hibernate.multi_tenant_connection_provider
。它在每个请求之前发出一个SET search_path
查询。这对通过上述servlet过滤器传递的请求非常有效。现在让我们来看看我们真正的问题:我们的应用程序有一些入口点没有通过servlet过滤器,例如一些SOAP端点。还有一些定时作业,这些作业没有通过servlet过滤器。这证明是一个问题。
作业/入口点以某种方式接收一个值,该值可用于标识应与作业/入口点请求相关联的客户端。这个唯一值通常映射在我们的
public
数据库架构中。因此,在我们知道与哪个客户相关联之前,我们需要查询数据库。Spring因此初始化了一个完整的Hibernate会话。此会话具有我们的默认租户ID,并且没有映射到特定的客户端。但是,解析唯一值以确定客户后,我们希望会话更改租户标识符。然而,似乎不支持这种操作,没有HibernateSession.setTenantIdentifier(String)
,而有一个SharedSessionContract.getTenantIdentifier()
。我们认为在以下方法中找到了解决方案:
org.hibernate.SessionFactory sessionFactory = getSessionFactory();
org.hibernate.Session session = null;
try
{
session = getSession();
if (session != null)
{
if(session.isDirty())
{
session.flush();
}
if(!session.getTransaction().wasCommitted())
{
session.getTransaction().commit();
}
session.disconnect();
session.close();
TransactionSynchronizationManager.unbindResource(sessionFactory);
}
}
catch (HibernateException e)
{
// NO-OP, apparently there was no session yet
}
TenantProviderImpl.setTenant(tenant);
session = sessionFactory.openSession();
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
return session;
然而,在作业/终端上下文中,这种方法似乎不起作用,并导致HibernateException
,例如“Session已关闭!”或“事务未成功启动”。
我们有点迷失了,因为我们一直在尝试找到解决方案。我们是否有什么误解?我们是否有什么误解?如何解决以上问题?
回顾一下:由定时作业等创建的HibernateSession
不会通过我们的servlet过滤器创建,因此在Hibernate会话开始之前没有关联租户标识符。它们具有唯一值,但我们可以通过查询数据库将其转换为租户标识符。如何告诉现有的Hibernate会话更改其租户标识符,从而发出新的SET search_path
语句?