这是我在阅读了几个关于jpa批量插入的主题后创建的简单示例,我有2个持久对象User和Site。一个用户可以有很多站点,所以我们在这里有一对多的关系。假设我想创建用户并创建/链接几个站点到用户帐户。考虑到我愿意为Site对象使用批量插入,代码就是这样的。
User user = new User("John Doe");
user.getSites().add(new Site("google.com", user));
user.getSites().add(new Site("yahoo.com", user));
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
entityManager.persist(user);
tx.commit();
但是当我运行这段代码(我使用hibernate作为jpa实现提供程序)时,我看到以下sql输出:
Hibernate: insert into User (id, name) values (null, ?)
Hibernate: call identity()
Hibernate: insert into Site (id, url, user_id) values (null, ?, ?)
Hibernate: call identity()
Hibernate: insert into Site (id, url, user_id) values (null, ?, ?)
Hibernate: call identity()
所以,我的意思是“真正的”批量插入不起作用或我感到困惑?
这个示例项目的source code,这是maven项目,因此您只需下载并运行mvn install来检查输出。
更新:
在Ken Liu亲切地建议之后,我已经禁用了Site对象id自动生成:
User user = new User("John Doe");
user.getSites().add(new Site(1, "google.com", user));
user.getSites().add(new Site(2, "yahoo.com", user));
entityManager.setFlushMode(FlushModeType.COMMIT);
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
entityManager.persist(user);
tx.commit();
现在我在调试输出中有以下行:
DEBUG:org.hibernate.jdbc.AbstractBatcher - 执行批量大小:2
有效!
答案 0 :(得分:19)
如果您正在使用数据库生成ID,那么Hibernate必须执行查询以生成每个实体的主键。
答案 1 :(得分:6)
我写了一篇简短的博客,讨论批量插入陷阱,并且还有一个指向小项目的指针,该项目具有所有正确的配置,可以开始使用Hibernate进行批量插入。请参阅http://sensiblerationalization.blogspot.com/2011/03/quick-tip-on-hibernate-batch-operation.html
上的详细信息答案 2 :(得分:6)
我发现绕过hibernate进行批量插入会更有效率。您必须取消ORM(对象关系映射),但您仍然可以利用与当前会话和事务管理关联的连接。
虽然你暂时失去了你的ORM的便利性,但是回报很重要,特别是如果你原生地生成了ID,因为hibernate通常会为每个SELECT
执行一个INSERT
。
Session.doWork
对于促进这一点非常方便。
private MyParentObject saveMyParentObject(final MyParentObject parent, final List<MyChildObject> children)
{
transaction = session.beginTransaction();
try
{
session.save(parent); // NOTE: parent.parentId assigned and returned here
session.doWork(new Work()
{
public void execute(Connection con) throws SQLException
{
// hand written insert SQL - can't use hibernate
PreparedStatement st = con.prepareStatement("INSERT INTO my_child (parent_id, name, ...) values (?, ?, ...)");
for (MyChildObject child : children)
{
MyChildObject child = new MyChildObject();
child.setParentId(parent.getParentId()); // assign parent id for foreign key
// hibernate can't help, determine jdbc parameters manually
st.setLong(1, child.getParentId());
st.setString(2, child.getName());
...
st.addBatch();
}
// NOTE: you may want to limit the size of the batch
st.executeBatch();
}
});
// if your parent has a OneToMany relationship with child(s), refresh will populate this
session.refresh(parent);
transaction.commit();
return parent;
}
catch(Throwable e)
{
transaction.rollback();
throw new RuntimeException(e);
}
}