Spring Boot + Hibernate多租户:@Transactional无法正常工作

时间:2019-05-03 15:46:50

标签: spring hibernate spring-boot jpa

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

只要在到达Controller端点之前在Filter或Interceptor中设置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强制该方法在单独的事务中运行,因此休眠会话的创建将推迟到调用carRepo.findAll()方法之前。似乎并非如此,因为@Transactional什么也不做。

这使我想到两个问题:

  1. 在根据过滤器/拦截器中不可用的某些逻辑设法设置正确的租户之前,如何在请求期间推迟创建休眠会话? @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);
    }
}

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;
    }
}

2 个答案:

答案 0 :(得分:2)

在春季,当我们从另一个bean类中调用该方法时,将调用事务。在这种情况下,如果将findAll调用移至服务类并在该方法上添加事务,则行为将与您期望的一样。事务将在您调用服务方法时开始,然后在TenantContext上设置架构值

注意:从Controller中删除@Transactional。由于您正在读取,最好将readonly属性添加到@Transactional中,该方法添加到服务方法'getAllCars()'

@RestController
public class CarController {

    @GetMapping("/cars")
    public List<Car> getCars(@RequestParam(name = "schema") String schema) {
        TenantContext.setCurrentTenant(schema);
        return carService.getAllCars();
    }
}

@Service
public class CarService{

    @Transactional(readOnly=true)
    public List<Car> getAllCars() {
        return carRepo.findAll();
    }
}

答案 1 :(得分:1)

@ tan-mally清楚地说明了我对@Transaction的问题以及如何解决的问题,而实际问题是由不同的Spring Boot配置默认值spring.jpa.open-in-view=true

引起的

将其设置为false时,我根本不需要@Transaction注释。连接的检索将推迟到遇到存储库的findAll()方法之后,在调用TenantContext.setCurrentTenant(schema)之后

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

希望这可以帮助下一个遇到此问题的人。仅在启动时弹出有关此默认设置的警告提示我此属性。有关此主题的讨论,请参见this Github issue