StringBuilder替代方案,用于更好的内存分配

时间:2018-06-19 08:29:30

标签: java string jpa out-of-memory heap-memory

我正在使用此StringBuilder来在查询中添加内容:

Integer lastEntryInEntityId = 1;//acquired through another query
Integer tmpValueForEntityId;
Integer lastEntryInEntity2Id = 1;//acquired through another query

StringBuilder queryString = new StringBuilder("insert 
into entity(column,column_1,column_2,column_3) values");
StringBuilder queryString2 = new StringBuilder("insert 
into entity2(column,column_1,column_2,column_3) values");

for(Object[] entityToCopy : entitiesToCopy){
    Entity entity= (Entity )entityToCopy[0];
    tmpValueForEntityId= lastEntryInEntityId ;
    queryString.append("("+ lastEntryInEntityId ++ +","+entity.getProperty()+","+entity[1]+","+entity.getProperty2()+"),");

    for(Entity2 entity2 : entity.getEntity2Collection()){
        queryString2.append("("+lastEntryInEntity2Id ++ +","+tmpValueForEntityId+","+entity.getProperty2()+","+entity.getProperty3()+"),");
    }
}

此代码需要太多时间和内存。它实际上会在一段时间后(当entitiesToCopy太多时)添加到第二个StringBuilder时抛出OutOfMemoryException。

我还能如何编写此代码以加快速度并减少内存使用?

注意:首选java 8解决方案。

注2:我使用EntityManager。

2 个答案:

答案 0 :(得分:3)

您应该在concat()

中使用+代替StringBuilder
    for(Object[] entityToCopy : entitiesToCopy){
    Entity entity= (Entity )entityToCopy[0];
    tmpValueForEntityId= lastEntryInEntityId ;
    queryString.append("(").append(lastEntryInEntityId++).append(",").append(entity.getProperty()).append(",").append(entity[1]).append(",").append(entity.getProperty2()).append("),");

    for(Entity2 entity2 : entity.getEntity2Collection()){
        queryString2.append("(").append(lastEntryInEntity2Id ++).append(",").append(tmpValueForEntityId).append(",").append(entity.getProperty2()).append(",").append(entity.getProperty3()).append("),");
    }
}

为了获得更好的性能,请在事务中使用PreparedStatement

dbCon.setAutoCommit(false);
    var pst = dbCon.prepareStatement("insert into entity (columnID, column_1, column_2, column_3) values (?, ?, ?, ?)";
for(Object[] entityToCopy : entitiesToCopy){
   var entity = (Entity )entityToCopy[0];
   tmpValueForEntityId = lastEntryInEntityId;
   pst.setInt(1, lastEntryInEntityId);
   pst.setString(2, entity.getProperty());
   pst.setString(3, entity[1]);
   pst.setString(4, entity.getProperty2());
   pst.addBatch();
}
pst.executeBatch();
dbCon.commit();
dbCon.setAutoCommit(true);

每个?代表一列。第一个代表ID,第二个代表column_1,等等。保持每个ID的顺序。

注意:如果您使用的是1.10之前的Java,请将var更改为PreparedStatement

使用并发连接(多个线程插入数据库):

  1. 提交后关闭数据库连接(程序退出时关闭)
  2. 插入数据的方法应为synchronized
  3. 请勿使用prepareStatement(),而是将createStatement()Pattern(正则表达式)一起使用以避免SQL注入。
  4. 注意:PreparedStatement是好的,快速且安全的。

    数据库保留一个准备好的语句池,以避免每次都创建新语句。但同时,在一个线程引用现有语句之后 - > PreparedStatement,另一个线程可以使用它并且transactioin很慢(等待新实例或对现有的新引用)。同时发生这种情况很多次。

    EntityManager示例:

        var em = emf.createEntityManager();
    EntityTransaction transaction = null;
    try {
        transaction = em.getTransaction();
        transaction.begin();
    
        for(Object[] entityToCopy : entitiesToCopy){
             var entity = (Entity )entityToCopy[0];
             ...//insert here
        }
    
        tx.commit();
    } catch (RuntimeException e) {
        if (transaction != null && transaction.isActive()) {
             tx.rollback();
             e.printStackTrace();
        }
    } finally {
        em.close();
    }
    

答案 1 :(得分:0)

我每x重复执行一次查询,以使查询不会太大。这解决了我的问题。

int count = 0;
for(Object[] entityToCopy : entitiesToCopy){
   Entity entity= (Entity )entityToCopy[0];
   tmpValueForEntityId= lastEntryInEntityId ;
   queryString.append("("+ lastEntryInEntityId ++ 
      +","+entity.getProperty()+","+entity[1]+","+entity.getProperty2()+"),");

    for(Entity2 entity2 : entity.getEntity2Collection()){
        queryString2.append("("+lastEntryInEntity2Id ++ 
  +","+tmpValueForEntityId+","+entity.getProperty2()+","+entity.getProperty3()+"),");
    }
   count++;
   if(count%2000 == 0 || entitiesToCopy.size() == count){
       em.executeQuery(queryString);
       queryString = "";
       em.executeQuery(queryString2);
       queryString2 = "";
   }
}