sql - 带有 HIBERNATE 的 JPA 插入非常慢

标签 sql sql-server database hibernate jpa

我正在尝试使用 JAP 和 HIBERNATE 将一些数据插入到 SQL Server 2008 R2。一切都“有效”,除了它非常慢。插入 20000 行大约需要 45 秒,而 C# 脚本大约需要不到 1 秒。

这个领域的任何资深人士都可以提供一些帮助吗?我将不胜感激。

更新:从下面的答案中得到了一些很好的建议,但它仍然没有按预期工作。速度是一样的。

这是更新后的 persistence.xml:

<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="ClusterPersist"
    transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>cluster.data.persist.sqlserver.EventResult</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
        <property name="javax.persistence.jdbc.url"
            value="jdbc:sqlserver://MYSERVER:1433;databaseName=MYTABLE" />
        <property name="javax.persistence.jdbc.user" value="USER" />
        <property name="javax.persistence.jdbc.password" value="PASSWORD" />
        <property name="javax.persistence.jdbc.driver"
            value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
        <property name="hibernate.show_sql" value="flase" />
        <property name="hibernate.hbm2ddl.auto" value="update" />

        <property name="hibernate.connection.provider_class"
            value="org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider" />

        <property name="hibernate.c3p0.max_size" value="100" />
        <property name="hibernate.c3p0.min_size" value="0" />
        <property name="hibernate.c3p0.acquire_increment" value="1" />
        <property name="hibernate.c3p0.idle_test_period" value="300" />
        <property name="hibernate.c3p0.max_statements" value="0" />
        <property name="hibernate.c3p0.timeout" value="100" />
        <property name="hibernate.jdbc.batch_size" value="50" />
        <property name="hibernate.cache.use_second_level_cache" value="false" />
    </properties>
</persistence-unit>

这是更新后的代码部分:

public static void writeToDB(String filePath) throws IOException {

    EntityManager entityManager = entityManagerFactory.createEntityManager();
    Session session = (Session) entityManager.getDelegate();
    Transaction tx = session.beginTransaction();
    int i = 0;

    URL filePathUrl = null;
    try {
        filePathUrl = new URL(filePath);
    } catch (MalformedURLException e) {
        filePathUrl = (new File(filePath)).toURI().toURL();
    }

    String line = null;
    BufferedReader stream = null;

    try {
        InputStream in = filePathUrl.openStream();
        stream = new BufferedReader(new InputStreamReader(in));


        // Read each line in the file
        MyRow myRow = new MyRow();
        while ((line = stream.readLine()) != null) {
            String[] splitted = line.split(",");
            int num1 = Integer.valueOf(splitted[1]);
            float num2= Float.valueOf(splitted[6]).intValue();

            myRow.setNum1(num1);
            myRow.setNum2(num2);

            session.save(myRow);

            if (i % 50 == 0) { 
                session.flush();
                session.clear();
            }

            i++;

        }
        tx.commit();

    } finally {
        if (stream != null)
            stream.close();
    }
    session.close();

}

已更新,这是 MyRow 的来源:

@Entity
@Table(name="MYTABLE")
public class MyRow {    

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY) 
private Long id;

@Basic
@Column(name = "Num1")
private int Num1;

@Basic
@Column(name = "Num2")
private float Num2;

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public float getNum1() {
    return Num1;
}

public void setNum1(float num1) {
    Num1 = num1;
}

public int getNum2() {
    return Num2;
}

public void setNum2(int num2) {
    Num2 = num2;
}
}

最佳答案

问题

如果你使用 Hibernate 作为你的 ORM,一个主要的性能损失是它的“脏检查”的实现方式(因为没有字节码增强,这是所有基于 JDO 的 ORM 和其他一些的标准,脏检查将永远是一个低效的 hack)。

刷新时,需要对 session 中的每个对象执行脏检查,看它是否“脏”,即它的一个属性自从从数据库加载后发生了变化。对于所有“脏”(已更改)对象,Hibernate 必须生成 SQL 更新以更新代表脏对象的记录。

众所周知,Hibernate 脏检查在除少数对象外的任何对象上都非常慢,因为它需要在内存中的对象与首次从数据库加载对象时拍摄的快照之间执行“逐个字段”比较。对象越多,例如,为显示页面而加载的 HTTP 请求,调用提交时需要进行的脏检查就越多。

Hibernate 脏检查机制的技术细节

您可以在此处阅读更多关于 Hibernate 的脏检查机制作为“逐字段”比较实现的信息:

How does Hibernate detect dirty state of an entity object?

问题在其他ORM中是如何解决的

一些其他 ORM 使用的更有效的机制是使用自动生成的“脏标志”属性而不是“逐个字段”比较,但传统上这只在 ORM(通常是基于 JDO 的 ORM)中可用并促进字节码增强或字节码“编织”,因为它有时被称为,例如 http://datanucleus.org

在字节码增强期间,通过 DataNucleus 或任何其他支持此功能的 ORM,每个实体类都增强为:

  • 添加隐式脏标志属性
  • 将代码添加到类中的每个 setter 方法以在调用时自动设置脏标志

然后在刷新期间,只需要检查脏标志,而不是逐个字段地进行比较——正如您可以想象的那样,速度要快几个数量级。

“逐字段”脏检查的其他负面后果

Hibernate 脏检查的另一个低效是需要在内存中保留每个加载对象的快照,以避免在脏检查期间必须重新加载和检查数据库。

每个对象快照都是其所有字段的集合。

除了 Hibernate 脏检查机制在刷新时的性能影响外,该机制还会给您的应用带来额外的内存消耗和 CPU 使用率,这些消耗与实例化和初始化从数据库加载的每个对象的这些快照相关联- 根据您的应用,可能会达到数千或数百万。

Hibernate 引入了字节码增强功能来解决这个问题,但我从事过许多 ORM 持久化项目(包括 Hibernate 和非 Hibernate),但我还没有看到使用该功能的 Hibernate 持久化项目,可能是由于多种原因:

  • 传统上,当人们评估 ORM 技术时,Hibernate 将其“不需要字节码增强”作为一项功能来宣传
  • Hibernate 的字节码增强实现的历史可靠性问题可能不如从一开始就使用和促进字节码增强的 ORM 成熟
  • 有些人仍然害怕使用字节码增强,因为反“字节码增强”立场的提倡以及某些群体对 ORM 早期使用字节码增强的恐惧

如今,字节码增强被用于许多不同的事情——而不仅仅是持久化。它几乎已成为主流。

关于sql - 带有 HIBERNATE 的 JPA 插入非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19983065/

相关文章:

sql-server - 根据 SQL Server 2005 中的当前月份查找列的总和

node.js - Sequelize Associations - 关联表

c# - 在 C# 中获取数据库表名

database - 如何正确设计数据库的这一部分(循环引用?)

c# - 使用导航属性的 LINQ 查询生成多个 SELECT 语句

php - 多个 sql 查询与 php 过滤器结果集

sql - mysql 中的 varchar(100) 声明使用了多少实际存储空间?

sql-server - 如何为此表选择 PIVOT?

sql - 通过重新排序行和列来对二维表进行排序

c# - asp.net、c#、用户提供的连接字符串和查询