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

Spring Boot多租户后端测试失败:会话保留旧租户ID

解决方案:Hibernate共享Schema多租户测试中Session租户ID不更新问题

问题根源

你的问题核心在于:测试方法上的@Transactional注解会在测试代码执行前就创建Hibernate Session,此时你还未调用TenantContext.setCurrentTenant(123),Session初始化时绑定了默认租户ID -1。再加上TenantResolver中validateExistingCurrentSessions返回false,Hibernate不会检查已存在Session的租户标识是否和当前解析的一致,因此后续设置租户ID也不会更新这个已创建的Session的租户值,最终导致保存实体时用了旧的租户ID。


方案一:在事务创建前设置租户ID

利用@BeforeEach在事务启动前设置租户ID,确保Session创建时就能获取到正确的租户值:

@SpringBootTest
@Transactional
@AutoConfigureMockMvc
@ActiveProfiles(profiles = "h2")
class MyEntityRepositoryTest{
    @Autowired
    MyEntityRepository myEntityRepository;

    @BeforeEach
    void setupTenant() {
        TenantContext.setCurrentTenant(123);
    }

    @AfterEach
    void cleanupTenant() {
        TenantContext.reset();
    }

    @Test
    void createEntityWithTenant() {
        MyEntity myEntity = new MyEntity();
        MyEntity storedEntity = myEntityRepository.save(myEntity);
        assertEquals(123, storedEntity.getTenant());
    }
}

如果需要在不同测试方法中使用不同租户ID,可以用TransactionTemplate手动控制事务时机:

@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles(profiles = "h2")
class MyEntityRepositoryTest{
    @Autowired
    MyEntityRepository myEntityRepository;

    @Autowired
    TransactionTemplate transactionTemplate;

    @Test
    void createEntityWithTenant() {
        TenantContext.setCurrentTenant(123);
        try {
            MyEntity storedEntity = transactionTemplate.execute(status -> {
                MyEntity myEntity = new MyEntity();
                return myEntityRepository.save(myEntity);
            });
            assertEquals(123, storedEntity.getTenant());
        } finally {
            TenantContext.reset();
        }
    }
}

方案二:让Hibernate自动验证并更新Session租户ID

修改TenantResolver的validateExistingCurrentSessions方法返回true,Hibernate会在每次操作时检查当前解析的租户ID是否和Session绑定的一致,不一致则更新Session的租户标识:

@Component
public class TenantResolver implements CurrentTenantIdentifierResolver<Integer>, HibernatePropertiesCustomizer {

    @Override
    public Integer resolveCurrentTenantIdentifier() {
        Integer tenant = TenantContext.getCurrentTenant();
        if(tenant == null) return -1;
        return tenant;
    }

    @Override
    public boolean validateExistingCurrentSessions() {
        return true; // 修改为true
    }

    @Override
    public void customize(Map<String, Object> hibernateProperties) {
        hibernateProperties.put(MultiTenancySettings.MULTI_TENANT_IDENTIFIER_RESOLVER, this);
    }
}

此方案无需修改测试代码,仅需调整Resolver配置,测试环境下的性能开销可忽略。


方案三:手动刷新Session获取新租户ID

注入EntityManager,清除当前Session后强制创建新Session,新Session会获取最新的租户ID:

@SpringBootTest
@Transactional
@AutoConfigureMockMvc
@ActiveProfiles(profiles = "h2")
class MyEntityRepositoryTest{
    @Autowired
    MyEntityRepository myEntityRepository;

    @Autowired
    EntityManager entityManager;

    @Test
    void createEntityWithTenant() {
        TenantContext.setCurrentTenant(123);
        // 清除当前Session,后续操作会创建绑定新租户ID的Session
        entityManager.clear();
        
        MyEntity myEntity = new MyEntity();
        MyEntity storedEntity = myEntityRepository.save(myEntity);
        assertEquals(123, storedEntity.getTenant());
    }

    @AfterEach
    void cleanupTenant() {
        TenantContext.reset();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 08:59:56