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

时间:2014-09-07 23:29:22

标签: hibernate jpa database-connection lazy-loading

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

我正在使用:

  • Hibernate 4.3.6.Final;
  • Primefaces 5.0.0;
  • CDI Weld 2.2.4.Final;
  • 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>

1 个答案:

答案 0 :(得分:1)

应使用以下方式注入EntityManager:

@PersistenceContext(unitName = "pfac")

不使用@Inject注释。

EntityManager是上下文绑定的,您可以将它与当前线程事务关联或使用应用程序范围持久性上下文。 The connection release mode始终与当前事务配置相关:JDBC或JTA。

在您的情况下,您正在使用JDBC资源本地事务,但您已配置:

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

您通常用于JTA积极发布模式。在使用JDBC连接时,它不是它的意思。

因为您正在使用DriverManagerConnectionProviderImpl,所以它将使用内部连接池来处理JDBC连接。

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

对于DriverManagerConnectionProviderImpl,它的外观如下:

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

    this.connections.offer( conn );
}

所以连接没有关闭,但它们返回到池中,这就是它们没有关闭的原因。您需要关闭EntityManagerFactory以关闭内部连接池,然后所有JDBC连接将在物理上关闭。