【问题标题】:JPA/Hibernate bulk(batch) insertJPA/Hibernate 批量(批量)插入
【发布时间】:2011-02-15 22:17:28
【问题描述】:

这是我在阅读了几个关于 jpa 批量插入的主题后创建的简单示例,我有 2 个持久对象用户和站点。一个用户可以有多个站点,所以我们在这里有一对多的关系。假设我想创建用户并将多个站点创建/链接到用户帐户。考虑到我愿意为 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 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();

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

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

有效!

【问题讨论】:

    标签: java hibernate jpa persistence openjpa


    【解决方案1】:

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

    【讨论】:

    • 哦,这是有道理的!谢谢
    • 您现在如何生成密钥?您必须确保您的密钥是唯一的。
    • 不可以让Hibernate在插入1000个对象之前执行UPDATE sometbl SET counter=counter+1000,然后只使用它刚刚保留的1000个id吗?
    • @KenLiu 这是否意味着当通过序列生成PK时,您不能使用oracle批量插入多个条目?
    【解决方案2】:

    我发现绕过休眠进行批量插入更有效。您必须取消 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);
        }   
    }
    

    【讨论】:

    • 我正在使用与您提供的相同技术。但是还是有问题;此方法为每个插入准备不同的语句,因为它可以在 sql profiler 中显示。为了提高性能,它需要编译或准备语句一次,然后为其余的插入调用该编译语句。
    • @Max 从 DBMS 的角度来看,语句是相同的,并且会选择相同的访问计划。只有在不使用变量绑定的情况下才会有所不同(不是这种情况)。
    • 保存大约 50k 条记录只用了大约 2 秒!太棒了,谢谢!
    【解决方案3】:

    我写了一篇简短的博客,其中讨论了批量插入陷阱,并且还提供了指向具有所有正确配置的小型项目的指针,可以开始使用 Hibernate 进行批量插入。详情见http://sensiblerationalization.blogspot.com/2011/03/quick-tip-on-hibernate-batch-operation.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-16
      • 2013-07-23
      • 2021-04-23
      • 2016-11-20
      • 1970-01-01
      • 2015-07-03
      • 2011-07-04
      • 1970-01-01
      相关资源
      最近更新 更多