Java Spring JPA交易没有';t似乎被重用,连接池也在不断增长
我将Spring框架与Hibernate5和dbcp2一起使用。我们从直接使用hibernate切换到JPA。当我们这样做时,我们的应用程序停止工作 1)问题 连接池似乎不起作用或使用不正确:当我们监视数据库活动时,我们注意到连接的数量稳步增长 在我们的例子中,在控制器中接收REST请求,该控制器将细节传递给服务层。每个请求调用一次服务,并用Java Spring JPA交易没有';t似乎被重用,连接池也在不断增长,java,hibernate,spring-mvc,jpa,transactions,Java,Hibernate,Spring Mvc,Jpa,Transactions,我将Spring框架与Hibernate5和dbcp2一起使用。我们从直接使用hibernate切换到JPA。当我们这样做时,我们的应用程序停止工作 1)问题 连接池似乎不起作用或使用不正确:当我们监视数据库活动时,我们注意到连接的数量稳步增长 在我们的例子中,在控制器中接收REST请求,该控制器将细节传递给服务层。每个请求调用一次服务,并用@Transactional(readOnly=true)注释该服务。然后,服务通过DAO类访问数据来处理请求 在这种体系结构中,实际业务逻辑只应使用一个事
@Transactional(readOnly=true)
注释该服务。然后,服务通过DAO类访问数据来处理请求
在这种体系结构中,实际业务逻辑只应使用一个事务,池中应使用一个JDBC连接。但是,跟踪和数据库统计数据显示,创建了更多的连接和事务
当请求结束时,许多到数据库的连接仍处于活动状态
通常应用程序会被卡住:
17:21:07,070 DEBUG [AbstractPlatformTransactionManager]:1020 - Resuming suspended transaction after completion of inner transaction
17:21:07,070 TRACE [TransactionSynchronizationManager]:272 - Initializing transaction synchronization
对于这种特殊情况,额外的事务来自不同的服务(用于编写统计数据)。我们感兴趣的服务的交易已暂停
查看线程,它正在等待来自池的连接
我们做了一个小测试来检查事务的传播。该测试并不复制我们的体系结构,但旨在复制问题
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
@ContextConfiguration(classes = { //
TransactionTest.class, //
ClientDataSourceConfig.class, //
TransactionTest.TestFakeService.class })
@Configurable
public class TransactionTest {
@Autowired
DataSource primaryDataSource;
@Autowired
PlatformTransactionManager primaryHibernateTransactionManager;
@Autowired
EntityManagerFactory primarySessionFactory;
@Autowired
TestFakeService fakeService;
// --------------
@Test
public void transactionTest() {
Tree tree = fakeService.getTree();
fakeService.getTree2();
assertNotNull(tree);
}
@Component
@Transactional(readOnly = true)
public class TestFakeService {
public Tree getTree() {
EntityManager em = primarySessionFactory.createEntityManager();
Tree tree = em.find(Tree.class, "ID");
fakeService.getTree2();
return tree;
}
public Tree getTree2() {
EntityManager em = primarySessionFactory.createEntityManager();
Tree tree = em.find(Tree.class, "ID");
return tree;
}
}
}
这导致了以下跟踪(没有来自测试初始化的日志。我使用了pastebin,因为我达到了字符限制)
我们从测试中注意到:
*我们调用第一个方法->创建事务
*第二个方法从调用事务->创建新事务(而不是传播现有事务)开始调用
*再次获取实体(而不是使用Hibernate会话中的实体)
2)配置
以下是数据源的配置:
@Configuration
@PropertySource({ "classpath:datasources.properties" })
@EnableTransactionManagement
public class ClientDataSourceConfig {
@Autowired
private Environment env;
/**************** DATASOURCE CONFIG *****************/
@Value("${jdbc.client.db}")
private String dataDatabase;
@Value("${jdbc.client.host}")
private String host;
@Value("${jdbc.client.port}")
private String port;
@Value("${jdbc.client.driver.class}")
private String driverClassName; // org.mariadb.jdbc.Driver
@Value("${jdbc.client.driver.name}")
private String driverName; // mariadb
@Value("${jdbc.client.user}")
private String username;
@Value("${jdbc.client.pass}")
private String password;
/*************** HIBERNATE CONFIG ******************/
@Value("${hibernate.dialect}")
private String hibernateDialect; // org.hibernate.dialect.MySQLDialect
@Value("${hibernate.show_sql}")
private String hibernateShowSql;
@Value("${hibernate.current_session_context_class}")
private String sessionContextClass; //org.springframework.orm.hibernate5.SpringSessionContext
/*************************************/
@Bean(name = "clientEntityManager")
@Primary
public EntityManagerFactory clientEntityManager(
@Qualifier("clientDataSource") DataSource clientDataSource) {
HibernateJpaVendorAdapter hibernateVendorAdapter = new HibernateJpaVendorAdapter();
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setJpaVendorAdapter(hibernateVendorAdapter);
em.setDataSource(clientDataSource);
em.setMappingResources("HibernateQueries.xml");
em.setPackagesToScan(new String[] { //
"entity.package", //
});
Properties properties = new Properties();
properties.put("hibernate.dialect", hibernateDialect);
properties.put("hibernate.show_sql", hibernateShowSql);
properties.put("hibernate.current_session_context_class", sessionContextClass);
em.setJpaProperties(properties);
em.afterPropertiesSet();
return em.getObject();
}
@Bean(name = "clientDataSource")
@Primary
public DataSource getDataSource() throws SQLException {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(driverClassName);
ds.setUrl("jdbc:" + driverName + "://" + host + ":" + port + "/" + dataDatabase);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
@Bean(name = "clientDataSourceTx")
@Primary
public PlatformTransactionManager clientDataSourceTx(
@Qualifier("clientEntityManager") EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
3)问题
总之,我们不明白为什么当我们直接使用hibernate的会话时,这种配置能够完美地工作,而在使用JPA的EntityManagerFactory时却不能
为什么到数据库的活动连接数量不断增加,而没有看到任何类型的日志显示连接池,为什么在我们设计服务/DAO层时创建了如此多的事务,以确保服务的方法不会被调用