Spring Boot + Hibernate 多租户:@Transactional 不起作用

Chr*_*ker 4 spring hibernate jpa multi-tenant spring-boot

我有一个 Spring Boot 2 + Hibernate 5 多租户应用程序连接到单个 PostgreSQL 数据库。我已经根据这些指南进行了设置:

只要我在访问控制器端点之前在过滤器或拦截器中设置tenantId,就可以正常工作。

但是,我需要在控制器内设置租户,如下所示:

@RestController
public class CarController {
    @GetMapping("/cars")
    @Transactional
    public List<Car> getCars(@RequestParam(name = "schema") String schema) {
        TenantContext.setCurrentTenant(schema);
        return carRepo.findAll();
    }
}
Run Code Online (Sandbox Code Playgroud)

但此时已经检索到连接(对于公共模式)并且设置TenantContext没有任何效果。

我认为@Transactional应该强制该方法在单独的事务中运行,因此 Hibernate 会话的创建将被推迟到carRepo.findAll()调用该方法为止。情况似乎并非如此,因为@Transactional什么也没做。

这让我想到两个问题:

  1. 如何在请求期间推迟 Hibernate 会话的创建,直到我设法根据过滤器/拦截器中不可用的某些逻辑设置正确的租户?@Transactional似乎什么也没做。
  2. 如何在同一请求或代码块中与不同模式进行交互?想象一下,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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Chr*_*ker 5

虽然 @tan-mally@Transaction清楚地解释了我的问题以及如何解决它,但实际问题是由不同的 Spring Boot 配置默认值引起的:spring.jpa.open-in-view=true

当将此设置为 时,我根本false不需要注释。@TransactionConnection 的检索将被推迟findAll()直到它在调用TenantContext.setCurrentTenant(schema).

显然spring.jpa.open-in-view=true,总是围绕整个请求急切地创建一个 Hibernate 会话。

希望这可以帮助下一个遇到这个问题的人。我只是通过启动期间弹出的有关此默认设置的警告暗示了此属性。请参阅此 Github 问题以获取有关此主题的讨论。