为什么Hibernate Lazy Loading不释放数据库连接



为什么hibernate(JPA)在执行延迟加载查询后不释放数据库连接?在下面的例子中,如果我评论这个"--System.out.println(user1.getUserOwner());",并用两倍的用户数量(500*2=1000个用户)执行JMeter测试就可以了。为什么延迟加载会捕获数据库连接?许多连接打开错误被抛出。

我正在使用:

  • Hibernate 4.3.6.Final
  • 底漆5.0.0
  • CDI焊缝2.2.4.最终
  • JSF 2.2.8

EMProducer.java

public class EMProducer implements Serializable {
    private static final long serialVersionUID = 1L;
    public static final String PESISTENCE_UNIT_NAME = "pfac";
    public static EntityManager getEntityManager(){
        return Persistence.createEntityManagerFactory(PESISTENCE_UNIT_NAME).createEntityManager();
    }
    @Produces
    @ApplicationScoped
    public EntityManagerFactory create() {
        return Persistence.createEntityManagerFactory(PESISTENCE_UNIT_NAME);
    }
    public void destroy(@Disposes EntityManagerFactory factory) {
        factory.close();
    }
    @Produces
    public EntityManager createEntityManager(EntityManagerFactory emf) {
        return extractEntityManager(emf);
    }
    private EntityManager extractEntityManager(EntityManagerFactory emf) {
        EntityManager em = emf.createEntityManager();
        return em;
    }
}

test.xml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:p="http://primefaces.org/ui"
    xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
>
<f:view>
<h:head>
    <title>Test</title>
</h:head>
<h:body>
    #{testView}
</h:body>
</f:view>
</html>

托管视图

@Named
@ViewScoped
public class TestView implements Serializable {
    private static final long serialVersionUID = 1L;
    @Inject private EntityManager entityManager;
    @PostConstruct
    private void inicialize(){
        User user1 = this.entityManager.find(User.class, 1);
        System.out.println(user1.getUserOwner());
    }
}

实体

@Entity
@Table(name = User.TABLE_NAME, schema = User.SCHEMA_NAME)
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    public static final String SCHEMA_NAME = "public";
    public static final String TABLE_NAME = "tb_user";
    public static final String SEQUENCE_NAME = "sq_"+TABLE_NAME;
    private Integer id;
    private User userOwner;
    private String name;
    public User() {
    }
    @Id
    @Column(name = "id_user", nullable=false, insertable=false, updatable=false)
    @SequenceGenerator(name = SEQUENCE_NAME, schema=SCHEMA_NAME, sequenceName = SEQUENCE_NAME, allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator = SEQUENCE_NAME)
    public Integer getId() {
        return this.id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "id_user_owner", nullable = false, updatable=false)
    @NotNull
    public User getUserOwner() {
        return userOwner;
    }
    public void setUserOwner(User userOwner) {
        this.userOwner = userOwner;
    }
    @Column(name = "ds_name")
    @Size(min=5, max=100)
    @NotNull
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return getName();
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getId() == null) {
            return false;
        }
        if (!(obj instanceof User)) {
            return false;
        }
        User other = (User) obj;
        if (!getId().equals(other.getId())) {
            return false;
        }
        return true;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + getId();
        return result;
    }
}

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
    <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
        version="2.1">
        <persistence-unit name="pfac" transaction-type="RESOURCE_LOCAL">
            <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
            <exclude-unlisted-classes>false</exclude-unlisted-classes>
            <properties>
                <property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
                <property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/test" />
                <property name="hibernate.connection.username"  value="postgres" />
                <property name="hibernate.connection.password" value="postgres" /> 
                <property name="hibernate.show_sql" value="true" />
                <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
                <property name="hibernate.archive.autodetection" value="class" />
                <property name="hibernate.connection.isolation" value="2" />
                <property name="hibernate.id.new_generator_mappings" value="true" />
                <property name="hibernate.default_batch_fetch_size" value="16" />
                <property name="hibernate.max_fetch_depth" value="3" />
                <property name="hibernate.connection.release_mode" value="after_statement" />
            </properties>
        </persistence-unit>
    </persistence>

EntityManager应使用注入

@PersistenceContext(unitName = "pfac")

而不是使用@Inject注释。

EntityManager是上下文绑定的,您可以将其与当前线程事务关联,也可以使用应用程序范围的持久性上下文。连接释放模式始终与当前事务配置相关:JDBC或JTA。

在您的案例中,您使用JDBC资源本地事务,但您配置了:

<property name="hibernate.connection.release_mode" value="after_statement" />

通常用于JTA激进发布模式。In可以使用JDBC连接,但这不是它的本意。

因为您使用的是DriverManagerConnectionProviderImpl,所以意味着Hibernate将使用内部连接池来处理JDBC连接。

在每条语句之后,将调用org.hibernate.engine.jdbc.connections.spi.ConnectionProvider#closeConnection(Connection conn)。

对于DriverManagerConnectionProviderImpl,它看起来是这样的:

@Override
public void closeConnection(Connection conn) throws SQLException {
    if (conn == null) {
        return;
    }
    this.connections.offer( conn );
}

所以连接没有关闭,但它们返回到池中,这就是为什么它们没有关闭。您需要关闭EntityManagerFactory以关闭内部连接池,然后所有JDBC连接都将物理关闭。

最新更新