Sql 带有HIBERNATE插入的JPA非常慢

Sql 带有HIBERNATE插入的JPA非常慢,sql,sql-server,database,hibernate,jpa,Sql,Sql Server,Database,Hibernate,Jpa,我正在尝试使用JAP和HIBERNATE将一些数据插入SQLServer2008R2。除了速度非常慢之外,一切都“正常”。插入20000行大约需要45秒,而C#脚本只需要不到1秒 这个领域的任何老手都能提供一些帮助吗?我会非常感激的 更新:从下面的答案中得到了一些很好的建议,但仍然没有达到预期效果。速度是一样的 以下是更新后的persistence.xml: <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persi

我正在尝试使用JAP和HIBERNATE将一些数据插入SQLServer2008R2。除了速度非常慢之外,一切都“正常”。插入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>
已更新,以下是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.jdbc.batch_size属性初始化为介于10和50之间(仅限int)

如果仍然没有预期的那么快,那么我将回顾上面的文档,注意注释和第4.1节。特别是注意到,“如果使用标识标识符生成器,Hibernate将在JDBC级别透明地禁用插入批处理。”

问题 如果您将Hibernate用作ORM,那么主要的性能问题之一就是它的“脏检查”的实现方式(因为如果没有字节码增强(这是所有基于JDO的ORM和其他一些ORM的标准配置),脏检查将永远是一种低效的攻击)

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

众所周知,Hibernate脏检查在除少量对象之外的任何对象上都非常慢,因为它需要在内存中的对象之间执行“逐字段”比较,并在对象首次从数据库加载时获取快照。加载越多的对象(比如HTTP请求)以显示页面,则调用commit时需要的脏检查就越多

Hibernate脏检查机制的技术细节 您可以在此处阅读有关Hibernate的脏检查机制的更多信息,该机制是作为“逐字段”比较实现的:

在其他ORM中如何解决该问题 其他一些ORM使用的一种更有效的机制是使用自动生成的“脏标志”属性,而不是“逐字段”比较,但这通常仅在使用和促进字节码增强或字节码“编织”的ORM(通常是基于JDO的ORM)中可用,例如。,及其他

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

  • 添加隐式脏标志属性
  • 将代码添加到类中的每个setter方法,以便在调用时自动设置脏标志
然后在刷新过程中,只需要检查脏标志,而不需要执行逐字段比较——正如您所想象的,这要快几个数量级

“逐字段”脏检查的其他负面后果 Hibernate脏检查的另一个效率是需要在内存中保留每个加载对象的快照,以避免在脏检查期间必须重新加载并检查数据库

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

除了刷新时Hibernate脏检查机制的性能受到影响之外,这种机制还为你的应用程序增加了额外的内存消耗和CPU使用,这些额外的内存消耗和CPU使用与实例化和初始化从数据库加载的每个对象的快照有关——根据你的应用程序,快照的数量可能达到数千或数百万

Hibernate引入了字节码增强来解决这个问题,但我已经在许多ORM持久化项目(Hibernate和非Hibernate)上工作过,我还没有看到使用该功能的Hibernate持久化项目,可能是由于以下原因:

  • Hibernate传统上在人们评估ORM技术时,将其“不需要字节码增强”作为一项特性加以推广
  • Hibernate的字节码增强实现存在历史可靠性问题,该实现可能不如从一开始就使用和促进字节码增强的ORM成熟
  • 一些人仍然害怕使用字节码增强,这是因为他们提倡反“字节码增强”的立场,以及在ORMs的早期,某些团体向人们灌输了对字节码增强的恐惧

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

这是一个古老的话题,但今天在寻找其他东西时遇到了这个问题。不幸的是,我没有很好地理解和记录这个常见的问题。很长一段时间以来,Hibernate的文档只有上面发布的简短注释。 从版本5开始,有一个更好但仍然很简单的解释:

超大集合插入速度慢的问题只是Id生成策略选择不当:

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY) 
使用标识策略时,需要了解的是,数据库服务器在物理插入上创建行的标识。Hibernate需要知道分配的Id,以便在会话中使对象处于持久状态。数据库生成的Id仅在insert的响应中已知。Hibernate别无选择,只能执行20000个单独的插入来检索生成的ID。据我所知,它不适用于批处理,不适用于Sybase,也不适用于MSSQL。这就是为什么,无论您如何努力,并且正确配置了所有批处理属性,Hibernate都将执行单独的插入

唯一的解决办法是
hibernate.jdbc.batch_size=50
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY) 
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@GenericGenerator(strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator")