Java JPA连接已与EntityManager关闭
请原谅这篇文章的格式可能不正确,因为我以前从未在这里发表过。我有一个正在运行的SpringMVCWeb服务,它调用了几个使用JPA/hibernate与SQLServer数据库交互的DAO。web服务运行一段时间后,与数据库的连接似乎关闭了 其中一个DAO类的示例如下:Java JPA连接已与EntityManager关闭,java,spring,hibernate,jpa,entitymanager,Java,Spring,Hibernate,Jpa,Entitymanager,请原谅这篇文章的格式可能不正确,因为我以前从未在这里发表过。我有一个正在运行的SpringMVCWeb服务,它调用了几个使用JPA/hibernate与SQLServer数据库交互的DAO。web服务运行一段时间后,与数据库的连接似乎关闭了 其中一个DAO类的示例如下: @Repository public class OceanClientDaoImpl { // Create entity manager objects that will talk to the database pri
@Repository
public class OceanClientDaoImpl {
// Create entity manager objects that will talk to the database
private EntityManagerFactory entityManagerFactory;
/**
* Load all clients in the database. Return as ClientLight objects.
*
* @return List of ClientLight objects.
*/
public List<Client> loadClients()
{
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
List<?> clients =
entityManager.createNativeQuery("select id, clientname, industry, logo, slug from client",
Client.class).getResultList();
entityManager.getTransaction().commit();
entityManager.close();
return Client.getCheckedList(clients);
}
}
因此,连接正在关闭。很明显,无论是注入方法还是处理错误的方法,实现都存在一些问题。所以,问题是——正确的方法是什么?如何避免连接关闭错误?非常感谢。我认为可能发生的情况是,当发生错误时,一些DAO会让数据库事务保持打开状态 上面的示例DAO正在对数据库事务进行编程管理,但不处理回滚场景。这是一个DAO实现的示例:
MyService {
private EntityManagerFactory emf;
public void myMethod() {
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
MyDaoA myDaoA = new MyDaoA(em);
MyDaoB myDaoB = new MyDaoB(em);
try {
tx.begin();
myDaoA.doSomething();
myDaoB.doSomething();
tx.commit();
} catch(Exception e) {
tx.rollback();
}
}
}
所有进行编程事务管理的DAO都应该使用此模式,否则事务迟早不会回滚,这可能会导致问题
实现DAO的一种更频繁、更不容易出错的方法是仅直接注入实体管理器:
MyService {
@PersistenceContext
private EntityManager em;
@Autowired
private MyDaoA myDaoA;
@Autowired
private MyDaoB myDaoB;
@Transactional
public void myMethod() {
myDaoA.doSomething();
myDaoB.doSomething();
}
}
注入EntityManagerFactory和执行手动事务管理应保留在例外情况下,默认情况下使用@Transactional。我想说的问题是连接池的问题,所以您可以使用连接池库来解决您的问题,但是,您应该非常小心地使用连接池属性 实际上,你可以用很多方法,我给你相同的代码作为参考。这可能会有帮助 步骤1:persistence.xml
<!-- c3p0 properties -->
<!-- Determines how many connections at a time c3p0 will try to acquire when the pool is exhausted -->
<property name="hibernate.c3p0.acquire_increment" value="1"/>
<!-- If this is a number greater than 0,
c3p0 will test all idle, pooled but unchecked-out connections, every this number of seconds -->
<property name="hibernate.c3p0.idle_test_period" value="60"/>
<property name="hibernate.c3p0.max_size" value="20"/>
<property name="hibernate.c3p0.max_statements" value="50"/>
<property name="hibernate.c3p0.min_size" value="5"/>
<property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider" />
步骤3:您需要配置数据源
<bean id="myJdbcDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<!-- Connection properties -->
<property name="driverClass" value="$DS{database.class}" />
<property name="jdbcUrl" value="$DS{database.url}" />
<property name="user" value="$DS{database.username}" />
<property name="password" value="$DS{database.password}" />
<bean/>
我建议您使用JPA连接池。您可以在entityManagerFactory中指定连接池。可能是在指定entityManagerFactory的JPAPProperties属性时。您必须使用某种JPA供应商,如hibernate、eclipselink或toplink等。每个JPA供应商使用连接池的方式/属性不同。您需要参考他们的文档来了解如何使用连接池。我认为entityManager应该只在单个请求的生命周期内有效。如果我以这种方式注入entityManager,它不会在应用程序的整个生命周期内都有效吗?spring实际注入的PersistenceContext不是实体管理器本身,而是实体管理器代理。如果代理配置了PersistenceContext(type=PersistenceContextType.EXTENDED),那么您就有了您提到的行为。但通常的默认情况是PersistenceContextType.TRANSACTION,它将为每个事务提供一个不同的实体管理器。这种机制是透明的,如果您使用@Transactional,您总是可以确保得到正确的实体管理器。我编辑了我的帖子,其中的原因是我认为这是相关的,似乎发生的情况是由于DAO的编写方式(出错时不处理回滚,手动事务处理)一些DAO使事务保持打开状态,连接被泄漏。因此,增加连接池大小或切换到另一个连接池(如c3Po)只能掩盖问题的真正原因。希望这有帮助。我将其更改为使用PersistenceContext将entityManager注入DAO,但不幸的是,问题仍然存在。我启动服务,等待20分钟,发出请求并获得连接关闭异常。根据堆栈跟踪,有一个对OceanClientDaoImpl的调用,它触发了对SessionImpl.beginTransaction的调用,它调用驱动程序向SQL server发送命令并通过TDSReader.readPacket等待答复根本原因是“SQL server未返回响应”。因此,服务器端发生了一些事情,导致服务器无法回复。一种解释是,这是由外部因素导致的SQL server上的操作问题。一个方向是与DBA确认是否需要在服务器上打开多个连接以及由谁打开。
<!-- c3p0 properties -->
<!-- Determines how many connections at a time c3p0 will try to acquire when the pool is exhausted -->
<property name="hibernate.c3p0.acquire_increment" value="1"/>
<!-- If this is a number greater than 0,
c3p0 will test all idle, pooled but unchecked-out connections, every this number of seconds -->
<property name="hibernate.c3p0.idle_test_period" value="60"/>
<property name="hibernate.c3p0.max_size" value="20"/>
<property name="hibernate.c3p0.max_statements" value="50"/>
<property name="hibernate.c3p0.min_size" value="5"/>
<property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider" />
c3p0.testConnectionOnCheckout=true
c3p0.preferredTestQuery=SELECT 1;
<bean id="myJdbcDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<!-- Connection properties -->
<property name="driverClass" value="$DS{database.class}" />
<property name="jdbcUrl" value="$DS{database.url}" />
<property name="user" value="$DS{database.username}" />
<property name="password" value="$DS{database.password}" />
<bean/>