2010-05-05 142 views
22

下面是我在阅读关于jpa批量插入的几个主题后创建的简单示例,我有2个持久对象用户和站点。一个用户可以有很多网站,所以我们在这里有一对多的关系。假设我想创建用户并创建/链接几个站点到用户帐户。考虑到我愿意为Site对象使用批量插入,以下是代码的样子。JPA/Hibernate批量(批量)插入

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来检查输出。

更新:

后刘宇昆请告知,我已禁用网站的对象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:组织。 hibernate.jdbc.AbstractBatcher - 执行批量大小:2

它的工作原理!

回答

19

如果您使用数据库来生成id,那么Hibernate必须执行一个查询来为每个实体生成主键。

+0

哦,这是有道理!谢谢 – abovesun 2010-05-05 14:33:26

+0

我已更新问题,请您检查一下,谢谢 – abovesun 2010-05-05 14:47:22

+5

您现在如何生成密钥?你必须确保你的密钥是唯一的。 – 2010-05-05 17:13:57

5

查看详细信息我发现绕过hibernate进行批量插入会更有效率。您必须取消ORM(对象关系映射),但仍可以利用与当前会话和事务管理关联的连接。

尽管您暂时失去了ORM的便利性,但回报是显着的,尤其是如果您本机生成的ID,因为休眠通常会为每个INSERT执行一个SELECT

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); 
    } 
} 
+1

我正在使用您提供的相同技术。但仍然存在问题;此方法为每个插入准备一个不同的语句,因为它可以在sql profiler中显示。为了提高性能,需要编译或准备语句一次,然后为其他插入调用该编译语句。 – Max 2015-11-25 06:22:21