ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Jasper Report 报表开发实战:Jaspersoft Studio 6 模板设计与 Java 集成

Jasper Report 报表开发实战:Jaspersoft Studio 6 模板设计与 Java 集成 1. 为什么报表工具选型绕不开 Jasper Report做企业级应用开发只要涉及到数据导出、打印、对账单、发票、统计报表这类需求Jasper Report 几乎是一个绕不过去的名字。它是一个纯 Java 编写的开源报表引擎能够把数据源里的数据按照设计好的模板渲染成 PDF、Excel、Word、HTML、CSV 等多种格式。而 Jaspersoft Studio 6 就是它的官方可视化设计器基于 Eclipse 平台构建用来拖拽式地设计 .jrxml 模板文件。我最早接触这套工具是在一个财务系统项目里当时需要生成几十种格式各异的对账单客户还要求能导出 Excel 和 PDF 两种格式并且打印出来要分毫不差。试过用 POI 手写、试过用 iText 硬拼最后都被复杂的表格布局和分组统计搞得焦头烂额。换成 Jasper Report 之后模板设计归模板设计数据填充归数据填充职责一下就清晰了。这篇内容适合谁看如果你是一个 Java 后端开发者手头有报表导出需求或者你是刚接手一个老项目里面全是 .jrxml 文件需要维护又或者你只是想找一个能替代手写 POI 的方案那这篇从环境搭建到模板设计再到代码集成的完整流程应该能帮你少走不少弯路。我会把 Jaspersoft Studio 6 的安装配置、模板设计的核心概念、数据源对接、分组与子报表、代码集成以及实际踩过的坑全部摊开来讲。2. 环境准备与 Jaspersoft Studio 6 安装配置2.1 JDK 版本选择与安装Jaspersoft Studio 6 是基于 Eclipse 的运行它本身就需要 JDK。这里有个坑要先说清楚Jaspersoft Studio 6.x 系列对 JDK 版本是有要求的。6.0 到 6.4 这几个版本用 JDK 8 最稳6.5 以后开始支持 JDK 11但如果你用的是 6.0 左右的版本千万别装 JDK 17启动会直接报错。JDK 8 的安装没什么好说的去官网下载对应平台的安装包一路下一步就行。装完之后配置JAVA_HOME环境变量把%JAVA_HOME%\bin加到PATH里。验证方式就是在命令行敲java -version能看到版本号就说明没问题。注意如果你机器上同时装了多个 JDK 版本Jaspersoft Studio 启动时会读取JAVA_HOME指向的那个。建议在启动前确认一下或者直接在sts.iniWindows 下是TIBCO Jaspersoft Studio.ini里用-vm参数指定 JDK 路径这样最保险。2.2 Jaspersoft Studio 6 的下载与安装Jaspersoft Studio 6 的安装包在官方渠道可以下载到有 Windows、macOS、Linux 三个平台的版本。Windows 下是一个 exe 安装程序macOS 下是 dmgLinux 下是 tar.gz。安装过程很标准选好安装路径下一步到底就行。安装完成后第一次启动它会让你选工作空间目录Workspace这个目录用来存放你的报表项目文件。建议单独建一个目录不要放在系统盘根目录或者中文路径下后面会讲到为什么。启动之后你会看到一个典型的 Eclipse 界面左边是项目资源管理器中间是编辑器区域右边是调色板和属性面板。如果你之前用过 Eclipse 或者 IDEA这个布局应该很熟悉。2.3 中文字体配置这一步千万别跳过这是新手最容易踩的坑。Jaspersoft Studio 默认的字体配置里没有中文字体你设计模板的时候如果用了默认字体预览时中文会全部变成方块或者空白。解决办法是在 Studio 里配置字体扩展。具体操作是在项目上右键选择 Properties找到 Jaspersoft Studio 下的 Fonts 选项添加一个字体家族。你需要指定字体名称比如宋体或微软雅黑、对应的 TTF 文件路径以及 PDF 导出时的编码方式。配置好之后在模板里选中文本元素把字体设置成你刚添加的这个中文字体。提示如果你只是导出 Excel 或 HTML中文字体问题不大但只要涉及 PDF 导出字体配置就是必须的。PDF 渲染引擎需要嵌入字体文件否则中文显示不出来。2.4 项目结构说明在 Jaspersoft Studio 里新建一个 JasperReports Project你会看到这样的目录结构src目录存放 .jrxml 源文件Jaspersoft Studio相关配置目录编译后的 .jasper 文件会生成在输出目录.jrxml 是 XML 格式的模板源文件你可以用文本编辑器打开看里面全是标签。.jasper 是编译后的二进制文件运行时引擎实际加载的是这个。在 Studio 里设计的时候改的是 .jrxml保存时会自动编译成 .jasper。3. 报表模板设计的核心概念拆解3.1 Band 机制报表的骨架Jasper Report 的模板设计核心是 Band带区机制。一个报表模板从上到下由若干个 Band 组成每个 Band 在报表渲染时有不同的行为和重复规则。理解 Band 是理解整个 Jasper Report 的关键。主要的 Band 类型包括Band 名称作用重复规则Title报表标题整个报表只出现一次Page Header页眉每页顶部重复Column Header列头每页列头区域重复Detail数据明细每条记录重复一次Column Footer列尾每页列尾区域重复Page Footer页脚每页底部重复Summary汇总整个报表最后出现一次除此之外还有 Group Header 和 Group Footer用于分组统计。比如你按部门分组显示员工薪资每个部门开始前打印部门名称Group Header部门结束后打印该部门的总薪资Group Footer。我个人的经验是设计模板之前先在纸上画一下报表的版面结构标清楚哪些内容每页都要有、哪些内容每条记录都要有、哪些内容只在开头或结尾出现。画清楚了再动手拖控件效率会高很多。3.2 参数、字段与变量Jasper Report 模板里有三种核心数据对象Parameter参数、Field字段、Variable变量。这三个概念初学者容易搞混我用一个实际场景来解释。假设你要生成一份员工薪资报表需要传入一个部门编号作为筛选条件这是 Parameter。查询出来的每条员工记录里有姓名、基本工资、奖金这些是 Field。你需要在报表里计算每个员工的总薪资基本工资奖金以及整个部门的薪资总和这些计算结果是 Variable。Parameter 是外部传入的在代码里通过 Map 传进去。Field 是数据源提供的通常对应 SQL 查询的列或者 JavaBean 的属性。Variable 是在报表渲染过程中计算出来的可以配置计算类型Sum、Count、Average 等和作用范围整个报表、某个分组、当前页。Variable 的 Reset Type 和 Increment Type 是两个关键配置。Reset Type 决定变量什么时候归零Increment Type 决定变量什么时候累加。比如你要算每个部门的总薪资Reset Type 就设成 Group作用在部门分组上Increment Type 设成 Detail每条明细记录都累加一次。3.3 数据源类型与选择Jaspersoft Studio 支持多种数据源类型常见的有JDBC 数据源直接连数据库用 SQL 查询取数JavaBean 数据源用 Java 对象集合作为数据源JSON 数据源用 JSON 数据XML 数据源用 XML 数据CSV 数据源用 CSV 文件空数据源用于纯静态报表在 Studio 里设计阶段你可以配置一个 JDBC 连接直接预览数据这样设计模板时就能看到真实数据的效果。但到了生产环境通常是在 Java 代码里用JRBeanCollectionDataSource或者JRResultSetDataSource来填充数据。选择哪种数据源取决于你的数据来源。如果数据已经在 Java 内存里了用 JavaBean 数据源最方便如果数据在数据库里且查询逻辑简单用 JDBC 数据源直接查也行。我一般倾向于在代码里查好数据封装成 List 传进去这样报表模板不依赖具体的数据库表结构维护起来更灵活。4. 从零设计第一个报表模板4.1 新建模板与数据源配置打开 Jaspersoft StudioFile - New - Jasper Report会弹出一个模板选择向导。它提供了一些预置模板比如空白模板、带标题的模板、带分组统计的模板等。新手建议先选 Blank A4从零开始搭。新建完成后你会看到一个空白的报表设计器。左边是 Outline 视图列出了所有 Band中间是设计画布右边是 Palette控件面板和 Properties属性面板。接下来配置数据源。在 Repository Explorer 视图里右键 Data Adapters - Create Data Adapter选择 Database JDBC Connection。填上数据库连接信息测试连接成功后保存。然后在报表设计器里点击工具栏上的 Dataset and Query 按钮选择你刚建的数据源写 SQL 查询。比如我写一个简单的查询SELECT emp_name, dept_name, base_salary, bonus FROM employee WHERE dept_no $P{deptNo} ORDER BY dept_name, emp_name这里$P{deptNo}就是一个 Parameter需要在模板里先定义好。在 Outline 视图里右键 Parameters - Create Parameter命名为 deptNo类型选 String 或 Integer。4.2 拖拽控件与布局设计数据源配好之后在 Dataset and Query 对话框里点击 Read FieldsStudio 会自动把查询结果里的列读取成 Field。你会看到 emp_name、dept_name、base_salary、bonus 四个字段出现在 Fields 列表里。现在开始设计版面。从 Palette 里拖一个 Static Text 到 Title Band写上员工薪资报表。拖一个 Text Field 到 Page Header表达式写$P{deptNo}用来显示部门编号。在 Column Header Band 里拖四个 Static Text分别写姓名、部门、基本工资、奖金。在 Detail Band 里拖四个 Text Field分别绑定$F{emp_name}、$F{dept_name}、$F{base_salary}、$F{bonus}。布局的时候注意对齐。Studio 提供了对齐工具选中多个控件后可以左对齐、右对齐、居中对齐、等间距分布。表格线可以用 Border 属性来设置选中控件后在 Properties 面板的 Borders 选项卡里勾选需要的边框。实操心得设计表格时建议先用一个 Rectangle 或者 Frame 作为容器把同一行的控件放进去这样调整列宽和行高的时候不容易乱。另外控件的宽度和位置尽量用整数避免出现 0.5 像素这种值导出 PDF 时容易出现线条错位。4.3 分组统计的实现现在加上分组功能。在 Outline 视图里右键 Report - Create Group命名为 deptGroup分组表达式选$F{dept_name}。创建后你会看到多了 Group Header 和 Group Footer 两个 Band。在 Group Header 里放一个 Text Field显示部门名称。在 Group Footer 里放一个 Text Field表达式写$V{deptSalarySum}用来显示该部门的总薪资。然后创建 Variable。右键 Variables - Create Variable命名为 deptSalarySumValue Class 选 BigDecimal 或 DoubleCalculation 选 SumExpression 写$F{base_salary}.add($F{bonus})Reset Type 选 GroupReset Group 选 deptGroupIncrement Type 选 Detail。这样配置之后每进入一个新部门deptSalarySum 会归零每渲染一条明细记录它会把基本工资和奖金加起来累加到了 Group Footer 渲染时显示的就是该部门的总薪资。4.4 预览与调试设计完成后点击工具栏上的 Preview 按钮Studio 会编译模板并填充数据在下方显示预览结果。你可以切换不同的导出格式标签页看 PDF、Excel、HTML 各是什么效果。预览时如果报错常见的原因有SQL 查询字段和 Field 定义不匹配、Parameter 没有传值、表达式语法错误、字体配置缺失。Studio 的错误提示一般比较清楚会告诉你哪个 Band 的哪个元素出了问题。我习惯在设计阶段就把预览调通确保模板本身没问题再拿到 Java 代码里集成。这样出了问题容易定位是模板的问题还是代码的问题。5. Java 代码集成与动态数据填充5.1 Maven 依赖引入在 Java 项目里使用 Jasper Report需要引入相关依赖。如果用 Maven在 pom.xml 里加上dependency groupIdnet.sf.jasperreports/groupId artifactIdjasperreports/artifactId version6.20.0/version /dependency dependency groupIdnet.sf.jasperreports/groupId artifactIdjasperreports-fonts/artifactId version6.20.0/version /dependency版本号根据你实际使用的版本来定。jasperreports-fonts 这个包包含了常用字体如果要做 PDF 导出且涉及中文这个包能省不少事。如果不用 Maven那就手动下载 jar 包加到 classpath 里。除了 jasperreports 主包还需要 commons-digester、commons-collections、commons-logging 等依赖包。用 Maven 的好处就是这些传递依赖会自动处理。5.2 编译模板与填充数据Java 代码里使用 Jasper Report 的流程分三步编译模板、填充数据、导出报表。编译模板的代码String jrxmlPath reports/employee_salary.jrxml; JasperReport jasperReport JasperCompileManager.compileReport(jrxmlPath);如果模板已经编译成 .jasper 文件了也可以直接加载String jasperPath reports/employee_salary.jasper; JasperReport jasperReport (JasperReport) JRLoader.loadObjectFromFile(jasperPath);填充数据MapString, Object parameters new HashMap(); parameters.put(deptNo, D001); ListEmployee employees employeeService.findByDept(D001); JRBeanCollectionDataSource dataSource new JRBeanCollectionDataSource(employees); JasperPrint jasperPrint JasperFillManager.fillReport(jasperReport, parameters, dataSource);这里 Employee 是一个 JavaBean属性名要和模板里的 Field 名对应。比如模板里写$F{emp_name}那 Employee 类里就要有getEmp_name()方法或者用net.sf.jasperreports.engine.data.JRBeanCollectionDataSource的时候字段名默认是属性名。注意JavaBean 的属性名和模板 Field 名的对应关系是大小写敏感的。模板里写$F{empName}JavaBean 里就得是getEmpName()。如果对不上填充出来就是 null。5.3 导出 PDF 与 Excel填充完成后得到 JasperPrint 对象接下来导出成需要的格式。导出 PDFJasperExportManager.exportReportToPdfFile(jasperPrint, output/employee_salary.pdf);导出 ExcelJRXlsxExporter exporter new JRXlsxExporter(); exporter.setExporterInput(new SimpleExporterInput(jasperPrint)); exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(output/employee_salary.xlsx)); SimpleXlsxReportConfiguration config new SimpleXlsxReportConfiguration(); config.setOnePagePerSheet(false); config.setDetectCellType(true); exporter.setConfiguration(config); exporter.exportReport();导出 Excel 的时候有几个配置项值得注意。setOnePagePerSheet控制是否每个分页单独一个 Sheet默认是 true但通常我们更希望所有数据在一个 Sheet 里所以设成 false。setDetectCellType让导出器自动识别单元格类型数字会导出成数字格式而不是文本。5.4 Web 环境下直接输出到浏览器如果是 Web 项目通常需要把报表直接输出到 HTTP 响应流里让用户下载。response.setContentType(application/pdf); response.setHeader(Content-Disposition, attachment; filenamereport.pdf); ServletOutputStream out response.getOutputStream(); JasperExportManager.exportReportToPdfStream(jasperPrint, out); out.flush(); out.close();Excel 的话把 contentType 改成application/vnd.openxmlformats-officedocument.spreadsheetml.sheet然后用 JRXlsxExporter 输出到 response 的 OutputStream。这里有个细节如果报表数据量很大填充和导出会比较耗时用户点了下载按钮后要等很久。可以考虑异步生成先生成到临时文件然后返回一个下载链接。或者用进度条提示用户等待。6. 常见问题排查与避坑经验6.1 中文显示问题速查中文显示问题是最高频的。表现是 PDF 里中文变成空白或者方块Excel 和 HTML 正常。根本原因是 PDF 渲染需要嵌入字体文件而默认字体不含中文字形。解决步骤在 Jaspersoft Studio 里配置字体扩展添加中文字体如宋体、微软雅黑在模板里把所有文本元素的字体设置成这个中文字体确保 jasperreports-fonts 依赖在 classpath 里如果是在 Linux 服务器上生成 PDF服务器上也要安装对应字体我踩过的一个坑是在 Windows 上开发时中文显示正常部署到 Linux 服务器后中文全没了。原因是 Windows 上有宋体字体文件Linux 上没有。解决办法是把字体文件打包到项目里用net.sf.jasperreports.engine.fonts.SimpleFontExtension或者直接在代码里注册字体。6.2 数据填充为空的排查思路填充出来数据全是 null或者报表是空白的排查顺序如下检查 Parameter 名称是否和代码里 put 的 key 一致检查 Field 名称是否和 JavaBean 属性名或 SQL 列名一致检查数据源是否真的有数据检查 SQL 查询条件是否过滤掉了所有数据检查 Detail Band 的高度是否被设成了 0我遇到过一次模板里 Field 写的是$F{userName}JavaBean 里的属性是username全小写结果填充出来全是 null。改成一致后就好了。这种问题很隐蔽因为不报错只是数据出不来。6.3 分页与页码控制报表分页是常见需求。Jasper Report 默认会根据页面高度自动分页。你可以在 Page Footer 里放一个 Text Field表达式写$V{PAGE_NUMBER}显示当前页码写$V{PAGE_COUNT}显示总页数。如果要控制分页行为比如每个分组强制另起一页可以在 Group 的属性里设置Start New Page或者Reset Page Number。如果要在某条记录后强制分页可以在 Detail Band 里加一个 Page Break 控件。有个细节$V{PAGE_NUMBER}在 Page Footer 里显示的是当前页码但如果你想要第 X 页 / 共 Y 页的效果需要把总页数放在一个 evaluationTime 为 Report 的 Text Field 里。因为总页数只有在整个报表渲染完后才知道。6.4 子报表的使用与传参子报表Subreport用于把复杂的报表拆分成多个模块。比如主报表显示订单信息子报表显示订单明细。使用子报表的步骤在主报表里拖一个 Subreport 控件设置子报表的模板路径.jasper 文件路径配置数据源表达式通常是new JRBeanCollectionDataSource($F{orderItems})配置参数传递用$P{...}或者$F{...}传值给子报表子报表的坑在于路径问题。在 Studio 里预览时路径可能是相对路径到了 Java 代码里运行时就找不到了。建议用类路径加载或者绝对路径或者把子报表编译后的 .jasper 文件放在固定位置。6.5 性能优化经验数据量大的时候报表生成会很慢。几个优化方向尽量在 SQL 层面做好过滤和聚合减少 Java 内存里的数据处理避免在模板里做复杂的表达式计算能在代码里算好的就提前算好导出 Excel 时如果数据量超过几万行考虑分 Sheet 或者分文件用JasperFillManager.fillReport的时候如果数据源是 ResultSet注意游标类型开启报表编译缓存避免每次请求都重新编译 .jrxml我做过一个报表数据量大概十万行导出 Excel 要几十秒。后来把 SQL 优化了一下加了索引并且在代码里做了分页查询每页五千行生成时间降到了几秒。所以性能问题往往不在 Jasper Report 本身而在数据准备阶段。6.6 常见问题速查表问题现象可能原因解决方向PDF 中文空白字体未嵌入配置字体扩展设置中文字体数据全为 null字段名不匹配检查 Field 名与属性名/列名报表空白数据源为空检查查询条件和数据源配置分页异常Band 高度或分页设置检查 Band 高度和分页属性子报表找不到路径错误用类路径或绝对路径导出 Excel 格式乱单元格类型未识别开启 detectCellType生成速度慢数据量大或表达式复杂优化 SQL简化模板表达式7. 模板复用与版本管理的一些做法7.1 模板文件的组织方式项目里报表多了之后模板文件的管理就成了问题。我的做法是按业务模块分目录存放比如reports/ finance/ income_statement.jrxml balance_sheet.jrxml hr/ employee_salary.jrxml attendance.jrxml common/ header.jrxml footer.jrxml公共的页眉页脚做成子报表各个业务报表引用。这样改一处所有报表都生效。.jrxml 文件是 XML 文本适合用 Git 做版本管理。但要注意Studio 保存时会重新格式化 XML导致 diff 很大。建议在 Studio 里设置固定的格式化选项减少无意义的变更。另外 .jasper 编译文件不要提交到 Git每次构建时重新编译就行。7.2 用参数控制模板行为同一个模板通过参数控制显示不同的内容可以减少模板数量。比如一个通用报表模板通过$P{showBonus}参数控制是否显示奖金列。在列的 Print When Expression 里写$P{showBonus}当参数为 false 时该列不显示。这种做法的好处是维护一套模板坏处是模板会越来越复杂。我的经验是如果差异超过三处就拆成两个模板不要硬塞到一个模板里。模板的可读性比复用性更重要。7.3 与 Spring Boot 集成现在很多项目是 Spring Boot 的集成 Jasper Report 也很简单。把 .jrxml 文件放在src/main/resources/reports/目录下用ClassPathResource加载。RestController public class ReportController { GetMapping(/report/employee) public void exportEmployeeReport(HttpServletResponse response) throws Exception { InputStream jrxmlStream getClass().getResourceAsStream(/reports/employee_salary.jrxml); JasperReport jasperReport JasperCompileManager.compileReport(jrxmlStream); MapString, Object params new HashMap(); params.put(deptNo, D001); ListEmployee employees employeeService.findByDept(D001); JRBeanCollectionDataSource dataSource new JRBeanCollectionDataSource(employees); JasperPrint jasperPrint JasperFillManager.fillReport(jasperReport, params, dataSource); response.setContentType(application/pdf); response.setHeader(Content-Disposition, attachment; filenamereport.pdf); JasperExportManager.exportReportToPdfStream(jasperPrint, response.getOutputStream()); } }如果模板编译比较耗时可以在应用启动时预编译把 JasperReport 对象缓存起来。Spring Boot 里可以用PostConstruct或者CommandLineRunner来做这件事。8. 一些实战中攒下来的小技巧设计模板的时候善用 Style样式。把常用的字体、边框、背景色定义成 Style然后在控件上引用 Style。这样要改样式的时候改一处就行不用一个个控件去调。Style 可以定义在模板内部也可以定义在外部 .jrtx 文件里多个模板共享。表达式里可以用三元运算符做条件判断比如$F{bonus} null ? 0 : $F{bonus}。但要注意类型匹配Jasper Report 的表达式是强类型的类型不对会报编译错误。如果报表需要打印设计时要注意页边距和打印机的可打印区域。A4 纸的标准是 210mm x 297mm但打印机通常有 5mm 左右的边距打不到。所以内容区域建议控制在 200mm x 287mm 以内。调试复杂表达式的时候可以在表达式里临时加一个new java.lang.Exception().printStackTrace()来输出调用栈或者用System.out.println打印中间值。虽然土但管用。最后说一个关于版本选择的建议。Jaspersoft Studio 6 和 Jasper Report 库的版本最好保持一致。比如 Studio 用 6.20.0那 Maven 依赖也用 6.20.0。版本不一致有时候会出现模板在 Studio 里预览正常代码里填充报错的情况。我一般会在项目里明确锁定版本号避免因为依赖升级导致兼容性问题。报表这个东西设计阶段花的时间越多后期维护越省心。模板结构清晰、命名规范、样式统一后面加需求改起来就快。反过来如果一开始就是拖控件堆出来的过两个月自己都看不懂了。
返回列表