我正在使用此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。
答案 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
使用并发连接(多个线程插入数据库):
synchronized
prepareStatement()
,而是将createStatement()
与Pattern
(正则表达式)一起使用以避免SQL注入。注意: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 = "";
}
}