Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用Spring boot JPA将数百万行从CSV保存到Oracle DB_Spring_Spring Boot_Jpa_Spring Data Jpa - Fatal编程技术网

使用Spring boot JPA将数百万行从CSV保存到Oracle DB

使用Spring boot JPA将数百万行从CSV保存到Oracle DB,spring,spring-boot,jpa,spring-data-jpa,Spring,Spring Boot,Jpa,Spring Data Jpa,另一个应用程序定期转储包含超过7-8百万行的CSV。我有一个cron作业,它从CSV加载数据,并将数据保存到oracle数据库中。这是我的代码片段 String line = ""; int count = 0; LocalDate localDateTime; Instant from = Instant.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(&quo

另一个应用程序定期转储包含超过7-8百万行的CSV。我有一个cron作业,它从CSV加载数据,并将数据保存到oracle数据库中。这是我的代码片段

String line = "";
    int count = 0;
    LocalDate localDateTime;
    Instant from = Instant.now();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yy");
    List<ItemizedBill> itemizedBills = new ArrayList<>();
    try {
        BufferedReader br=new BufferedReader(new FileReader("/u01/CDR_20210325.csv"));
        while((line=br.readLine())!=null) {
            if (count >= 1) {
                String [] data= line.split("\\|");
                ItemizedBill customer = new ItemizedBill();
                customer.setEventType(data[0]);
                String date = data[1].substring(0,2);
                String month = data[1].substring(3,6);
                String year = data[1].substring(7,9);
                month = WordUtils.capitalizeFully(month);
                String modifiedDate = date + "-" + month + "-" + year;
                localDateTime = LocalDate.parse(modifiedDate, formatter);
                customer.setEventDate(localDateTime.atStartOfDay(ZoneId.systemDefault()).toInstant());
                customer.setaPartyNumber(data[2]);
                customer.setbPartyNumber(data[3]);
                customer.setVolume(Long.valueOf(data[4]));
                customer.setMode(data[5]);
                if(data[6].contains("0")) { customer.setFnfNum("Other"); }
                else{ customer.setFnfNum("FNF Number"); }
                itemizedBills.add(customer);
            }
            count++;
        }
        itemizedBillRepository.saveAll(itemizedBills);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
字符串行=”;
整数计数=0;
LocalDate localDateTime;
瞬间开始=瞬间。现在();
DateTimeFormatter formatter=模式的DateTimeFormatter.of(“dd-MMM-yy”);
List itemizedBills=new ArrayList();
试一试{
BufferedReader br=新的BufferedReader(新文件读取器(“/u01/CDR_20210325.csv”);
而((line=br.readLine())!=null){
如果(计数>=1){
String[]data=line.split(“\\\\”);
ItemizedBill客户=新的ItemizedBill();
customer.setEventType(数据[0]);
字符串日期=数据[1]。子字符串(0,2);
字符串月份=数据[1]。子字符串(3,6);
字符串年份=数据[1]。子字符串(7,9);
月=大写的字数(月);
字符串modifiedDate=日期+“-”+月份+“-”+年份;
localDateTime=LocalDate.parse(modifiedDate,格式化程序);
customer.setEventDate(localDateTime.atStartOfDay(ZoneId.systemDefault()).toInstant());
customer.setaPartyNumber(数据[2]);
customer.setPartyNumber(数据[3]);
customer.setVolume(Long.valueOf(数据[4]);
customer.setMode(数据[5]);
if(数据[6]。包含(“0”){customer.setfnum(“其他”);}
else{customer.setfnum(“FNF编号”);}
itemizedBills.add(客户);
}
计数++;
}
itemizedBillRepository.saveAll(itemizedBills);
}捕获(IOE异常){
e、 printStackTrace();
}
}

此功能可以正常工作,但需要花费大量时间来处理。如何提高效率并加快此过程?

您可以使用spring data batch insert。此链接说明了如何操作:

您可以尝试使用Java 8 Streams和spring data JPA对MySQL结果进行流式处理。下面的链接对此进行了详细说明


您应该对代码做几件事

  • String.split
    ,虽然方便,但速度相对较慢,因为它每次都会重新编译regexp。最好使用
    Pattern
    split
    方法来减少开销

  • 使用适当的JPA批处理策略,如中所述

  • 首先在Spring
    应用程序中启用批处理。我们将使用50的批量大小(您需要试验适合您的案例的合适批量大小)

    spring.jpa.properties.hibernate.jdbc.batch\u size=50
    spring.jpa.properties.hibernate.order\u inserts=true
    spring.jpa.properties.hibernate.order\u updates=true
    
    然后直接将实体保存到数据库中,每50项执行一次
    刷新
    清除
    。这将刷新数据库的状态并清除一级缓存(这将防止过多的脏检查)

    有了以上这些,您的代码应该是这样的

    int count = 0;
    Instant from = Instant.now();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yy");
    Pattern splitter = Pattern.compile("\\|");
    try {
        BufferedReader br=new BufferedReader(new FileReader("/u01/CDR_20210325.csv"));
        while((line=br.readLine())!=null) {
            if (count >= 1) {
                String [] data= splitter.split(Line);
                ItemizedBill customer = new ItemizedBill();
                customer.setEventType(data[0]);
                String date = data[1].substring(0,2);
                String month = data[1].substring(3,6);
                String year = data[1].substring(7,9);
                month = WordUtils.capitalizeFully(month);
                String modifiedDate = date + "-" + month + "-" + year;
                LocalDate localDate = LocalDate.parse(modifiedDate, formatter);
                customer.setEventDate(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
                customer.setaPartyNumber(data[2]);
                customer.setbPartyNumber(data[3]);
                customer.setVolume(Long.valueOf(data[4]));
                customer.setMode(data[5]);
                if(data[6].contains("0")) { 
                  customer.setFnfNum("Other"); 
                } else { 
                  customer.setFnfNum("FNF Number"); 
                }
                itemizedBillRepository.save(customer);
            }
            count++;
            if ( (count % 50) == 0) {
              this.entityManager.flush(); // sync with database
              this.entityManager.clear(); // clear 1st level cache
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    
    您还可以进行2项其他优化:

  • 如果
    volume
    属性是
    long
    而不是
    long
    ,则应使用
    long.parseLong(数据[4])取而代之。它保存
    Long
    创建和取消装箱。如果只有10行,这可能不是问题,但如果有数百万行,那么这些毫秒的总和就会增加

  • 使用
    ddMMMyy
    作为
    DateTimeFormatter
    并删除代码中的
    子字符串
    部分。只需执行
    LocalDate.parse(date[1].toUpperCase(),格式化)
    即可获得相同的结果,而无需增加5个
    String
    对象的额外开销

  • int count=0;
    瞬间开始=瞬间。现在();
    DateTimeFormatter formatter=模式的DateTimeFormatter.of(“ddMMMyy”);
    patternspilter=Pattern.compile(“\\\\”);
    试一试{
    BufferedReader br=新的BufferedReader(新文件读取器(“/u01/CDR_20210325.csv”);
    而((line=br.readLine())!=null){
    如果(计数>=1){
    String[]data=splitter.split(行);
    ItemizedBill客户=新的ItemizedBill();
    customer.setEventType(数据[0]);
    LocalDate LocalDate=LocalDate.parse(数据[1].toUpperCase(),格式化程序);
    customer.setEventDate(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
    customer.setaPartyNumber(数据[2]);
    customer.setPartyNumber(数据[3]);
    customer.setVolume(Long.parseLong(数据[4]);
    customer.setMode(数据[5]);
    如果(数据[6]。包含(“0”){
    客户。SETFNUM(“其他”);
    }否则{
    客户。设置FNUM(“FNF编号”);
    }
    itemizedBillRepository.save(客户);
    }
    计数++;
    如果((计数%50)=0){
    this.entityManager.flush();//与数据库同步
    this.entityManager.clear();//清除一级缓存
    }
    }
    }捕获(IOE异常){
    e、 printStackTrace();
    }
    
    如果您想耗尽内存并出现性能问题,这是一种解决方法。不要。不要把东西放在列表中,然后全部保存。而是直接使用
    save
    保存单个项目,并在每个x个项目(可能50或100个)之后执行
    entitymanager.flush
    entitymanager.clear
    。性能将得到提高,有关更多提示,请参阅另一件事
    String。相对而言,split
    速度较慢。相反,使用
    模式
    ,编译并重用它。这节省了每次需要创建regexp类时的开销。节省内存,因此gc循环。问题是如何解析文本并将其插入数据库。这不是从数据库中读取大量结果。非常感谢您的建议。这意味着很多。