我有一个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
每次迭代将更新的五个表如下:
上述表格之间的关系如下:
掌握M-M属性值
Master 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万条记录的保存。我们怎样才能减少时间和缺少的东西?
答案 0 :(得分: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 ..尝试试验这个数字来找到最佳点。
答案 1 :(得分:0)
加载数据需要花费时间(因此只能从代码中获取)可能有很多原因,并且 - 您应该优化较小的数据块。
所以我只是根据自己的经验盲目拍摄:
如果可能,使用persist()而不是merge(),merge()会产生一个select并进行一些值复制
加载大量数据时 - 请勿使用交易。我看到你只在每50条记录上刷新一次,但是交易开销可能仍然非常昂贵
如上一篇文章所述,设置批量插入属性(取决于使用的JPA)
答案 2 :(得分:0)
由于你有一个包含大量条目的CSV文件,我考虑另一种选择:特定于数据库的独立工具。
例如,对于MySQL,您有these tools以及load data infile;对于PostgreSQL,this syntax可以使用from command line;对于Oracle,sqlldr。那些更适合这类事情。