JPA:EntityManager保存数据的时间太长。

12 浏览
0 Comments

JPA:EntityManager保存数据的时间太长。

我有一个包含10万条记录的数据csv文件。我正在遍历这些记录,尝试为每条记录更新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. Master
  2. MasterDescription
  3. Attributes
  4. AttributeValues
  5. AssociationTable

上述表之间的关系如下:

Master M-M AttributeValues

Master M-1 MatserDescription

Master M-M Attributes

Attributes 1-M AttributeValues

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

服务类

@Service
public class EanService{
@AutoWired
public EanRepository eanrepository;
// 将数据从CSV保存到数据库的方法
@Transactional
public void saveEANMasterData1(BufferedReader br, String userName,
        List 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 eans = new ArrayList();
        // 迭代CSV中的每条记录并将数据保存到数据库中            
        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())){
                     // 持久化Ean;我认为这个方法有些问题
                     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万条记录。我们如何缩短时间并解决问题?

0
0 Comments

问题出现的原因:

在这个web应用程序中,用户需要上传数据,然后根据上传的数据推断出模式,用户选择需要保存的列,然后将这些列保存到数据库中。这个过程可能会导致EntityManager保存数据的时间过长。

解决方法:

可以将文件先保存到磁盘上,然后调用数据库特定的工具来处理这个文件,这些工具更适合处理这种类型的数据。这样做还更加用户友好,因为用户不需要等待数据的持久化过程。

代码示例:

// 保存文件到磁盘上
String filePath = "path/to/save/file.csv";
File file = new File(filePath);
// 将上传的文件保存到磁盘上
// ...
// 调用数据库特定的工具来处理文件
String importCommand = "database-specific-import-command " + filePath;
Runtime.getRuntime().exec(importCommand);
// 删除保存在磁盘上的文件
file.delete();

通过将数据保存到磁盘上,并使用数据库特定的工具来处理文件,可以更快地将数据导入到数据库中,并提高用户体验。

0
0 Comments

JPA: EntityManager保存数据的时间太长

在加载数据时可能会有很多原因导致花费时间(所以只从代码上来说)- 你应该优化为较小的数据块。

所以,我只能从我的经验中进行盲目尝试:

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

JPA : EntityManager保存数据时间过长的原因和解决方法

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

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

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

然后根据可能创建的线程数量,将集合平均分割,并将一定范围的数据提供给线程。

然后每个集合都会并行处理。

我不会详细介绍这个过程,因为它超出了这个问题的范围。我可以给出的一个提示是,尝试利用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) Flush计数 - 你将计数设置为在刷新到数据库之前的50次...现在在启用批量插入的情况下,可以将其提高到几百次...尝试使用这个数字进行实验,找到最佳值。

0