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
相关产品推荐
相关产品推荐

