我有大量使用 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/