Java JPA连接已与EntityManager关闭

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

请原谅这篇文章的格式可能不正确,因为我以前从未在这里发表过。我有一个正在运行的SpringMVCWeb服务,它调用了几个使用JPA/hibernate与SQLServer数据库交互的DAO。web服务运行一段时间后,与数据库的连接似乎关闭了

其中一个DAO类的示例如下:

@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/>