JAVA springboot+JasperReports+spring-data-JPA demo

最近做了个关于导出PDF的功能,便写篇博客记录下。

1.准备工作。

我用的开发工具为是eclipse和TIBCO Jaspersoft studio 6.17.0
eclipse软件应该就不用说了,主要是TIBCO Jaspersoft studio 6.17.0,这个软件是用来画报表模板的
软件下载地址:点我跳转
在这里插入图片描述
安装过程略过,默认安装即可。

2.开始画模板。

file->new->Jasper report->Blank A4(即空白模板)->设置report名称->选择数据源(我这里是到时候通过java代码传,这里模板就直接给空即选择one Empty Record这个)->finish
选择Blank A4
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
模板设计界面的区域说明如下。中间的模板设计区域分别多个band,这些band都可以右键删除
Title:标题区,如果数据有多页只会在第一页打印
Page Header:页头区,多页的情况下每页都会打印
Column Header:列头区,配合下面的Detail区打印列表数据,多页的情况下每页都会打印
Detail:可以新增多个Detail区,可以组合打印出多个列表数据,多页的情况下每页都会打印
Column Footer:列表底部区,多页的情况下每页都会打印
Page Footer:页底部区,多页的情况下每页都会打印
Summary:合计区,只会在最后一页打印
在这里插入图片描述
这个是公司需求样式画出来的模板。具体分析下其中内容;删除了一些用不上的板块,只留下了Title,4个detail和page footer板块。其中detail1为单个数据使用text field,detail2和detail4为循环数据并嵌套了一个子报表即分为表头static text和子报表subreport,detail 3和title为纯static text。另外每个小块都用Frame套了一层,使用 frame 的好处是,可以画一个边框把某一类的元素圈起来,这里推荐使用矩形框,这样这些元素就可以作为一个整体统一操作咯。(如果用过android studio的小伙伴应该对这个也很熟悉,无非就是定义参数,绑定元素,拖拉至设计区域,设计成预期样式)
$P、$F、$V的区别: P为传参,不能为空,F为携带的数据,可以为空、V为求和;P在Parameters中定义,F在Fields中定义。

在这里插入图片描述
在这里插入图片描述
注释:定义P和F还有V的时候需要指定该字段的类型,若指定的类型和传入的值的类型不同会报错。
元素具体层级为:
在这里插入图片描述

在这里插入图片描述
注释:模块的英文部分最好选择DejaVu Sans,这个字体在springboot可以通过直接添加依赖进行使用,选择其他的字体,若服务器上没有该字体需要进行手动导入,否则会报错,虽然可以在配置文件中设置忽略该问题,但是会导致展示出来的与预期不同。
在这里插入图片描述
当数据没值得时候会显示null若希望将其设置为空白,在上图中Blank when null勾选即可。
主模板大概就是这样了。再准备设计子模板并绑定了。两个子模板样式为:
在这里插入图片描述
在这里插入图片描述
注释:这里把边距全部设置为零了,不然嵌入主模板会隔得很开,不美观。
$V需要在Variable中定义。
主子报表关联:
步骤一选择Subreport并托到对应的位置便会弹出Subreport窗口,按顺序选择指定的子模板即可。
![在这里插入图片描述](https://img-blog.csdnimg.cn/fa6600789c8c4e51ba6a3474da2c3430.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-R6JC95aaC6aOY6Zuq,size_20,color_FFFFFF,t_70,g_se,x_16
步骤二,点击subreport元素,编辑详情在这里插入图片描述
在这里插入图片描述

注释:Expression是查询子模板位置,我这边设置为动态,用P将子模板的路径传过来。data source Expression选择new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{sub1}) sub1是一个list集合,用于保存子模板的数据。

3.展示模板效果

大致上模板就这样就完成了,然后运行下看看样式(写入$P的传参):
在这里插入图片描述
在这里插入图片描述
模板到这里就没什么问题了,需要将jrxml文件转换为jasper文件。然后开始java端调用以及传参。
在这里插入图片描述
上面的是将所有的jrxml生成jasper文件,下面的是只将当前的jrxml文件生成。

4.java开发

1.添加依赖
因为我TIBCO Jaspersoft studio的版本是 6.17.0,所以我添加的依赖版本也都是6.17.0,因为学习过程中看有小伙伴说版本不一致可能会导致出问题,我这边虽然没出现,但是保险起见还是选择相同的版本,毕竟不要给自己找不自在不是。

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

注释:jasperreports依赖是基本的方法jar,jasperreports-functions是函数jar,比如我模板中用到的dateformate函数就需要导入该jar,不然是无法识别并报错。jasperreports-fonts这个是自带的字体也就是上面模板中DejaVu Sans字体,我写需求的时候一开始用的是Arial字体,然后本地没问题,但是放到测试环境上就不行,日志显示的就是没有Arial字体,虽然说可以在测试环境加,但是如果后续又换了一个环境,岂不是又会报错,所以不建议每个PC都手动添加字体,如果非要使用Arial字体,则需要在资源文件中做相对于的操作,后面会写到,这里就先不说了。
2.将模板放入指定位置
resource下创建jasper文件夹,再将jrxml和jasper文件放入该路径下,fonts文件夹和fonts.xml是用于添加字体,msyh.ttf为微软雅黑字体的ttf文件。jasperreport.properties文件用于设置忽略不存在字体报错的问题,jasperreport_extension.properties用于配置新添加的字体路径。
在这里插入图片描述
fonts.xml

<?xml version="1.0" encoding="UTF-8"?>
<fontFamilies>
    <fontFamily name="是微软雅黑吖">
        <normal>jasper/fonts/msyh.ttf</normal>
        <bold>jasper/fonts/msyh.ttf</bold>
        <italic>jasper/fonts/msyh.ttf</italic>
        <boldItalic>jasper/fonts/msyh.ttf</boldItalic>
        <pdfEncoding>Identity-H</pdfEncoding>
        <pdfEmbedded>true</pdfEmbedded>
        <exportFonts>
            <export key="net.sf.jasperreports.html">'是微软雅黑吖', Arial, Helvetica, sans-serif</export>
            <export key="net.sf.jasperreports.xhtml">'是微软雅黑吖', Arial, Helvetica, sans-serif</export>
        </exportFonts>
    </fontFamily>
</fontFamilies>

jasperreport.properties

net.sf.jasperreports.awt.ignore.missing.font=true

jasperreport_extension.properties

net.sf.jasperreports.extension.registry.factory.simple.font.families=net.sf.jasperreports.engine.fonts.SimpleFontExtensionsRegistryFactory
net.sf.jasperreports.extension.simple.font.families.dejavu=jasper/fonts/fonts.xml

3.编写公共方法:

public class JasperReportUtil {
	static JasperPrint getJasperPrint(InputStream jasperStream, Map parameters, List<?> list) throws JRException {
        JRDataSource dataSource = null;
        if (null == list || list.size() == 0) {
            dataSource = new JREmptyDataSource();
        } else {
            dataSource = new JRBeanCollectionDataSource(list);
        }
        JasperPrint jasperPrint = JasperFillManager.fillReport(jasperStream, parameters, dataSource);
        return jasperPrint;
    }

    public static void exportToPdf(String jasperPath, Map parameters, List<?> list, HttpServletResponse response) throws Exception {
        OutputStream outputStream = response.getOutputStream();
      
        try {
            ClassPathResource resource = new ClassPathResource(jasperPath);
            response.setContentType("application/pdf");
            InputStream jasperStream = resource.getInputStream();
            JasperPrint jasperPrint = getJasperPrint(jasperStream, parameters, list);
            JasperExportManager.exportReportToPdfStream(jasperPrint, outputStream);
        } catch (Exception e) {
            e.printStackTrace();
            outputStream.write("读取报表异常".getBytes());
        } finally {
            outputStream.flush();
            outputStream.close();
        }
    }
}```
exportToPdf这个方法的参数:1.模板路径;2.数据list;3顾名思义response,用于获取输出流写出。


4.调用方法
control层
@RequestMapping(value = “/printPdf”,method = RequestMethod.POST)
@NoAuthorized
public void printPdf(Map<String, Object> parameters,
HttpServletResponse response,Long headId) throws IOException {
Service.printPdf(parameters,response,headId);
}```
headeId为具体展示那条数据
service层

//resultList是查询数据库后返回的数据,因为涉及到公司数据库,所以就不展示具体Sql了。
List<Map> resultList = new ArrayList<map>();
        List<HashMap> list = new ArrayList<>();
        List<Object> list2 = new ArrayList<>();
        List<HashMap> list3= new ArrayList<>();
        HashMap<String, Object> item2 = new HashMap<String, Object>();
        parameters.put("ReplenishmentSub1", new ClassPathResource("jasper/Replenishment_sub1.jasper").getPath());
        parameters.put("ReplenishmentSub2", new ClassPathResource("jasper/Replenishment_sub2.jasper").getPath());
        for (Map map : resultList) {
        	item2.put("CHANNEL_FROM",map.get("from")+"");
            item2.put("CHANNEL_TO",map.get("to")+"");
        
            item2.put("ADDRESS1_EN",map.get("address")+"");
            item2.put("ADDRESS2_EN","");
            item2.put("ADDRESS3_EN","");
            item2.put("NO_OF_PACKAGE", "");
            
            List<Map> lines = 具体查询数据同上不展示;
            for(int i=0;i<lines.size();i++){
            	HashMap<String, Object> item = new HashMap<String, Object>();
            	item.put("ITEM_CODE", lines.get(i).get("ITEM")+"");
            	item.put("ITEM_DESC",  lines.get(i).get("ITEMDESC")+"");
            	item.put("ITEM_QTY", new BigDecimal(lines.get(i).get("QTY")+""));
            	list.add(item);
            	list3.add(item);
            }
            
        }        
            item2.put("sub1", list);
            item2.put("sub2", list3);
            list2.add(item2);
            try {
			JasperReportUtil.exportToPdf("jasper/test_main.jasper",parameters,list2,response);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
          

整体逻辑就是需要将$P通过parameters属性传入模板,查询出的数据需要装入一个List传入模板,每个$F都是Hashmap中的一个值,而子模板的所有数据也要装入一个list中,所以就有三个List,两个为子模板的数据,一个为父模板的数据,而子模板的list与父模板的数据平级即有层级关系为list2>item2>(list=list3=父模板的$F)>item;注释:item为子模板的数据map,item2为父模板的数据map,list和list3为子模板的list集合,list2为父模板的list集合。

5.测试展示:
在这里插入图片描述
这样就可以了。

5.总结

这篇文章是在我开发完后总结写的,所以可能有一些地方遗漏了,如果有啥问题可以留言,另外有地方错误也希望各位小伙伴指出。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值