转自:http://www.jcombat.com/spring/working-with-jasperreports-for-easy-report-generation
JasperReports is a Java based open source report generating engine. It has great dynamic report generating capabilities that can be added to any Java based application. I have a very simple demo to show how it works.
I have created a link somewhere in my application and on the click of it, I see a pop-up window with the report PDF embedded within.
To start with, we need a .jrxml template that is externalized and every time the link is clicked, the.jrxml template is dynamically compiled to generate a .jasper file at the same external file location. So the first and foremost step is to prepare the mark-up for the report template, which can be easily designed using the iReport tool. The tool finally generates the .jrxml file based on the design. The standard JasperReport template has the following layout in the same order:
1
2
3
4
5
6
7
|
<title>
<pageHeader>
<columnHeader>
<detail>
<columnFooter>
<pageFooter>
<summary>
|
Fine, now let’s assume, I have my .jrxml file generated and looks something like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
<!DOCTYPE jasperReport PUBLIC "//JasperReports//DTD Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
<jasperReport xsi
:
schemaLocation
=
"http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
name
=
"jasper_report_template"
language
=
"groovy"
pageWidth
=
"595"
pageHeight
=
"842"
columnWidth
=
"555"
leftMargin
=
"20"
rightMargin
=
"20"
topMargin
=
"20"
bottomMargin
=
"20"
xmlns
=
"http://jasperreports.sourceforge.net/jasperreports"
xmlns
:
xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
>
<parameter
name
=
"ReportTitle"
class
=
"java.lang.String"
/>
<parameter
name
=
"Author"
class
=
"java.lang.String"
/>
<queryString>
</queryString>
<field
name
=
"subjectName"
class
=
"java.lang.String"
>
<fieldDescription>
subjectName
</fieldDescription>
</field>
<field
name
=
"marks"
class
=
"java.math.BigDecimal"
>
<fieldDescription>
marks
</fieldDescription>
</field>
<title>
<band
height
=
"70"
>
<line>
<reportElement
x
=
"0"
y
=
"0"
width
=
"515"
height
=
"1"
/>
</line>
<textField
isBlankWhenNull
=
"true"
bookmarkLevel
=
"1"
>
<reportElement
x
=
"0"
y
=
"10"
width
=
"515"
height
=
"30"
/>
<textElement
textAlignment
=
"Center"
>
<font
size
=
"22"
/>
</textElement>
<textFieldExpression
class
=
"java.lang.String"
>
$P{ReportTitle}
</textFieldExpression>
<anchorNameExpression>
"Title"
</anchorNameExpression>
</textField>
<textField
isBlankWhenNull
=
"true"
>
<reportElement
x
=
"0"
y
=
"40"
width
=
"515"
height
=
"20"
/>
<textElement
textAlignment
=
"Center"
>
<font
size
=
"10"
/>
</textElement>
<textFieldExpression
class
=
"java.lang.String"
>
$P{Author}
</textFieldExpression>
</textField>
</band>
</title>
<columnHeader>
<band
height
=
"23"
>
<staticText>
<reportElement
mode
=
"Opaque"
x
=
"0"
y
=
"3"
width
=
"535"
height
=
"15"
backcolor
=
"#70A9A9"
/>
<box>
<bottomPen
lineWidth
=
"1.0"
lineColor
=
"#CCCCCC"
/>
</box>
<textElement/>
<text>
</text>
</staticText>
<staticText>
<reportElement
x
=
"414"
y
=
"3"
width
=
"121"
height
=
"15"
/>
<textElement
textAlignment
=
"Center"
verticalAlignment
=
"Middle"
>
<font
isBold
=
"true"
/>
</textElement>
<text>
Marks
</text>
</staticText>
<staticText>
<reportElement
x
=
"0"
y
=
"3"
width
=
"136"
height
=
"15"
/>
<textElement
textAlignment
=
"Center"
verticalAlignment
=
"Middle"
>
<font
isBold
=
"true"
/>
</textElement>
<text>
Subject
</text>
</staticText>
</band>
</columnHeader>
<detail>
<band
height
=
"16"
>
<staticText>
<reportElement
mode
=
"Opaque"
x
=
"0"
y
=
"0"
width
=
"535"
height
=
"14"
backcolor
=
"#E5ECF9"
/>
<box>
<bottomPen
lineWidth
=
"0.25"
lineColor
=
"#CCCCCC"
/>
</box>
<textElement/>
<text>
</text>
</staticText>
<textField>
<reportElement
x
=
"414"
y
=
"0"
width
=
"121"
height
=
"15"
/>
<textElement
textAlignment
=
"Center"
verticalAlignment
=
"Middle"
>
<font
size
=
"9"
/>
</textElement>
<textFieldExpression
class
=
"java.lang.String"
>
$F{subjectName}
</textFieldExpression>
</textField>
<textField>
<reportElement
x
=
"0"
y
=
"0"
width
=
"136"
height
=
"15"
/>
<textElement
textAlignment
=
"Center"
verticalAlignment
=
"Middle"
/>
<textFieldExpression
class
=
"java.lang.String"
>
$F{marks}
</textFieldExpression>
</textField>
</band>
</detail>
<columnFooter>
<band
height
=
"200"
>
<barChart>
<chart
evaluationTime
=
"Report"
>
<reportElement
x
=
"0"
y
=
"0"
width
=
"555"
height
=
"200"
/>
<chartTitle>
<titleExpression>
"My First JR Bar Chart"
</titleExpression>
</chartTitle>
</chart>
<categoryDataset>
<dataset
incrementType
=
"None"
/>
<categorySeries>
<seriesExpression>
$F{subjectName}
</seriesExpression>
<categoryExpression>
$F{subjectName}
</categoryExpression>
<valueExpression>
$F{marks}
</valueExpression>
</categorySeries>
</categoryDataset>
<barPlot
isShowTickMarks
=
"false"
>
<plot/>
</barPlot>
</barChart>
</band>
</columnFooter>
<pageFooter>
<band
height
=
"400"
splitType
=
"Stretch"
>
<pieChart>
<chart
evaluationTime
=
"Report"
>
<reportElement
x
=
"135"
y
=
"0"
width
=
"270"
height
=
"300"
/>
<chartTitle>
<titleExpression>
"My First JR Pie Chart"
</titleExpression>
</chartTitle>
</chart>
<pieDataset>
<dataset
incrementType
=
"None"
/>
<keyExpression>
$F{subjectName}
</keyExpression>
<valueExpression>
$F{marks}
</valueExpression>
</pieDataset>
<piePlot>
<plot/>
<itemLabel/>
</piePlot>
</pieChart>
</band>
</pageFooter>
</jasperReport>
|
I keep the above .jrxml file to some externalised location, let it be, D:/Documents/
Create a sample POJO, with two instance variables for subject name and marks, as:
DataMarksBean.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package
com
.
jcombat
.
beans
;
public
class
DataMarksBean
{
private
String
subjectName
;
private
Integer
marks
;
public
String
getSubjectName
(
)
{
return
subjectName
;
}
public
void
setSubjectName
(
String
subjectName
)
{
this
.
subjectName
=
subjectName
;
}
public
Integer
getMarks
(
)
{
return
marks
;
}
public
void
setMarks
(
Integer
marks
)
{
this
.
marks
=
marks
;
}
}
|
Create another class DataBeanMarksList.java, which generates a collection of java bean objects to be used for report generation. This class looks something like:
DataBeanMarksList.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package
com
.
jcombat
.
bo
;
import
java
.
util
.
ArrayList
;
public
class
DataBeanMarksList
{
public
ArrayList
<DataMarksBean>
getDataBeanList
(
)
{
ArrayList
<DataMarksBean>
dataBeanList
=
new
ArrayList
<DataMarksBean>
(
)
;
dataBeanList
.
add
(
produce
(
"English"
,
58
)
)
;
dataBeanList
.
add
(
produce
(
"SocialStudies"
,
68
)
)
;
dataBeanList
.
add
(
produce
(
"Maths"
,
38
)
)
;
dataBeanList
.
add
(
produce
(
"Hindi"
,
88
)
)
;
dataBeanList
.
add
(
produce
(
"Scince"
,
78
)
)
;
return
dataBeanList
;
}
/*
* This method returns a DataBean object, with subjectName and marks set
* in it.
*/
private
DataMarksBean
produce
(
String
subjectName
,
Integer
marks
)
{
DataMarksBean
dataBean
=
new
DataMarksBean
(
)
;
dataBean
.
setSubjectName
(
subjectName
)
;
dataBean
.
setMarks
(
marks
)
;
return
dataBean
;
}
}
|
Now you need to create a method in the controller of your Spring MVC application, that actually serves the purpose of report generation. I give it some mapping as /jasper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
@RequestMapping
(
value
=
"/jasper"
)
public
void
generatePDFJasperChart
(
HttpServletRequest
request
,
HttpServletResponse
response
)
throws
IOException
{
String
sourceFileName
=
"D://Documents/jasper_report_template.jrxml"
;
System
.
out
.
println
(
sourceFileName
)
;
Map
<
String
,
Object
>
parameters
=
new
HashMap
<
String
,
Object
>
(
)
;
parameters
.
put
(
"ReportTitle"
,
"Jasper Demo"
)
;
parameters
.
put
(
"Author"
,
"Prepared By jCombat"
)
;
try
{
System
.
out
.
println
(
"Start compiling!!! ..."
)
;
JasperCompileManager
.
compileReportToFile
(
sourceFileName
)
;
System
.
out
.
println
(
"Done compiling!!! ..."
)
;
sourceFileName
=
"D://Documents/jasper_report_template.jasper"
;
DataBeanMarksList
DataBeanList
=
new
DataBeanMarksList
(
)
;
ArrayList
<DataMarksBean>
dataList
=
DataBeanList
.
getDataBeanList
(
)
;
JRBeanCollectionDataSource
beanColDataSource
=
new
JRBeanCollectionDataSource
(
dataList
)
;
JasperReport
report
=
(
JasperReport
)
JRLoader
.
loadObjectFromFile
(
sourceFileName
)
;
JasperPrint
jasperPrint
=
JasperFillManager
.
fillReport
(
report
,
parameters
,
beanColDataSource
)
;
if
(
jasperPrint
!=
null
)
{
byte
[
]
pdfReport
=
JasperExportManager
.
exportReportToPdf
(
jasperPrint
)
;
response
.
reset
(
)
;
response
.
setContentType
(
"application/pdf"
)
;
response
.
setHeader
(
"Cache-Control"
,
"no-store"
)
;
response
.
setHeader
(
"Cache-Control"
,
"private"
)
;
response
.
setHeader
(
"Pragma"
,
"no-store"
)
;
response
.
setContentLength
(
pdfReport
.
length
)
;
response
.
getOutputStream
(
)
.
write
(
pdfReport
)
;
response
.
getOutputStream
(
)
.
flush
(
)
;
response
.
getOutputStream
(
)
.
close
(
)
;
}
}
catch
(
JRException
e
)
{
e
.
printStackTrace
(
)
;
}
}
|
Almost done, create a link somewhere on the landing page in your application as:
1
|
<a
href
=
""
onclick
=
"openNewWindowForJasperWithCharts();"
>
Generate Report
</a>
|
Male sure you include the following javascript function as well into your application:
1
2
3
4
5
|
function
openNewWindowForJasperWithCharts
(
)
{
var
url
=
"/jasper"
;
var
strWindowFeatures
=
"menubar=no,location=no,width=800,height=500"
;
window
.
open
(
url
,
"_blank"
,
"location=0,height=500,width=800"
)
;
}
|
Finally, clicking on the link, shows up the report PDF in a new pop-up window as:
If you need the same report to be exported to HTML, just modify the above controller method and add the following:
1
2
3
|
if
(
jasperPrint
!=
null
)
{
renderHtml
(
new
JRHtmlExporter
(
)
,
jasperPrint
,
writer
)
;
}
|
Add a new method renderHtml as:
1
2
3
4
5
6
7
8
|
public
static
void
renderHtml
(
JRExporter
exporter
,
JasperPrint
print
,
Writer
writer
)
throws
JRException
{
exporter
.
setParameter
(
JRExporterParameter
.
JASPER_PRINT
,
print
)
;
exporter
.
setParameter
(
JRExporterParameter
.
OUTPUT_WRITER
,
writer
)
;
exporter
.
setParameter
(
JRHtmlExporterParameter
.
IS_USING_IMAGES_TO_ALIGN
,
Boolean
.
FALSE
)
;
exporter
.
setParameter
(
JRHtmlExporterParameter
.
IMAGES_URI
,
"servlets/image?image="
)
;
exporter
.
exportReport
(
)
;
}
|
So we have several points worth noting:
- The .jasper file is not kept anywhere in the JVM for any subsequent PDF generation and for now, is generated every time the link is clicked.
- As expected, if we modify the .jrxml template, it reflects instantly at runtime and we do not require any java changes, hence no build/deploy needed.
- As per the documentation available for JasperReports, “Even though all its reporting functionality is available in this single JAR file (jasperreports-5.0.1.jar), JasperReports relies on other third-party libraries for related required functionality like XML parsing, logging, and PDF generation.”
To avoid performance hit on the application because of the .jrxml file being compiled again and again, I suggest following options to restrict the subsequent compilations:
- We can keep the .jasper file in cache and generate the .jasper soon after the cache expires.
- We can also check for the existing .jasper file at the respective externalized location. If it doesn’t exist, compile, else not.
- We can build a utility to explicitly compile the .jrxml template, generate the .jasper file and copy it to the respective externalized location.