批量插入导致失败
Batch insert causing failure
我有一个任务需要使用休眠将大量数据插入数据库。我目前正在测试插入 500,000 个实体,每个实体都有一个关系,因此总共插入 1,000,000 个。
基于此 guide 我创建了以下实际有效的代码。所有数据都已插入并提交,没有错误。
import javax.annotation.Resource;
import javax.ejb.*;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.*;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.TransformerException;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class WhosebugExample {
@PersistenceContext
private EntityManager entityManager;
@Resource
private SessionContext sessionContext;
@EJB
private XmlProcessorFactory xmlProcessorFactory;
@EJB
private TaskManagerBean taskManagerBean;
public void processFile(String[] args, Task task) throws HeuristicRollbackException, RollbackException, HeuristicMixedException, SystemException, IOException, TransformerException, ParseException, XMLStreamException, NotSupportedException {
UserTransaction tx = null;
XmlProcessor instance = xmlProcessorFactory.getInstance(new File("data.xml"));
XmlElement nextElement = instance.getNextElement();
int i = 0;
int batchSize = 50;
entityManager.setProperty("hibernate.jdbc.batch_size", batchSize);
tx = sessionContext.getUserTransaction();
tx.begin();
while (nextElement != null) {
Entry entry = new Entry(nextElement.getUserReference(), nextElement.getXml());
entityManager.persist(entry);
if (i % batchSize == 0) {
entityManager.flush();
entityManager.clear();
}
nextElement = instance.getNextElement();
i++;
}
task.setStatus(status);
task.setEndTime(now());
// This gives the OutOfMemoryError
entityManager.merge(task);
tx.commit();
}
}
这将在我调用 taskManagerBean.update() 的行中失败,并出现以下错误:
2017-03-31 08:49:30,212 ERROR [org.jboss.as.ejb3.invocation] (EJB default - 3) WFLYEJB0034:
EJB Invocation failed on component TaskManagerBean for method public void
TaskManagerBean.update(Task,TaskStatus):
javax.ejb.EJBTransactionRolledbackException: org.hibernate.exception.GenericJDBCException:
could not load an entity: [Task#3]
at org.jboss.as.ejb3.tx.CMTTxInterceptor.handleInCallerTx(CMTTxInterceptor.java:159)
at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInCallerTx(CMTTxInterceptor.java:256)
...
at TaskManagerBean$$$view18.update(Unknown Source)
at StoreEntriesBean.processFile(StoreEntriesBean.java:117)
...
at org.jboss.threads.JBossThread.run(JBossThread.java:320)
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not load an entity: [Task#3]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1692)
...
at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInCallerTx(CMTTxInterceptor.java:254)
... 104 more
Caused by: org.hibernate.exception.GenericJDBCException: could not load an entity: [Task#3]
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
...
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1161)
... 135 more
Caused by: java.sql.SQLException: Error
at org.jboss.jca.adapters.jdbc.WrappedConnection.checkException(WrappedConnection.java:1972)
...
at org.hibernate.loader.Loader.loadEntity(Loader.java:2204)
... 155 more
Caused by: java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Arrays.java:3664)
...
at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:478)
Runtime.getRuntime().freeMemory()
returns 71540896 就在错误之前。
我用 -Xmx2048m 启动 Wildfly,快速浏览一下内存使用情况表明它只使用了不到一半。
我已经尝试在每 1,000 次插入后提交插入的条目。这会触发 TaskManagerBean
并且在几次更新后它会以同样的方式失败。
在少数情况下,我在完成大量批处理作业并且要更新拥有实体后也看到了这个错误。
我试过一个只有 5,000 个条目的文件,整个过程工作正常。
这是 MySQL 驱动程序中的错误还是我在这里做错了什么?
尝试通过 EJB 运行 批处理几乎行不通,因为您 运行 内存不足或事务超时。
这是“批量申请”的原因之一
开发了 Java 平台”(又名 JSR-352)规范。
WildFly 10 为您提供了一个实现。
您可以在 Batch Applications in Java EE 7 - Undertanding JSR 352 Concepts: TOTD #192
阅读更多相关信息
您关闭会话或实体管理器吗?
我从未使用过 EntityManager,但当您在 Java 中处理数据库时,您应该始终 "explicitly" 在作业(事务)完成后关闭连接
调用 EntityManger.merge()
是导致错误的原因。
我不是 100% 熟悉 Hibernate,但显然 merge
在这种情况下会获取从 Task
到 Entry
的整个关系,在这种情况下是一个包含 500,000 个条目的集合 - 即使这关系是延迟加载的。
我用 Entity.find(Task.class, taskId)
替换了 merge 并在该实例上设置了状态,这解决了我的问题。
同时我介绍了Java Batch Processing,我只能推荐。它避免了必须自己编写批处理作业。
我有一个任务需要使用休眠将大量数据插入数据库。我目前正在测试插入 500,000 个实体,每个实体都有一个关系,因此总共插入 1,000,000 个。 基于此 guide 我创建了以下实际有效的代码。所有数据都已插入并提交,没有错误。
import javax.annotation.Resource;
import javax.ejb.*;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.*;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.TransformerException;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class WhosebugExample {
@PersistenceContext
private EntityManager entityManager;
@Resource
private SessionContext sessionContext;
@EJB
private XmlProcessorFactory xmlProcessorFactory;
@EJB
private TaskManagerBean taskManagerBean;
public void processFile(String[] args, Task task) throws HeuristicRollbackException, RollbackException, HeuristicMixedException, SystemException, IOException, TransformerException, ParseException, XMLStreamException, NotSupportedException {
UserTransaction tx = null;
XmlProcessor instance = xmlProcessorFactory.getInstance(new File("data.xml"));
XmlElement nextElement = instance.getNextElement();
int i = 0;
int batchSize = 50;
entityManager.setProperty("hibernate.jdbc.batch_size", batchSize);
tx = sessionContext.getUserTransaction();
tx.begin();
while (nextElement != null) {
Entry entry = new Entry(nextElement.getUserReference(), nextElement.getXml());
entityManager.persist(entry);
if (i % batchSize == 0) {
entityManager.flush();
entityManager.clear();
}
nextElement = instance.getNextElement();
i++;
}
task.setStatus(status);
task.setEndTime(now());
// This gives the OutOfMemoryError
entityManager.merge(task);
tx.commit();
}
}
这将在我调用 taskManagerBean.update() 的行中失败,并出现以下错误:
2017-03-31 08:49:30,212 ERROR [org.jboss.as.ejb3.invocation] (EJB default - 3) WFLYEJB0034:
EJB Invocation failed on component TaskManagerBean for method public void
TaskManagerBean.update(Task,TaskStatus):
javax.ejb.EJBTransactionRolledbackException: org.hibernate.exception.GenericJDBCException:
could not load an entity: [Task#3]
at org.jboss.as.ejb3.tx.CMTTxInterceptor.handleInCallerTx(CMTTxInterceptor.java:159)
at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInCallerTx(CMTTxInterceptor.java:256)
...
at TaskManagerBean$$$view18.update(Unknown Source)
at StoreEntriesBean.processFile(StoreEntriesBean.java:117)
...
at org.jboss.threads.JBossThread.run(JBossThread.java:320)
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not load an entity: [Task#3]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1692)
...
at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInCallerTx(CMTTxInterceptor.java:254)
... 104 more
Caused by: org.hibernate.exception.GenericJDBCException: could not load an entity: [Task#3]
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
...
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1161)
... 135 more
Caused by: java.sql.SQLException: Error
at org.jboss.jca.adapters.jdbc.WrappedConnection.checkException(WrappedConnection.java:1972)
...
at org.hibernate.loader.Loader.loadEntity(Loader.java:2204)
... 155 more
Caused by: java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Arrays.java:3664)
...
at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:478)
Runtime.getRuntime().freeMemory()
returns 71540896 就在错误之前。
我用 -Xmx2048m 启动 Wildfly,快速浏览一下内存使用情况表明它只使用了不到一半。
我已经尝试在每 1,000 次插入后提交插入的条目。这会触发 TaskManagerBean
并且在几次更新后它会以同样的方式失败。
在少数情况下,我在完成大量批处理作业并且要更新拥有实体后也看到了这个错误。
我试过一个只有 5,000 个条目的文件,整个过程工作正常。
这是 MySQL 驱动程序中的错误还是我在这里做错了什么?
尝试通过 EJB 运行 批处理几乎行不通,因为您 运行 内存不足或事务超时。
这是“批量申请”的原因之一 开发了 Java 平台”(又名 JSR-352)规范。
WildFly 10 为您提供了一个实现。
您可以在 Batch Applications in Java EE 7 - Undertanding JSR 352 Concepts: TOTD #192
阅读更多相关信息您关闭会话或实体管理器吗?
我从未使用过 EntityManager,但当您在 Java 中处理数据库时,您应该始终 "explicitly" 在作业(事务)完成后关闭连接
调用 EntityManger.merge()
是导致错误的原因。
我不是 100% 熟悉 Hibernate,但显然 merge
在这种情况下会获取从 Task
到 Entry
的整个关系,在这种情况下是一个包含 500,000 个条目的集合 - 即使这关系是延迟加载的。
我用 Entity.find(Task.class, taskId)
替换了 merge 并在该实例上设置了状态,这解决了我的问题。
同时我介绍了Java Batch Processing,我只能推荐。它避免了必须自己编写批处理作业。