代码之家  ›  专栏  ›  技术社区  ›  Andriy Kopachevskyy

JPA/Hibernate批量(批)插入

  •  24
  • Andriy Kopachevskyy  · 技术社区  · 14 年前

    下面是我在阅读了关于jpa批量插入的几个主题之后创建的简单示例,我有2个持久对象user和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善意建议之后,我禁用了站点对象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();
    

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

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

    它起作用了!

    3 回复  |  直到 10 年前
        1
  •  19
  •   Ken Liu    14 年前

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

        2
  •  6
  •   prabhat jha    13 年前

    我写了一个简短的博客,其中谈到批量插入gotchas,还有一个指向小项目的指针,这个小项目拥有所有正确的配置,可以使用hibernate开始批量插入。详情请参见 http://sensiblerationalization.blogspot.com/2011/03/quick-tip-on-hibernate-batch-operation.html

        3
  •  6
  •   pstanton    10 年前

    我发现绕过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);
        }   
    }