问题描述
我正在尝试使用class LoggedOutNicknameBinding extends Bindings {
LoggedOutNicknameBinding() {
final ldp = Get.put(LocalDataProvider());
lds = Get.put(LocalDataSource(dataProvider: ldp));
repository = Get.put(UserRepository(lds));
}
IUserRepository repository;
LocalDataSource lds;
@override
void dependencies() {
Get.lazyPut<LoggedOutNicknameController>(
() => LoggedOutNicknameController(repository),);
Get.lazyPut<UserRepository>(
() => UserRepository(lds),);
}
}
在我的DAO上执行集成测试。
我的目标是在单独的容器中运行每个测试。为此,我添加了Testcontainers
注释-我的逻辑是,如果我在每次测试中注入新的@DirtiesContext
,它将有一个更新的EntityManager
。是的,它实际上可以正常工作,但是,当涉及第11次测试时,我遇到一个例外,即Hikari无法创建新的连接,导致达到了连接限制。
我假设当应用执行DataSource
时,当前@DirtiesContext
被推离应用上下文,并且不再受Spring的管理,因此它可能会“忘记”关闭连接。
还是EntityManager
有问题?
这是我的代码:
JpaUserDao.java:
Testcontainers
JpaUserDaoTest.java:
@Repository
public class JpaUserDao implements UserDao {
@PersistenceContext
private EntityManager entityManager;
@Transactional
@Override
public long add(User user) {
entityManager.persist(user);
return user.getId();
}
//other DAO methods
}
ApplicationConfigurationTest.java:
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = ApplicationConfigurationTest.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
@Testcontainers
class JpaUserDaoTest {
@Container
private PostgreSQLContainer<?> postgreSqlContainer = new PostgreSQLContainer<>("postgres:13.0-alpine")
.withDatabaseName("example")
.withUsername("test")
.withPassword("test");
@Autowired
private UserDao userDao;
private User user;
@BeforeEach
void set(){
user = new User();
//set parameters of User
}
@Test
void addShouldAddUserToDatabase() {
long id = userDao.add(user);
assertEquals(1,id);
}
//other tests
}
hibernate.properties:
@ComponentScan(basePackages = "com.example")
@Configuration
@EnableTransactionManagement
public class ApplicationConfigurationTest {
public static final String PACKAGES_TO_SCAN = "com.example.model";
private static final String JDBC_CONFIGURATION_FILE = "src/test/resources/jdbc.properties";
private static final String HIBERNATE_CONFIGURATION_FILE = "src/test/resources/hibernate.properties";
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPackagesToScan(PACKAGES_TO_SCAN);
entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter());
entityManagerFactoryBean.setJpaProperties(additionalProperties());
return entityManagerFactoryBean;
}
private DataSource dataSource(){
HikariConfig hikariConfig = new HikariConfig(JDBC_CONFIGURATION_FILE);
return new HikariDataSource(hikariConfig);
}
private JpaVendorAdapter jpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
private Properties additionalProperties() {
try (InputStream input = new FileInputStream(HIBERNATE_CONFIGURATION_FILE)) {
Properties properties = new Properties();
properties.load(input);
return properties;
} catch (IOException ex) {
throw new PropertiesLoadException();
}
}
}
jdbc.properties:
hibernate.show_sql=true
hibernate.hbm2ddl.auto=create
hibernate.dialect=org.hibernate.dialect.PostgreSQL95Dialect
最后,异常本身:
jdbcUrl=jdbc:tc:postgresql:13.0:///example
dataSource.user=test
dataSource.password=test
dataSource.cachePrepStmts=true
dataSource.prepStmtCacheSize=250
dataSource.prepStmtCacheSqlLimit=2048
解决方法
您需要将dataSource
变成一个bean。由于未将其声明为bean,因此它的生命周期不受Spring的管理,因此在上下文被销毁时也不会关闭。
通常最好将bean依赖项声明为bean本身。如果那些依赖项实现AutoCloseable
(HikariDataSource
确实如此)或依赖生命周期回调(@PreDestroy
,@PostConstruct
)来管理其生命周期,则必须格外小心。否则,Spring不会将它们与常规对象区别对待,因此不会调用生命周期回调/自动清理。