跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Spring Boot 数据导入导出与报表生成实战

演示了如何在 Spring Boot 项目中实现数据导入导出与报表生成功能。通过集成 Apache POI 库,实现了 Excel 文件的读取与写入,支持批量导入产品数据及导出查询结果。同时结合 JasperReports 引擎,展示了如何加载模板生成 PDF 格式的销售报表。代码涵盖了依赖配置、实体定义、Service 层流处理及 Controller 接口暴露,适用于企业级数据迁移、备份及统计场景。

念念不忘发布于 2026/3/15更新于 2026/10/2287 浏览
Spring Boot 数据导入导出与报表生成实战

Spring Boot 数据导入导出与报表生成

架构图

在企业级开发中,数据的批量处理与可视化展示是高频需求。无论是将 Excel 表格导入数据库进行初始化,还是从系统导出报表供管理层查看,都需要稳定高效的解决方案。本文将结合 Apache POI 和 JasperReports,演示如何在 Spring Boot 项目中实现 Excel 的导入导出以及 PDF 报表的生成。

一、Excel 数据导入导出 (Apache POI)

1. 依赖配置

首先需要在 pom.xml 中添加 Web、JPA、H2 测试库以及 Apache POI 的核心依赖。这里我们使用 H2 内存数据库以便快速验证逻辑。

<dependencies>
    <!-- Web 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Data JPA 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!-- H2 数据库依赖 -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</>
        runtime
    
    
    
        org.apache.poi
        poi
        4.1.2
    
    
        org.apache.poi
        poi-ooxml
        4.1.2
    
    
    
        org.springframework.boot
        spring-boot-starter-test
        test
    

artifactId
<scope>
</scope>
</dependency>
<!-- Apache POI 依赖 -->
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
<version>
</version>
</dependency>
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
<version>
</version>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
<scope>
</scope>
</dependency>
</dependencies>

2. 基础配置与实体类

在 application.properties 中开启 H2 控制台并配置 JPA 行为:

server.port=8080
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

定义产品实体类,注意字段映射与构造器:

import javax.persistence.*;

@Entity
@Table(name = "product")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String productId;
    private String productName;
    private double price;
    private int sales;

    public Product() {}

    public Product(String productId, String productName, double price, int sales) {
        this.productId = productId;
        this.productName = productName;
        this.price = price;
        this.sales = sales;
    }

    // Getter and Setter methods omitted for brevity
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getProductId() { return productId; }
    public void setProductId(String productId) { this.productId = productId; }
    public String getProductName() { return productName; }
    public void setProductName(String productName) { this.productName = productName; }
    public double getPrice() { return price; }
    public void setPrice(double price) { this.price = price; }
    public int getSales() { return sales; }
    public void setSales(int sales) { this.sales = sales; }
}

配合标准的 Repository 接口即可开始业务层开发。

3. 核心 Service 实现

Service 层负责文件流的处理。导入时解析 Excel 行数据,导出时将数据库记录写入 Workbook。

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

@Service
public class ProductService {
    @Autowired
    private ProductRepository productRepository;

    @Transactional
    public void importProducts(MultipartFile file) throws IOException {
        List<Product> products = new ArrayList<>();
        Workbook workbook = new XSSFWorkbook(file.getInputStream());
        Sheet sheet = workbook.getSheetAt(0);
        Iterator<Row> iterator = sheet.iterator();
        
        if (iterator.hasNext()) {
            iterator.next(); // 跳过标题行
        }
        
        while (iterator.hasNext()) {
            Row currentRow = iterator.next();
            Product product = new Product();
            product.setProductId(currentRow.getCell(0).getStringCellValue());
            product.setProductName(currentRow.getCell(1).getStringCellValue());
            product.setPrice(currentRow.getCell(2).getNumericCellValue());
            product.setSales((int) currentRow.getCell(3).getNumericCellValue());
            products.add(product);
        }
        productRepository.saveAll(products);
        workbook.close();
    }

    @Transactional(readOnly = true)
    public byte[] exportProducts() throws IOException {
        List<Product> products = productRepository.findAll();
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("Products");
        
        // 创建表头
        Row headerRow = sheet.createRow(0);
        Cell headerCell0 = headerRow.createCell(0); headerCell0.setCellValue("Product ID");
        Cell headerCell1 = headerRow.createCell(1); headerCell1.setCellValue("Product Name");
        Cell headerCell2 = headerRow.createCell(2); headerCell2.setCellValue("Price");
        Cell headerCell3 = headerRow.createCell(3); headerCell3.setCellValue("Sales");

        int rowNum = 1;
        for (Product product : products) {
            Row row = sheet.createRow(rowNum++);
            row.createCell(0).setCellValue(product.getProductId());
            row.createCell(1).setCellValue(product.getProductName());
            row.createCell(2).setCellValue(product.getPrice());
            row.createCell(3).setCellValue(product.getSales());
        }
        
        // 自动调整列宽
        for (int i = 0; i < 4; i++) {
            sheet.autoSizeColumn(i);
        }
        
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        workbook.write(outputStream);
        workbook.close();
        return outputStream.toByteArray();
    }

    @Transactional
    public Product addProduct(Product product) {
        return productRepository.save(product);
    }

    @Transactional(readOnly = true)
    public List<Product> getAllProducts() {
        return productRepository.findAll();
    }
}

4. 控制器暴露接口

Controller 负责接收文件上传请求或返回下载流。注意设置正确的 Content-Type 和响应头。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;

@RestController
@RequestMapping("/api/products")
public class ProductController {
    @Autowired
    private ProductService productService;

    @PostMapping("/import")
    public ResponseEntity<String> importProducts(@RequestParam("file") MultipartFile file) {
        try {
            productService.importProducts(file);
            return ResponseEntity.ok("数据导入成功");
        } catch (IOException e) {
            e.printStackTrace();
            return ResponseEntity.status(500).body("数据导入失败:" + e.getMessage());
        }
    }

    @GetMapping("/export")
    public ResponseEntity<byte[]> exportProducts() {
        try {
            byte[] bytes = productService.exportProducts();
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
            headers.setContentDispositionFormData("attachment", "products.xlsx");
            headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
            return ResponseEntity.ok().headers(headers).body(bytes);
        } catch (IOException e) {
            e.printStackTrace();
            return ResponseEntity.status(500).body(null);
        }
    }

    @PostMapping("/")
    public Product addProduct(@RequestBody Product product) {
        return productService.addProduct(product);
    }

    @GetMapping("/")
    public List<Product> getAllProducts() {
        return productService.getAllProducts();
    }
}

二、PDF 报表生成 (JasperReports)

对于需要打印或正式归档的场景,PDF 格式更为合适。JasperReports 是 Java 生态中成熟的报表引擎。

1. 添加依赖

<dependency>
    <groupId>net.sf.jasperreports</groupId>
    <artifactId>jasperreports</artifactId>
    <version>6.17.0</version>
</dependency>
<dependency>
    <groupId>net.sf.jasperreports</groupId>
    <artifactId>jasperreports-fonts</artifactId>
    <version>6.17.0</version>
</dependency>

2. 报表服务实现

需要先准备好编译后的 .jasper 模板文件(通常由 .jrxml 编译而来),放在 resources/reports/ 目录下。

import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class ReportService {
    @Autowired
    private ProductRepository productRepository;

    @Transactional(readOnly = true)
    public byte[] generateProductReport() throws JRException {
        List<Product> products = productRepository.findAll();
        
        // 加载预编译的模板
        InputStream templateStream = getClass().getResourceAsStream("/reports/product-report.jasper");
        JasperReport jasperReport = (JasperReport) JRLoader.loadObject(templateStream);

        // 设置参数
        Map<String, Object> parameters = new HashMap<>();
        parameters.put("Title", "产品销售报表");

        // 设置数据源
        JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(products);

        // 填充报表
        JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, dataSource);

        // 导出为 PDF
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        JasperExportManager.exportReportToPdfStream(jasperPrint, outputStream);
        return outputStream.toByteArray();
    }
}

3. 集成到 Controller

只需在原有的 Controller 中注入 ReportService 并新增一个 GET 接口即可。

@GetMapping("/report")
public ResponseEntity<byte[]> generateProductReport() {
    try {
        byte[] bytes = reportService.generateProductReport();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("application/pdf"));
        headers.setContentDispositionFormData("attachment", "product-report.pdf");
        headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
        return ResponseEntity.ok().headers(headers).body(bytes);
    } catch (JRException e) {
        e.printStackTrace();
        return ResponseEntity.status(500).body(null);
    }
}

三、应用启动与测试

为了让应用启动时自动初始化一些测试数据,建议实现 CommandLineRunner 接口,而不是在 main 方法中直接操作 Bean。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ProductApplication implements CommandLineRunner {
    @Autowired
    private ProductService productService;

    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        productService.addProduct(new Product("P001", "手机", 1000.0, 100));
        productService.addProduct(new Product("P002", "电脑", 5000.0, 50));
        productService.addProduct(new Product("P003", "电视", 3000.0, 80));
        productService.addProduct(new Product("P004", "手表", 500.0, 200));
        productService.addProduct(new Product("P005", "耳机", 300.0, 150));
    }
}

启动后,访问 /api/products/export 可下载 Excel,访问 /api/products/report 可下载 PDF。实际开发中,根据业务复杂度,还可以引入 EasyExcel 简化 POI 的使用,或使用 Freemarker 定制 HTML 报表。

目录

  1. Spring Boot 数据导入导出与报表生成
  2. 一、Excel 数据导入导出 (Apache POI)
  3. 1. 依赖配置
  4. 2. 基础配置与实体类
  5. 3. 核心 Service 实现
  6. 4. 控制器暴露接口
  7. 二、PDF 报表生成 (JasperReports)
  8. 1. 添加依赖
  9. 2. 报表服务实现
  10. 3. 集成到 Controller
  11. 三、应用启动与测试

更多推荐文章

查看全部
  • 次模函数与机器学习中的离散优化
  • Spring 整合 Apollo 配置中心
  • HDFS 与 YARN 框架组件职责及对比
  • CentOS 系统下 Python 环境搭建与生产部署实战指南
  • SRC 漏洞挖掘实战指南:信息收集、逻辑漏洞与绕过技巧
  • 大模型基本概念解析与分类发展
  • 二分算法实战:A-B 数对与高考志愿匹配
  • 数据结构与算法:顺序表详解
  • 基于 FPGA 的北斗导航自适应抗干扰算法设计与实现
  • SQL Server 安装与基础使用指南
  • Trae 编辑器免费版体验:i18n 任务排队及死循环问题
  • SpringBoot 结合 Redis+Caffeine 多级缓存实践解析
  • SpringBoot 整合 Langchain4j RAG 技术深度使用解析
  • Java SPI 机制深度解析:从源码到实战最佳实践
  • Python 能做什么?常见应用场景与学习指南
  • 基于 SpringBoot 的体育竞技门票售卖系统设计与实现
  • .NET 的 WebApi 项目必要可配置项都有哪些?
  • 二分查找算法原理及常见变种解析
  • 基于 ECharts 与 Three.js 的碳排放可视化大屏实现
  • OpenClaw 开源 AI 项目汇总:部署工具、技能库与多平台接入

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online