2

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;
}
}
4

4 に答える 4

7

問題

ORM として Hibernate を使用する場合の主要なパフォーマンス ヒットの 1 つは、その「ダーティ チェック」の実装方法です (すべての JDO ベースの ORM およびその他のいくつかで標準であるバイト コード拡張がないと、ダーティ チェックは常に非効率的なハックになるため) )。

フラッシュする場合、セッション内のすべてのオブジェクトに対してダーティ チェックを実行して、オブジェクトが「ダーティ」であるかどうか、つまりデータベースからロードされてから属性の 1 つが変更されているかどうかを確認する必要があります。すべての「ダーティ」 (変更された) オブジェクトに対して、Hibernate は SQL 更新を生成して、ダーティ オブジェクトを表すレコードを更新する必要があります。

Hibernate のダーティ チェックは、オブジェクトが最初にデータベースからロードされたときに取得されたスナップショットを使用して、メモリ内のオブジェクト間で「フィールドごと」の比較を実行する必要があるため、少数のオブジェクト以外では非常に遅いことで知られています。たとえば、ページを表示するために HTTP リクエストが読み込まれるオブジェクトが多いほど、commit が呼び出されたときに必要なダーティ チェックが多くなります。

Hibernate のダーティ チェック メカニズムの技術的詳細

ここで、「フィールドごと」の比較として実装された Hibernate のダーティ チェック メカニズムについて詳しく読むことができます。

Hibernate はエンティティ オブジェクトのダーティ状態をどのように検出しますか?

他の ORM で問題がどのように解決されるか

他のいくつかの ORM で使用されるより効率的なメカニズムは、「フィールドごと」の比較ではなく、自動生成された「ダーティ フラグ」属性を使用することですが、これは従来、バイトを使用および促進する ORM (通常は JDO ベースの ORM) でのみ使用可能でした。http://datanucleus.orgなどと呼ばれることもあるコード強化またはバイトコード「ウィービング」

DataNucleus またはこの機能をサポートするその他の ORM によるバイト コード拡張中に、各エンティティ クラスは次のように拡張されます。

  • 暗黙のダーティ フラグ属性を追加する
  • クラスの各セッター メソッドにコードを追加して、呼び出されたときにダーティ フラグを自動的に設定します。

次に、フラッシュ中に、フィールドごとの比較を実行する代わりに、ダーティ フラグのみをチェックする必要があります。これは、ご想像のとおり、桁違いに高速です。

「フィールドごと」のダーティ チェックのその他の悪影響

Hibernate ダーティ チェックのもう 1 つの非効率性は、ロードされたすべてのオブジェクトのスナップ ショットをメモリに保持して、ダーティ チェック中にデータベースをリロードしてチェックする必要がないことです。

各オブジェクト スナップ ショットは、そのすべてのフィールドのコレクションです。

フラッシュ時の Hibernate ダーティ チェック メカニズムのパフォーマンス ヒットに加えて、このメカニズムは、データベースからロードされるすべての単一オブジェクトのこれらのスナップショットのインスタンス化と初期化に関連する余分なメモリ消費と CPU 使用率によってアプリに負担をかけます。アプリケーションに応じて、数千または数百万になります。

Hibernate はこれに対処するためにバイト コード拡張を導入しましたが、私は多くの ORM 永続化プロジェクト (Hibernate と非 Hibernate の両方) に取り組んできましたが、おそらくいくつかの理由により、その機能を使用する Hibernate 永続化プロジェクトをまだ見ていません。

  • Hibernate は伝統的に、人々が ORM 技術を評価する際の機能として「バイトコード拡張の要件がない」ことを宣伝してきました
  • Hibernate のバイトコード拡張実装の歴史的な信頼性の問題は、最初からバイトコード拡張を使用および促進した ORM ほど成熟していない可能性があります。
  • アンチ「バイトコード拡張」スタンスの推進と、ORM の初期のバイトコード拡張の使用に関して特定のグループが人々に植え付けた恐怖のために、バイトコード拡張を使用することをいまだに恐れている人もいます。

最近では、永続化だけでなく、さまざまな目的でバイト コード拡張が使用されています。ほぼ主流になりました。

于 2017-02-13T19:55:42.880 に答える
6

JDBC バッチ処理を有効にするには、プロパティ hibernate.jdbc.batch_size を 10 から 50 (int のみ) に初期化する必要があります。

hibernate.jdbc.batch_size=50

それでも期待したほど速くない場合は、上記のドキュメントを注意事項とセクション 4.1 に注意して確認します。特に、「ID ジェネレーターを使用する場合、Hibernate は JDBC レベルでの挿入バッチ処理を透過的に無効にします」という注。

于 2013-11-14T16:41:18.077 に答える
2

休止状態の「デフォルトモード」は遅いです。

その利点は、オブジェクト リレーショナル マッピングと一部のキャッシュです (ただし、一括挿入にはあまり役に立ちません)。

代わりにバッチ処理を使用http://docs.jboss.org/hibernate/core/4.0/devguide/en-US/html/ch04.html

于 2013-11-14T16:36:17.070 に答える