【问题标题】:How to insert all rows in a single statement如何在单个语句中插入所有行
【发布时间】:2013-10-11 02:21:41
【问题描述】:

如何在一条语句中插入所有行。这是我的代码,但插入行需要很长时间。

for(Myobject object : objectList)
        getCurrentSession().save(object);

它为每条记录创建一个语句;

insert into myobject (id, type) values (?, ?)
insert into myobject (id, type) values (?, ?)
....

我想做的是;

insert into myobject (id, type) values (?, ?), (?, ?), (?, ?) ......(?, ?);

有没有办法创建这个语句?

【问题讨论】:

    标签: hibernate


    【解决方案1】:

    您可能对batch inserts on Hibernate tutorial感兴趣。

    问题不在于save()操作,因为所有做这个操作的都是将保存的对象放入一级缓存中(在会话中,使其持久化),而是flush()操作触发插入。他们推荐以下方法以实现良好的性能

    在教程中也说过 - 我没有尝试过 - 你可以得到 OutOfMemoryException 用于非常多的持久行,他们似乎推荐 20 作为批量大小。

    当使新对象持久化时,定期flush()然后clear()会话,以控制一级缓存的大小。

    for ( int i=0; i<objectList.size(); i++ ) {
       getCurrentSession().save(objectList.get(i));
       if ( i % 20 == 0 ) { //20, same as the JDBC batch size
        //flush a batch of inserts and release memory:
       session.flush();
       session.clear();
       }
    }
    

    EDIT 还可以将配置文件中的hibernate.jdbc.batch_size 设置为20。如果需要,也可以设置为50。 Hibernate 必须对它们进行分组,而不是 20 个插入,您应该只有一个按 20 分组:

    into myobject (id, type) values (id1, val1), (id2, val2), ......(id20, val20)
    

    【讨论】:

    • 它不会改变任何东西。仍然尝试一一插入所有行。
    • 它应该按其中的 20 个对插入进行分组。您还可以在我刚刚在 SO 上找到的 question 上看到已接受的回复。
    • 我不明白的是我要添加 10.000 条记录,这需要将近 115 秒,而按照您的方式(按 20 分组)需要 114 秒。
    • 如果您直接从数据库客户端插入它们需要多少(如果您有可能对此进行测试)?如果将其更改为 50 会怎样?好吧...这是执行此操作的方法,您可以从教程或其他关于 SO 的问题中看到。我希望能节省更多时间......
    • 我发现hibernate还在尝试一一插入。这是休眠输出; Hibernate: 插入 myobject (id, type) 值 (?, ?) Hibernate: 插入 myobject (id, type) 值 (?, ?)....
    【解决方案2】:

    使用原生查询(原始 SQL):

    entityManager
      .createNativeQuery("insert into myobject (id, type) values (?, ?), (?, ?), (?, ?) ......(?, ?)")
      .setParameter(1, foo)
      // etc
      .execute();
    

    【讨论】:

    • 问题是我有 10.000 条记录 :)
    • 我不会使用休眠。我将通过命令行实用程序加载。一旦记录更新计数超过几十个,Hibernate 就没有希望了。
    【解决方案3】:

    我想添加一种我设法使用Native Queries 解决此问题的方法。我从@Bohemian 的回答中获得了一些灵感。首先,在 Service 层,我们将对象列表拆分为块,然后在 DAO 层插入每个块。

    服务

    @Override
    public void massInsert(List<Object> objects) throws Exception {
    
        // First, let's split the list in chunks.
        final int chunkSize = 50;
        final AtomicInteger counter = new AtomicInteger();
        final Collection<List<Object>> result =
                objects.stream().collect(Collectors.groupingBy(it -> counter.getAndIncrement() / chunkSize)).values();
    
        // Now, for each iteration, we will insert the corresponding details.
        for (List<Objects> oList : result) {
            this.dao.massInsert(oList);
        }
    }
    

    DAO

    @Override
    public void massInsert(List<Object> objects) throws Exception {
        Session session = this.sessionFactory.getCurrentSession();
    
        // Create the query. It is important to consider that we will be using a
        // native query, which we will build from scratch. This is done in order to improve the insert speed.
        String hql = "insert into TABLE (column1, column2) VALUES ";
    
        // For each object, add a new object to insert.
        // In the end we will need to remove the last comma.
        for (int i = 0; i < objects.size(); i++) {
            hql = hql.concat("(?, ?),");
        }
        hql = hql.substring(0, hql.length()-1);
    
        // Create the query.
        Query query = session.createSQLQuery(hql);
    
        // Now, for each object, set the needed parameters.
        int index = 1;
        for (Object o : objects) {
            query.setParameter(index++, o.getAttribute1());
            query.setParameter(index++, o.getAttribute2());
        }
    
        // Execute the query.
        query.executeUpdate();
    
    }
    

    它比逐行插入要快得多。希望对您有所帮助。

    【讨论】:

      【解决方案4】:

      只需为休眠启用“DEBUG”级别日志记录,然后查看是否正在发生批量插入。

        log4j.rootLogger=TRACE, file, stdout
      
        log4j.logger.org.hibernate=TRACE
      
        log4j.logger.org.hibernate.type=ALL
      

      您应该会看到如下所示的日志。

        DEBUG AbstractBatcher:66 - Executing batch size: 20
      

      您看到的插入语句是在调用 ps.executeBatch() 之前生成的。 org.hibernate.jdbc.BatchingBatcher 的第 70 行。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-05-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-11
        • 1970-01-01
        • 2023-03-08
        相关资源
        最近更新 更多