2017-05-22 9 views
1

私は生成され、EMF/Texoの組み合わせを使用して注釈が付けられた多数のクラスを持っています。 JPA/Eclipselinkを使用してSQL Serverデータベースにそれらを保持します。JPA/Eclipselink/Texoでのバルク挿入が、純粋なJDBC挿入よりも遅い(!)のはなぜですか?

これはうまくいきますが、多数のオブジェクトを永続化する必要がある場合、パフォーマンスはひどいです。そこで、フレームワーク(foo)とプレーンJDBCの一括挿入(bar)を使用して、一括挿入のパフォーマンスを比較する2つのテストケース(TestBulkInserts.javaを参照)を作成しました。

オブジェクトを挿入するときは、平均サイズよりも小さい一括挿入です。

-duration JPA/Texo::foo()、およびbar()は、以下の時間を与える

-duration平野JDBC 19.620ms:892ms

をこのような巨大な違いがあるなぜ私は(思ったんだけど20倍以上!)。より大きなサイズではさらに悪化します。

DatabaseObjectクラスはPersistableObjectClass.java(以下を参照)を拡張し、両方ともTexo + EMFで生成されます(それぞれのDAOクラスを含む)。

私は、必要な接続の詳細を除いて、persistence.xmlに特定の設定を追加していません。

TestBulkInserts.java:

import java.sql.Connection; 
import java.sql.Date; 
import java.sql.DriverManager; 
import java.sql.PreparedStatement; 
... 
import com.ownproject.loader.generated.DbModelPackage; 
import com.ownproject.loader.DatabaseObject; 
import com.ownproject.loader.dao.DatabaseObjectDao; 
import javax.persistence.Persistence; 
import org.eclipse.emf.texo.server.store.EntityManagerProvider; 
import org.junit.Test; 

public class TestBulkInserts { 

private static final int NUM_LOOPS = 10000; 

@Test 
public void foo() { 
    TestMethods.connectTestDBandEMF(); 
    // basically does this 
    // DbModelPackage.initialize(); 
    // EntityManagerProvider.getInstance().setEntityManagerFactory(Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_TEST)); 

    Stopwatch sw = Stopwatch.createStarted(); 

    DatabaseObjectDao dao = new DatabaseObjectDao(); 
    dao.getEntityManager().getTransaction().begin(); 
    for (int i = 0; i < NUM_LOOPS; i++) { 
    DatabaseObject dbo = new DatabaseObject(); 
    dbo.setString(UUID.randomUUID().toString()); 
    dbo.setInsert_time(Date.valueOf(LocalDate.now())); 
    dao.insert(dbo); 
    } 
    dao.getEntityManager().getTransaction().commit(); 

    sw.stop(); 
    System.out.println(String.format("Duration JPA/Texo: %,dms", sw.elapsed(TimeUnit.MILLISECONDS))); 
    } 

@Test 
public void bar() throws ClassNotFoundException, SQLException { 
    Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); 
    String connectionUrl = "jdbc:sqlserver://hostname:1433;databaseName=local_test;user=sa;password=blablub;"; 
    Connection con = DriverManager.getConnection(connectionUrl); 
    con.setAutoCommit(false); 

    Stopwatch sw = Stopwatch.createStarted(); 

    PreparedStatement insertStatement = con.prepareStatement("INSERT INTO DatabaseObject(b_id, insert_time) VALUES (?, ?)"); 
    for (int i = 0; i < NUM_LOOPS; i++) { 
    insertStatement.setString(1, UUID.randomUUID().toString()); 
    insertStatement.setDate(2, Date.valueOf(LocalDate.now())); 
    insertStatement.addBatch(); 
    } 
    insertStatement.executeBatch(); 
    con.commit(); 
    con.close(); 

    sw.stop(); 
    System.out.println(String.format("Duration plain JDBC: %,dms", sw.elapsed(TimeUnit.MILLISECONDS))); 
    } 
} 

PersistableObjectClass.java:

import javax.persistence.Basic; 
... 
import javax.persistence.TemporalType; 

@Entity(name = "PersistableObjectClass") 
@MappedSuperclass() 
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 
public abstract class PersistableObjectClass { 

    @Basic() 
    @Temporal(TemporalType.TIMESTAMP) 
    private Date insert_time = null; 

    @Id() 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    private int s_id = 0; 

... 
} 
+0

は慎重にこれを読む:

だから、これは好きなバッチ・ジョブがどのように見えるかですバッチセッションバッチ – zloster

+0

ヒントありがとう!あなたは記憶上の問題を意味しましたか?フラッシングは積載時間を短縮しなかったので。 – KayleeTheMech

+1

JPAはオーバーヘッドを追加するため、いくつかの違いが予想されますが、数字を説明する変数が多すぎます。リンゴを実際に比較していることを確認するために生成されたSQLを確認するためにログオンしましたか? JPA/EclipseLinkでどのような設定を使用していますか?なぜあなたのバッチサイズに一致する事前割り当てを可能にするものではなく、シーケンス管理にIDを使用していますか? – Chris

答えて

2

としては、this articleで説明していないだけで、あなたがbatch updatesを使用する必要がありますが、あなたも確保する必要があることトランザクションは定期的にコミットされます。それ以外の場合は長時間実行されるトランザクションに実行されますが、これは2PLまたはMVCCデータベースエンジン。 https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#:

int entityCount = 50; 
int batchSize = 25; 

EntityManager entityManager = null; 
EntityTransaction transaction = null; 

try { 
    entityManager = entityManagerFactory() 
     .createEntityManager(); 

    transaction = entityManager.getTransaction(); 
    transaction.begin(); 

    for (int i = 0; i < entityCount; ++i) { 
     if (i > 0 && i % batchSize == 0) { 
      entityManager.flush(); 
      entityManager.clear(); 

      transaction.commit(); 
      transaction.begin(); 
     } 

     Post post = new Post( 
      String.format("Post %d", i + 1) 
     ); 
     entityManager.persist(post); 
    } 

    transaction.commit(); 
} catch (RuntimeException e) { 
    if (transaction != null && 
     transaction.isActive()) { 
     transaction.rollback(); 
    } 
    throw e; 
} finally { 
    if (entityManager != null) { 
     entityManager.close(); 
    } 
} 
関連する問題