JPA如何从Feeder、Monthlyfeederdetails多表提取数据插入Report表
实现方案
方式一:使用原生SQL @Query 直接实现批量插入(性能最优,和你熟悉的原生SQL逻辑完全一致)
这种方式直接在数据库层面完成查询+插入操作,不需要把中间数据拉到Java内存,适合所有场景,尤其是数据量较大的情况。
首先创建ReportRepository接口,继承JpaRepository,直接写原生插入语句即可:
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.transaction.annotation.Transactional; public interface ReportRepository extends CrudRepository<Report, Integer> { @Modifying // 标识这是写操作 @Transactional // 增删改必须加事务 @Query(value = "INSERT INTO `report of caifi and caidi` (feeder_name, feeder_type, no_of_afcon, duration_of_intr, no_of_afconXduration_of_intr) " + "SELECT f.feeder_name, f.feeder_type, m.no_of_afcon, m.duration_of_intr, m.duration_of_intr * m.no_of_afcon " + "FROM feeder f INNER JOIN monthlyfeederdetails m ON f.feeder_id = m.feeder_id", nativeQuery = true) void batchGenerateReport(); // 如果需要加过滤条件比如按月份、年份生成报表,直接加参数即可,示例: @Modifying @Transactional @Query(value = "INSERT INTO `report of caifi and caidi` (feeder_name, feeder_type, no_of_afcon, duration_of_intr, no_of_afconXduration_of_intr) " + "SELECT f.feeder_name, f.feeder_type, m.no_of_afcon, m.duration_of_intr, m.duration_of_intr * m.no_of_afcon " + "FROM feeder f INNER JOIN monthlyfeederdetails m ON f.feeder_id = m.feeder_id WHERE m.month = ?1 AND m.year = ?2", nativeQuery = true) void batchGenerateReportByMonth(int month, int year); }
需要调用的时候直接注入ReportRepository,执行对应的方法即可。
方式二:先查询关联数据再组装存储(适合数据量小、需要额外业务逻辑处理的场景)
如果需要在插入前对数据做额外校验、修改,就用这种方式:
- 先关联查询
Feeder和Monthlyfeederdetails的对应字段 - 遍历结果组装
Report实体,计算乘积字段 - 批量保存所有Report实体
示例Service层代码:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.List; @Service public class ReportService { @Autowired private ReportRepository reportRepository; // 自定义查询获取两个表关联的所需字段,可用Object数组接收,也可自定义DTO投影接收 @Autowired private FeederRepository feederRepository; @Transactional public void generateReport() { // FeederRepository中自定义关联查询方法,返回的数组顺序对应[feeder_name,feeder_type,no_of_afcon,duration_of_intr] List<Object[]> rawData = feederRepository.getReportRawData(); List<Report> reportList = new ArrayList<>(); for (Object[] row : rawData) { Report report = new Report(); report.setFeeder_name((String) row[0]); report.setFeeder_type((Integer) row[1]); report.setNo_of_afcon((Integer) row[2]); report.setDuration_of_intr((Integer) row[3]); // 计算乘积字段 report.setNo_of_afconXduration_of_intr(report.getNo_of_afcon() * report.getDuration_of_intr()); reportList.add(report); } // 批量保存 reportRepository.saveAll(reportList); } }
注意事项
- 优先选择方式一实现,性能比方式二高很多,也不需要额外写关联查询逻辑
- 务必给
Report实体类补全所有字段的getter、setter方法,否则会出现字段映射异常 - 如果两个表的关联逻辑有特殊要求,直接修改SQL中JOIN的条件即可
内容的提问来源于stack exchange,提问作者Kangkan Talukdar
相关产品推荐
相关产品推荐

