spring - Spring Boot + Hibernate 多租户:@Transactional 不起作用
问题描述
我有一个连接到单个 PostgreSQL 数据库的 Spring Boot 2 + Hibernate 5 多租户应用程序。我已经根据这些指南进行了设置:
- http://www.greggbolinger.com/tenant-per-schema-with-spring-boot/
- https://blog.aliprax.me/schema-based-multitenancy/
- https://fizzylogic.nl/2016/01/24/make-your-spring-boot-application-multi-tenant-aware-in-2-steps/
只要我在到达控制器端点之前在过滤器或拦截器中设置了tenantId,这就可以正常工作。
但是,我需要在控制器内设置租户,如下:
@RestController
public class CarController {
@GetMapping("/cars")
@Transactional
public List<Car> getCars(@RequestParam(name = "schema") String schema) {
TenantContext.setCurrentTenant(schema);
return carRepo.findAll();
}
}
但是此时已经检索到一个连接(对于公共模式)并且设置TenantContext
没有效果。
我认为@Transactional
应该强制该方法在单独的事务中运行,因此 Hibernate Session 的创建将被推迟到carRepo.findAll()
调用该方法之前。情况似乎并非如此,因为@Transactional
什么都不做。
这让我想到了两个问题:
- 如何在请求期间推迟 Hibernate 会话的创建,直到我设法根据过滤器/拦截器中不可用的某些逻辑设置正确的租户?
@Transactional
似乎什么也没做。 - 如何在同一个请求或代码块中与不同的模式对话?想象一下 1 个存储库仅在公共模式中可用,而 1 个在租户模式中。
其他相关类(仅显示相关部分!)
MultiTenantConnectionProviderImpl.java:
@Component
public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider {
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
final Connection connection = getAnyConnection();
connection.setSchema(tenantIdentifier);
return connection;
}
@Override
public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
connection.setSchema(null);
releaseAnyConnection(connection);
}
}
TenantIdentifierResolver.java
@Component
public class TenantIdentifierResolver implements CurrentTenantIdentifierResolver {
@Override
public String resolveCurrentTenantIdentifier() {
String tenantId = TenantContext.getCurrentTenant();
return (tenantId != null) ? tenantId : "public";
}
@Override
public boolean validateExistingCurrentSessions() {
return true;
}
}
HibernateConfig.java:
@Configuration
public class HibernateConfig {
@Autowired
private JpaProperties jpaProperties;
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
MultiTenantConnectionProvider multiTenantConnectionProviderImpl,
CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl) {
Map<String, Object> properties = new HashMap<>(jpaProperties.getProperties());
properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProviderImpl);
properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolverImpl);
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan("com.example");
em.setJpaVendorAdapter(jpaVendorAdapter());
em.setJpaPropertyMap(properties);
return em;
}
}
解决方案
虽然@tan-mally@Transaction
清楚地解释了我的问题以及如何解决它,但实际问题是由不同的 Spring Boot 配置默认值引起的:spring.jpa.open-in-view=true
将其设置为 时false
,我根本不需要@Transaction
注释。在findAll()
调用. _ _TenantContext.setCurrentTenant(schema)
显然spring.jpa.open-in-view=true
总是急切地围绕整个请求创建一个 Hibernate 会话。
希望这有助于下一个人遇到这个问题。我只是通过在启动期间弹出有关此默认设置的警告来提示此属性。有关此主题的讨论,请参阅此 Github 问题。
推荐阅读
- reactjs - 如何查询 gatsby-image?
- javascript - Mui-DataTable 中的行跨越
- git - Visual Studio“Git Pull”与 Git 命令行不同吗?
- python - 如何使用 Beautiful Soup 抓取多页搜索结果
- go - 是否有查找完整文件权限的功能?
- angular - 需要在订阅内的条件内调用服务
- reactjs - 为什么我不能在 React 中使用 Iterator 或 Map.get()?
- flutter - Flutter:应用内购买信用卡测试
- python - 如何修复'类型错误:不能将序列乘以'float'类型的非int?
- typo3 - Typo3:创建的页面结果为 404。为什么?以及如何解决这个问题?