java - 为什么使用 JPA/Eclipselink/Texo 进行批量插入比使用纯 JDBC 插入慢得多(!)?

标签 java jpa jdbc eclipselink emf

我有大量使用 EMF/Texo 组合生成和注释的类。我使用 JPA/Eclipselink 将它们保存在 SQL Server 数据库上。

这工作得很好,但是当需要持久化大量对象时,性能很糟糕。因此,我编写了两个测试用例(请参阅 TestBulkInserts.java),它们比较了使用框架 (foo) 的批量插入与普通 JDBC 批量插入 ( )。

插入 10000 个对象时,这是低于平均大小的批量插入。 foo()bar() 给出以下时间:

  • JPA/Texo 持续时间:19.620ms

  • 普通 JDBC 持续时间: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;

...
}

最佳答案

不仅需要使用批量更新,还需要确保定期提交事务,否则,您会遇到长时间运行的事务,这对 2PL 都不好。或MVCC数据库引擎。

所以,这就是批处理作业的样子:

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();
    }
}

关于java - 为什么使用 JPA/Eclipselink/Texo 进行批量插入比使用纯 JDBC 插入慢得多(!)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44112886/

相关文章:

java - 我可以创建带注释的 Faces 应用程序监听器类吗?

java - 如何使用分组映射流?

java - Jackson JSON 不扩展对象的属性

java - PoolingDataSource - 如何使用特定于驱动程序的PreparedStatement实现

java - 网格中最大的产品

java - Random.nextgaussian() 可以从具有不同均值和标准差的分布中采样值吗?

java - Hibernate Transient Extends问题

java - Hibernate 不会在特定字段上级联更新

java - Gradle buildscript 找不到 Postgresql JDBC 驱动程序

Google Apps 脚本的 Jdbc 连接错误