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

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

Spring Boot 结合 Apache POI 和 JasperReports 实现数据导入导出与报表生成。通过配置 Maven 依赖集成 POI 处理 Excel 读写,利用 JasperReports 生成 PDF 报表。文章涵盖实体类设计、Service 层业务逻辑及 Controller 接口封装,演示了从文件上传解析到数据库存储再到结果导出的完整流程。实际应用中可根据需求选择 CSV、Excel 或 PDF 格式,确保数据传输的准确性与效率。

PgDevote发布于 2026/3/29更新于 2026/9/1061 浏览
Spring Boot 数据导入导出与报表生成实战

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

在业务开发中,数据的导入导出和报表生成是高频需求。无论是批量处理 Excel 数据,还是生成 PDF 统计报表,Spring Boot 都能提供高效的解决方案。本文将结合 Apache POI 和 JasperReports 两个主流库,演示如何在 Spring Boot 项目中实现这些功能。

核心概念与格式选择

数据导入导出本质上是系统间的数据迁移、备份或共享。常见的格式包括 CSV、Excel、JSON 和 XML。对于结构化较强的表格数据,Excel(.xlsx)是最常用的交互格式;而对于需要固定版式打印的报表,PDF 则是更好的选择。

集成这些功能通常具备易用性、高效性和可靠性的特点。在 Spring Boot 中,我们主要通过依赖注入和 RESTful 接口来封装这些逻辑。

使用 Apache POI 处理 Excel

Apache POI 是操作 Office 文档的经典库。在 Spring Boot 中集成它,主要涉及 Maven 依赖配置、实体类映射以及 Service 层的读写逻辑。

1. 依赖配置

首先需要在 pom.xml 中添加 Web、JPA、H2 数据库以及 POI 相关依赖:

<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</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- Apache POI 依赖 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>4.1.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>4.1.2</version>
    </dependency>
    <!-- 测试依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

2. 实体类设计

定义一个 Product 实体,用于映射数据库表结构。注意字段类型要与 Excel 单元格类型对应。

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 和 Setter 方法省略,实际开发中建议使用 Lombok 简化
    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; }
}

3. Service 层实现

Service 层负责核心的文件解析与写入逻辑。读取 Excel 时,需跳过标题行并逐行解析;导出时,则遍历集合填充单元格。

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 层封装

Controller 负责接收请求并返回响应。导出时需要设置正确的 Content-Type 和 Content-Disposition 头,确保浏览器能下载文件。

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();
    }
}

使用 JasperReports 生成 PDF 报表

当需要生成固定格式的统计报表时,JasperReports 是更专业的选择。它支持复杂的布局设计和多种输出格式(如 PDF)。

1. 依赖与模板

添加 JasperReports 依赖后,你需要准备一个 .jrxml 模板文件,编译为 .jasper 文件放在 resources 目录下。

<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. Service 层实现

加载模板、设置参数和数据源,然后填充生成报表。

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();
        
        // 加载 Jasper 模板
        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 调用

只需新增一个接口调用 ReportService 即可。

@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");
        return ResponseEntity.ok().headers(headers).body(bytes);
    } catch (JRException e) {
        e.printStackTrace();
        return ResponseEntity.status(500).body(null);
    }
}

总结

通过上述示例,我们可以看到 Spring Boot 在处理数据导入导出和报表生成方面的灵活性。对于 Excel 场景,Apache POI 提供了底层的 API 控制;对于复杂报表,JasperReports 则提供了强大的模板引擎能力。在实际开发中,根据业务需求选择合适的工具,并注意异常处理和资源关闭,就能构建出稳定可靠的数据服务模块。

目录

  1. Spring Boot 数据导入导出与报表生成
  2. 核心概念与格式选择
  3. 使用 Apache POI 处理 Excel
  4. 1. 依赖配置
  5. 2. 实体类设计
  6. 3. Service 层实现
  7. 4. Controller 层封装
  8. 使用 JasperReports 生成 PDF 报表
  9. 1. 依赖与模板
  10. 2. Service 层实现
  11. 3. Controller 调用
  12. 总结

更多推荐文章

查看全部
  • SpringBoot 源码解析:AnnotationConfigServletWebServerApplicationContext 构造流程
  • Obsidian Copilot API 密钥配置指南:OpenRouter、Gemini、OpenAI
  • Vue 核心语法与原理实战指南
  • Java 基础:集合与异常处理的生动比喻解析
  • 微软 Edge Webview2 v144 升级导致 SAP GUI 白屏故障及解决方案
  • WebView 并发初始化竞争风险分析
  • GitHub Copilot Agent 模式使用指南与实战经验
  • Java 线程状态详解及生命周期转换
  • VSCode 创建 Python 项目及 PyCharm 项目迁移指南
  • 使用大模型定制专属 AI 应用指南与场景分析
  • Anaconda 与 Python 环境安装配置指南
  • eBay 商品数据采集实战:Python 接入 IPIDEA 网页抓取 API
  • Python 核心数据结构:集合(Set)详解
  • 基于RLlib的MAPPO算法解决simple_spread多智能体合作任务
  • OpenClaw 大龙虾机器人安装与配置指南
  • DeepSeek R1 7B 模型在 RK3588 开发板上的 RKLLM 转换与 Web 部署
  • GitHub 教育认证通过后如何领取 Copilot Pro
  • Tomcat 安装与配置指南
  • Ubuntu 24.04 GPU 服务器测试系统盘制作
  • 基于 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