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

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

探讨了在 Spring Boot 项目中实现数据导入导出及报表生成的技术方案。通过集成 Apache POI 处理 Excel 文件读写,结合 JasperReports 生成 PDF 报表,解决了企业级应用中常见的数据交互需求。文章涵盖了依赖配置、实体映射、业务逻辑实现及控制器接口设计,提供了从代码构建到实际场景落地的完整示例,帮助开发者高效完成数据迁移、备份及可视化报表功能。

BackendPro发布于 2026/3/22更新于 2026/10/884 浏览
Spring Boot 数据导入导出与报表生成实战

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

技术架构图

在企业级开发中,数据的交互往往比业务逻辑本身更繁琐。无论是从 Excel 批量导入用户信息,还是导出销售报表供管理层查看,都是高频需求。Spring Boot 生态提供了丰富的工具来简化这些工作,核心在于选择合适的库并规范代码结构。

数据导入导出的核心概念

数据导入导出本质上是在不同系统或格式间迁移数据的过程。常见的场景包括数据备份、系统迁移和共享。支持的格式通常有 CSV、Excel、JSON 和 XML。对于 Java 开发者而言,易用性、高效性和可靠性是选型的关键指标。

集成 Apache POI 处理 Excel

Apache POI 是操作 Office 文档的老牌库,在 Spring Boot 中集成它非常成熟。我们主要关注 .xlsx 格式的读写。

依赖配置

首先需要在 pom.xml 中添加必要的依赖。除了基础的 Web 和 JPA 支持外,POI 的核心包和 OOXML 包必不可少。为了支持文件上传测试,建议补充 Commons FileUpload 依赖。

<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>5.2.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.3</version>
    </dependency>
    <!-- 文件上传支持 -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.5</version>
    </dependency>
    <!-- 测试依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

应用配置

H2 内存数据库适合演示,配置简单。记得开启控制台以便调试。

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

实体与仓库层

定义一个产品实体,注意字段映射要准确。Repository 接口继承 JpaRepository 即可获得基础 CRUD 能力。

import javax.persistence.*;
import java.util.Objects;

@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
    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; }

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", productId='" + productId + '\'' +
                ", productName='" + productName + '\'' +
                ", price=" + price +
                ", sales=" + sales +
                '}';
    }
}

业务逻辑实现

Service 层是核心。导入时需要注意跳过表头,导出时要设置列宽以便阅读。资源关闭(Workbook)务必放在 finally 块或使用 try-with-resources,防止内存泄漏。

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 = null;
        try {
            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);
        } finally {
            if (workbook != null) {
                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();
    }
}

控制器接口

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 报表

当需要生成固定格式的正式报表(如财务报表)时,Excel 可能不够美观,此时 JasperReports 是不错的选择。它支持复杂的布局设计和多种输出格式(PDF, HTML 等)。

依赖与模板

同样需要在 pom.xml 中添加 JasperReports 相关依赖。注意,JasperReports 通常需要预编译好的 .jasper 模板文件,或者在运行时加载 .jrxml 源码。

<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>

报表服务实现

Service 层负责加载模板、填充数据并导出为 PDF。这里假设模板文件位于 src/main/resources/reports/product-report.jasper。

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 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;

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

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        JasperExportManager.exportReportToPdfStream(jasperPrint, outputStream);
        return outputStream.toByteArray();
    }
}

报表接口

Controller 中增加一个端点来触发报表生成。

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

实际应用场景总结

在实际项目中,这些功能通常组合使用。例如,允许管理员通过 Excel 批量更新库存,同时定期导出月度销售报表给财务部门。

  • 产品信息管理:支持 Excel 导入导出,方便线下整理后批量入库。
  • 用户信息管理:类似地用于会员数据迁移。
  • 订单统计:结合 JasperReports 生成带图表的 PDF 订单汇总。

访问以下接口即可体验完整流程:

  • GET /api/products/export:下载产品信息 Excel 文件。
  • POST /api/products/import:上传产品信息 Excel 文件,导入数据。
  • GET /api/products/report:下载产品销售报表(PDF 格式)。

开发建议

  1. 异常处理:导入大文件时务必做好事务管理和资源释放,避免 OOM。
  2. 权限控制:导入导出接口涉及敏感数据,记得加上 Spring Security 拦截。
  3. 异步处理:如果数据量超过万条,建议将导入导出改为异步任务,避免阻塞主线程。
  4. 模板维护:JasperReports 的模板修改较为复杂,建议建立专门的模板设计流程。

目录

  1. Spring Boot 数据导入导出与报表生成实战
  2. 数据导入导出的核心概念
  3. 集成 Apache POI 处理 Excel
  4. 依赖配置
  5. 应用配置
  6. 实体与仓库层
  7. 业务逻辑实现
  8. 控制器接口
  9. 使用 JasperReports 生成 PDF 报表
  10. 依赖与模板
  11. 报表服务实现
  12. 报表接口
  13. 实际应用场景总结
  14. 开发建议

更多推荐文章

查看全部
  • DeepSeek 结合通义万相制作 AI 视频实战
  • LlamaFactory v0.9.4 正式发布:全面升级的 LLM 微调框架
  • 腾讯大模型「实用」路线揭示企业 AI 应用新方向
  • LazyLLM 多 Agent 应用全流程实践:源码部署与可视化 Web 调试
  • Windows 本地部署 OpenClaw 集成 DeepSeek 与飞书实现 AI 控制
  • Gazebo 机器人三维物理仿真平台
  • 基于SpringBoot和Vue的制造装备物联及生产管理系统
  • 医疗 AI 中的马尔科夫链深度应用与 Python 实现
  • 2026 年 3 月 18 日人工智能产业动态:算力、智能体与生态
  • Mac Mini M4 本地部署大模型:Ollama、Llama、ComfyUI 与 Stable Diffusion
  • Ubuntu 24.04 在线安装 Redis 8.x 教程
  • 算法:快慢指针解决快乐数判定问题
  • 基于 Edge TTS 的 Python 语音合成实践
  • FANUC 机器人 PR 寄存器详解:定义、操作与实战
  • 扫频信号原理与应用
  • 大模型部署框架详解:Xinference、LocalAI、Ollama 与 FastChat 对比
  • 宇树科技机器人核心技术
  • 二分查找算法原理及常见变种解析
  • 2026 年 Python+AI 入门指南:从零基础到实操落地,避开新手常见坑
  • Python 实现 K-Means 最优 K 值选择:肘部法则与轮廓系数

相关免费在线工具

  • 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