You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot升级至3.1.5后Hibernate 6.2基于Schema的多租户失效

解决Spring Boot 3.1.5 + Hibernate 6.2 基于Schema的多租户配置问题

针对你升级后遇到的Connection is closed错误及多租户配置失效问题,核心原因是Hibernate 6对多租户组件的生命周期管理逻辑变更,加上Spring Boot 3.x自动配置顺序调整,导致连接提供者在依赖的配置Bean初始化完成前就被调用,进而产生无效连接。以下是具体解决方案:

1. 调整多租户连接提供者的依赖注入方式

将连接提供者依赖的自定义配置Bean改为延迟加载,避免初始化阶段依赖未就绪的Bean:

import jakarta.inject.Provider;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@Component
@Lazy
public class TenantBasedConnectionProvider implements MultiTenantConnectionProvider {
    // 用Provider延迟获取配置Bean,而非直接注入
    private final Provider<YourCustomConfigBean> configBeanProvider;
    private final DataSource dataSource;

    @Autowired
    public TenantBasedConnectionProvider(Provider<YourCustomConfigBean> configBeanProvider, DataSource dataSource) {
        this.configBeanProvider = configBeanProvider;
        this.dataSource = dataSource;
    }

    @Override
    public Connection getConnection(String tenantIdentifier) throws SQLException {
        // 真正需要时才获取配置Bean实例
        YourCustomConfigBean config = configBeanProvider.get();
        String targetSchema = config.getSchemaForTenant(tenantIdentifier);
        
        Connection connection = dataSource.getConnection();
        // 设置租户Schema
        connection.setSchema(targetSchema);
        return connection;
    }

    @Override
    public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
        // 重置Schema到默认值(如public),再释放连接
        connection.setSchema("public");
        connection.close();
    }

    @Override
    public boolean supportsAggressiveRelease() {
        return false;
    }

    @Override
    public boolean isUnwrappableAs(Class<?> unwrapType) {
        return MultiTenantConnectionProvider.class.isAssignableFrom(unwrapType);
    }

    @Override
    public <T> T unwrap(Class<T> unwrapType) {
        return unwrapType.cast(this);
    }
}

2. 正确注册多租户组件到Hibernate

放弃旧版通过HibernatePropertiesCustomizer硬塞组件的方式,改用Spring Boot 3.x兼容的EntityManagerFactory配置:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;

import javax.sql.DataSource;
import java.util.Map;

@Configuration
public class HibernateMultiTenantConfig {

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            DataSource dataSource,
            TenantBasedConnectionProvider connectionProvider,
            YourTenantIdentifierResolver tenantResolver) {
        
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource);
        em.setPackagesToScan("your.entity.package.path"); // 替换为你的实体类包路径
        em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        
        // 配置Hibernate多租户属性
        em.setJpaPropertyMap(Map.of(
                org.hibernate.cfg.AvailableSettings.MULTI_TENANT_CONNECTION_PROVIDER, connectionProvider,
                org.hibernate.cfg.AvailableSettings.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantResolver
        ));
        
        return em;
    }
}

3. 修复连接生命周期管理逻辑

Hibernate 6对连接的释放和复用要求更严格,确保:

  • 每次获取连接都从Spring管理的数据源中获取,不要自行缓存连接
  • 释放连接前重置Schema到默认值,避免后续连接被污染
  • 严格实现MultiTenantConnectionProvider的所有接口方法,尤其是supportsAggressiveRelease返回false,让Hibernate管理连接生命周期

4. 调整Testcontainers测试配置

在测试阶段提前初始化租户Schema,并确保租户标识符正确设置:

import org.springframework.test.context.jdbc.Sql;
import org.junit.jupiter.api.BeforeEach;

@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS, scripts = "classpath:init-tenant-schemas.sql")
public class TestContainerTests {

    @BeforeEach
    void setupTenant() {
        // 设置当前租户标识符(替换为你的租户上下文工具类)
        TenantContext.setCurrentTenant("test-tenant-1");
    }

    // 测试方法...
}

其中init-tenant-schemas.sql包含创建测试所需Schema的SQL语句:

CREATE SCHEMA IF NOT EXISTS test-tenant-1;
CREATE SCHEMA IF NOT EXISTS test-tenant-2;

5. 确保配置Bean的属性加载优先级

通过@ConfigurationProperties配合@EnableConfigurationProperties确保YAML配置被正确绑定,避免配置加载滞后:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableConfigurationProperties;

import java.util.Map;

@Configuration
@EnableConfigurationProperties(TenantSchemaProperties.class)
public class YourCustomConfigBean {
    private final TenantSchemaProperties schemaProperties;

    public YourCustomConfigBean(TenantSchemaProperties schemaProperties) {
        this.schemaProperties = schemaProperties;
    }

    public String getSchemaForTenant(String tenantId) {
        return schemaProperties.getMapping().get(tenantId);
    }
}

@ConfigurationProperties(prefix = "tenant.schemas")
public class TenantSchemaProperties {
    private Map<String, String> mapping; // 对应YAML中的租户-Schema映射

    // getter/setter
}

内容的提问来源于stack exchange,提问作者NoID

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 11:30:56