使用springbootjpa将数百万行从csv保存到oracledb

3qpi33ja  于 2021-07-13  发布在  Java
关注(0)|答案(3)|浏览(353)

另一个应用程序定期转储包含超过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();
    }
}

此功能正常工作,但需要大量时间来处理。我怎样才能提高效率并加快这个过程?

xam8gpfp

xam8gpfp1#

你应该对你的代码做几件事。 String.split ,虽然方便,但是相对较慢,因为它每次都会重新编译regexp。更好地使用 Pattern 以及 split 方法来减少开销。
使用适当的jpa批处理策略,如本博客所述。
首先在spring中启用批处理 application.properties . 我们将使用50的批量大小(您需要试验什么是适合您的案例的合适批量大小)。

spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true

然后直接将实体保存到数据库中,每50个条目做一次 flush 以及 clear . 这将刷新数据库的状态并清除一级缓存(这将防止过多的脏检查)。
上面的代码应该是这样的。

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(data[4]); 相反。它节省了时间 Long 创建和拆箱。对于只有10行的情况,这可能不是问题,但是对于数百万行的情况,这些毫秒将相加。
使用 ddMMMyy 作为 DateTimeFormatter 然后取下 substring 代码中的部分。就这么做吧 LocalDate.parse(date[1].toUpperCase(), formatted) 以实现相同的结果,而无需额外增加5%的开销 String 物体。

int count = 0;
Instant from = Instant.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("ddMMMyy");
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]);
            LocalDate localDate = LocalDate.parse(data[1].toUpperCase(), formatter);
            customer.setEventDate(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
            customer.setaPartyNumber(data[2]);
            customer.setbPartyNumber(data[3]);
            customer.setVolume(Long.parseLong(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();
}
1hdlvixo

1hdlvixo2#

您可以使用spring数据批插入。此链接说明了如何执行:https://www.baeldung.com/spring-data-jpa-batch-inserts

vfh0ocws

vfh0ocws3#

您可以尝试使用Java8流和SpringDataJPA来流式处理mysql结果。下面的链接对此进行了详细解释
http://knes1.github.io/blog/2015/2015-10-19-streaming-mysql-results-using-java8-streams-and-spring-data.html

相关问题