JPA/Hibernate 批量(批量)插入

发布于 2024-08-31 01:57:21 字数 1533 浏览 9 评论 0原文

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

那么,我的意思是“真正的”批量插入不起作用,或者我很困惑?

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

更新:

在 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();

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

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

它有效!

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.

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: 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 code for this example project, this is maven project so you have only download and run mvn install to check output.

UPDATED:

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

    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

It works!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

为你鎻心 2024-09-07 01:57:22

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

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

胡大本事 2024-09-07 01:57:22

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

虽然您暂时失去了 ORM 的便利性,但回报是巨大的,特别是如果您有本机生成的 Id,因为 hibernate 通常会为每个 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);
    }   
}

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.

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 SELECT for each INSERT.

Session.doWork is very handy for facilitating this.

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);
    }   
}
难如初 2024-09-07 01:57:22

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文