JPA:EntityManager需要很长时间才能保存数据

问题描述 投票:1回答:3

我有一个csv数据文件,共有10万条记录。我正在迭代记录并尝试为每条记录更新5个表。以下是示例数据:

EAN Code,Site,Genric Material,Material,Sap Ean Code,Style,Color,Size,MRP,Gender,EAN Code,Season,Collection,BRAND,Color revision,Category (L5),Category (L6)
123456789,6001,000000000061000102,000000061000102001,61000102001,03/BE100,SC/TG,L/112 cm,850.00,MENS,123456789,AW12,Colors,XXXXXX,RD/TG,Tee Shirt,Graphic

每次迭代将更新的五个表如下:

  1. MasterDescription
  2. 属性
  3. AttributeValues
  4. AssociationTable

上述表格之间的关系如下:

掌握M-M属性值

掌握M-1 MatserDescription

掌握M-M属性

属性1-M AttributeValues

以下是使用批处理技术将CSV数据保存到单个会话中的5个表中的代码:

服务类

@Service
public class EanService{

@AutoWired
public EanRepository eanrepository;

// Method that saves data from CSV to DataBase
@Transactional
public void saveEANMasterData1(BufferedReader br, String userName,
        List<EanAttributes> attributes, String eanMasterName,String description) {
    int i =1;

    EanMasterDiscription eanDes = new EanMasterDiscription();
    User user = userRepository.findUserByUsername(userName);
    EanMasterDiscription deciption = null;
    eanDes.setDescription(description);
    eanDes.setMasterName(eanMasterName);
    eanDes.setDate(new Timestamp(Calendar.getInstance()
            .getTimeInMillis()));
    String line;
    try {
        List<Ean> eans = new ArrayList<Ean>();
        // iterating over each record in the CSV and saving the data into DB            
        while (((line = br.readLine()) != null)) {
             String[] cols = line.split(",");
             // Style Keeping Unit
             Ean ean = new Ean();
             for(EanAttributes attr : attributes){
                 EanAttributeValues eanAttributeValues = new EanAttributeValues();
                 if(attr.getAttrInferredType().equalsIgnoreCase("EAN")){
                         ean.setEAN(cols[attr.getAttributeOrder()]);
                 }else if(attr.getAttrInferredType().equalsIgnoreCase("Season")){
                     ean.setSeason(cols[attr.getAttributeOrder()]);
                 }else {
                     if(attr.getAttrInferredType().equalsIgnoreCase("Attribute")){
                         EanAttributes eanAttr = eanrepository.loadAttrsListByAttName(attr.getAttributeName());
                         if(eanAttr == null){
                             eanAttributeValues.setAttributeValue(cols[attr.getAttributeOrder()]);
                             eanAttributeValues.setEanAttributes(attr);
                             ean.getEanAttributeValues().add(eanAttributeValues);
                             ean.getEanAttributes().add(attr);
                             attr.getEan().add(ean);
                         }else{
                             ean.getEanAttributes().add(eanAttr);
                             eanAttr.getEan().add(ean);
                             if(eanrepository.isAttributeValueAvailable(cols[attr.getAttributeOrder()])){
                                 eanAttributeValues.setAttributeValue(cols[attr.getAttributeOrder()]);
                                 eanAttributeValues.setEanAttributes(eanAttr);
                                 ean.getEanAttributeValues().add(eanAttributeValues);
                             }else{
                                 EanAttributeValues values = eanrepository.loadDataByAttrValue(cols[attr.getAttributeOrder()]);
                                 ean.getEanAttributeValues().add(values);
                                 values.getEan().add(ean);
                             }
                         }
                         eanAttributeValues.getEan().add(ean);
                     }
                 }
             }
             if(!eanrepository.isEanMasterNameAvailable(eanMasterName)){
                EanMasterDiscription eanMasterDes = eanrepository.loadDataByMasterName(eanMasterName);
                 ean.setEanMasterDesciption(eanMasterDes);
             }else{
                 ean.setEanMasterDesciption(eanDes);
             }
             ean.setUser(user);
             if(eanrepository.isEanWithSeasonAvailable(ean.getEAN(),ean.getSeason())){
                     // Persisting Ean; I think there is some problem with this method
                     eanrepository.saveEanData(ean,i);
             }else{
                 System.out.println("************ EAN ALREADY EXIST ******************** ");
             }

             i++;
        }
    } catch (NumberFormatException | IOException e) {
        e.printStackTrace();
    }       
    }
}

存储库类

@Repository
public class EanRepository{

@PersistanceContext
EntityManager em;

public void saveEanData(Ean ean , int recordNum){
    em.merge(ean);
    if(recordNum % 50 == 0){
        em.flush();
        em.clear();
        // em.getEntityManagerFactory().getCache().evictAll();
    }
}

}

但这需要花费太多时间(近10小时)才能完成所有10万条记录的保存。我们怎样才能减少时间和缺少的东西?

java spring hibernate jpa orm
3个回答
1
投票

我的批处理应用程序遇到了同样的问题,我们采用了两种技术,大大加快了导入数据的过程:

1)多线程 - 您必须利用多个线程处理文件数据并进行保存。

我们这样做的方法是先读取文件中的所有数据并将其打包成一组POJO对象。

然后根据我们可以创建的可能线程的数量,我们将均匀地拆分Set,并为线程提供一定范围的数据。

然后每个集合将被并行处理。

我不打算详细说明,因为这超出了这个问题的界限。我只能给出一个提示,你应该尝试利用它提供的java.util.concurrent和功能。

2)批量保存 - 我们做的第二个改进是利用hibernate的批量保存功能(你已经添加了Hibernate标签,所以我假设这是你的底层持久性提供者):

您可以尝试利用批量插入功能。

您可以定义hibernate属性以启用此功能:

<property name="jdbc.batch_size">250</property>

使用此批处理设置,您应该具有以下输出:

insert into Table(id , name) values (1, 'na1') , (2, 'na2') ,(3, 'na3')..

代替

insert into Table(id , name) values (1, 'na1');
insert into Table(id , name) values (2, 'na2');
insert into Table(id , name) values (3, 'na3');

3)刷新计数 - 在刷新数据库之前,你的计数设置为50 ..现在启用批量插入可能你可以将它提升到几个houndread ...尝试用这个数字来试验找到最佳点。


0
投票

加载数据需要花费时间(因此只能从代码中获取)可能有很多原因 - 您应该优化较小的数据块。

所以我只是根据自己的经验盲目拍摄:

  • 如果可能的话,使用persist()而不是merge(),merge()会产生一个select并进行一些值复制
  • 加载大量数据时 - 不要使用事务。我看到你只在每50条记录上刷新一次,但是交易开销可能仍然非常昂贵
  • 如上一篇文章所述,设置批量插入属性(取决于使用的JPA)

0
投票

由于您有一个包含大量条目的CSV文件,我会考虑另一种选择:特定于数据库的独立工具。

例如,对于MySQL,你有these tools以及load data infile;对于PostgreSQL,可以使用this syntaxfrom command line;对于Oracle,sqlldr。这些更适合这些事情。

© www.soinside.com 2019 - 2024. All rights reserved.