Java JPA/Hibernate 批量(批量)插入

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2773302/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 12:42:02  来源:igfitidea点击:

JPA/Hibernate bulk(batch) insert

javahibernatejpapersistenceopenjpa

提问by abovesun

Here is simple example I've created after reading several topics about jpa bulk inserts, I have 2 persistent objects User, and Site. One user could have many site, so we have one to many relations here. Suppose I want to create user and create/link several sites to user account. Here is how code looks like, considering my willing to use bulk insert for Site objects.

这是我在阅读有关 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();

But when I run this code (I'm using hibernate as jpa implementation provider) I see following sql output:

但是当我运行此代码时(我使用 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()

So, I means "real" bulk insert not works or I am confused?

所以,我的意思是“真正的”批量插入不起作用还是我很困惑?

Here is source codefor this example project, this is maven project so you have only download and run mvn install to check output.

这是此示例项目的源代码,这是 maven 项目,因此您只需下载并运行 mvn install 即可检查输出。

UPDATED:

更新:

After Ken Liu kindly advise, I've disabled Site object id auto generation:

在 Ken Liu 善意的建议之后,我禁用了站点对象 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();

Now I have following line in debug output:

现在我在调试输出中有以下行:

DEBUG: org.hibernate.jdbc.AbstractBatcher - Executing batch size: 2

调试:org.hibernate.jdbc.AbstractBatcher - 执行批量大小:2

It works!

有用!

采纳答案by Ken Liu

If you're using the database to generate ids, then Hibernate has to execute a query to generate the primary key for each entity.

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

回答by prabhat jha

I have written a short blog which talks about batch insert gotchas and also has pointer to small project that has all the right configurations to get started with batch insert with Hibernate. See the details at http://sensiblerationalization.blogspot.com/2011/03/quick-tip-on-hibernate-batch-operation.html

我写了一个简短的博客,其中讨论了批量插入问题,并且还提供了指向小项目的指针,该项目具有所有正确的配置,可以开始使用 Hibernate 进行批量插入。请参阅http://sensiblerationalization.blogspot.com/2011/03/quick-tip-on-hibernate-batch-operation.html 上的详细信息

回答by pstanton

I have found it much more efficient to bypass hibernate for bulk inserts. You must do away with ORM (object relational mapping) but you can still leverage the connection associated with the current session and the transaction management.

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

While you temporarily lose the convenience of your ORM, the payoff is significant, especially if you have natively generated Ids since hibernate would normally perform one SELECTfor each INSERT.

虽然您暂时失去了 ORM 的便利性,但回报是显着的,特别是如果您本机生成了 Id,因为 hibernate 通常会SELECT为每个INSERT.

Session.doWorkis very handy for facilitating this.

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