用(yòng)JasperReport+iReport進行(xíng)Web報≥∑(bào)表開(kāi)發
序言
在很(hěn)多(duō)實際的(de)項目裡 δ↓(lǐ),報(bào)表都(dōu)是β∑(shì)其中十分(fēn)重要(yào)的(de)組'§成部分(fēn),例如(rú)把查詢結果以報(bào)表的(de)形¥Ω☆ 式呈現(xiàn)出來(lái)。這(zhè)裡(lǐ)所提到(dào)的(de)報(bà γγo)表可(kě)不(bù)是(shì)簡單的(de)二維表,而是(shì)¥↑擁有(yǒu)複雜(zá)表頭的(de)、多(duō)維的(de)、可↔ (kě)以在運行(xíng)期從(cóng)數(shù)據庫中自(zì)動讀(dúΩ©≥σ)取數(shù)據、可(kě)自(zì)動分(fēn)頁、擁有(yǒu)™>↓↓豐富的(de)頁面元素(圖片ÿ↓↑✔0c;超連接等)、支持分(fēn)組和(hé)交叉表→©δ、支持打印、最好(hǎo)還(hái)能(néng)導出到(dào)Excel或Word…...&σ≠#xff08;汗
L)。但(dàn)是(shì)顯而易見(jiàn)α₩$c;報(bào)表功能(néng)越強大(dà),λ±₹提供的(de)服務越豐富,其複雜(zá)度也(yě)就(jiù)越提高(gπ<÷÷āo),所以僅靠石器(qì)時(shí)代的(de)手工(gōng)方∏©α式生(shēng)成報(bào)表是(shì)不(bù)αδ'能(néng)滿足需要(yào)的(de)。所幸,目前我們所熟知(zhī)的(¶✘de)幾款報(bào)表工(gōng)具功能(néng)上(shàng)足夠強大(dà)>Ωπ,而且都(dōu)附有(yǒu)很(hβ₹ ěn)方便的(de)報(bào)表生(shēng)成工(gōnΩ©g)具。它們分(fēn)别是(shì):JasperRepor¶→'t(+iReport)ÿγε✔0c;BIRT(+eclipseÿ↑ ₽$09;,水(shuǐ)晶報(bào)表(+ec←"≤lipse,JBuiler等等)。
之所以提到(dào)這(↓±zhè)三種報(bào)表工(gōng)具首先是(shì)因為(wèi)他(tā)們都(dōu) π÷✘是(shì)開(kāi)放(fàng)源碼的(de)↓∑×★xff08;CrystalReportForEclipse1.0已經開(kāi₽•≥)源了(le))。既然不(bù)用(yòng)考慮費(fèi)用(yòng)>©≠f0c;那(nà)在我們的(de)項目中到(dào)底選用(yòng)哪一(yī)個(gè)呢(n→↔↔λe)?對(duì)于水(shuǐ)晶報(bào)表而言 ≈•™c;雖然其在.Net平台上(shàng)表現(xiàn)十分(fēn)搶眼♠$♦,但(dàn)是(shì)在Java平台上(s♥↔hàng),多(duō)數(shù)的(de)實現(xiàn)都(dōu)是(shì'γ )要(yào)收費(fèi)的(de)(例如(rú)For JBuilder版&≈ ™₹#xff09;,而且其Eclipse插件(jiàn)的(de)資源消耗十分(f σφēn)驚人(rén)(我的(de)機(jī)器("'σqì)配置為(wèi)P
4 3.0+512RAM,使用(yòng)“Ecl♣σ∏♥ipse3.2+水(shuǐ)晶報(bào)表插件(jiàn)”根本就(jiù)跑不(bù★™)動)。所以我選擇了(le)純Java的(de)報(bào)表工(gōng)∑≥具JasperReport與iReport的(de)組合。但(dàn)是(shì)關于Jaspe★←rReport的(de)文(wén)檔相(xiàngπ>)對(duì)匮乏,其官方文(wén)檔還(÷εhái)是(shì)要(yào)收費(fèi)的(de),所我希望利用(y★Ωòng)這(zhè)篇文(wén)章(zhāng)展示如(rú)何利用(yòng)這(zβ>hè)一(yī)強力組合來(lái)進行(xíng)基于Web₩≥的(de)報(bào)表開(kāi)發,希望能≠β← (néng)為(wèi)那(nà)些(xiē)苦于報(bào)♠表的(de)同仁們解決一(yī)些(xiē)實際問(wèn∑•&)題。
本文(wén)将火(huǒ)力集中在如(rú)何在Web★α÷環境下(xià)配置和(hé)使用(yòng)JasperReport報(b≥φào)表和(hé)報(bào)表的(de)導出功能(néng∞π✘)等方面,由于在以前的(de)Blog中我已經寫 γφ∏過如(rú)何設計(jì)普通(tōng)的(de)報(bào)表±★✔,所裡(lǐ)這(zhè)裡(lǐ)将不(bφ☆±ù)再贅述。對(duì)于那(nà)些(xiē)基本的(de)操&¶↓作(zuò)則留給讀(dú)者自(zì)行(xíng)體(tǐ)會(♠<♦huì),相(xiàng)信在iReport的(d≤↑♣e)幫助下(xià),上(shàng)手會(huì)很(hěn)快(k₹βuài)的(de)。
(注:本文(wén)已被《程序員(yuán)》收錄± φc;未經允許不(bù)得(de)轉載
)
1
JasperReport簡介
2
Web報(bào)表開(kāi)發
2.1
環境設置
2.2
報(bào)表預覽框架
2.3
使用(yòng)JNLP技(jì)術(shù)實現(xiàn)客戶端預覽
3
結束語... 24
1 JasperReport簡介
JasperReport是(shì)一(yī)個(gè)強大(dà>π♣ )、靈活的(de)報(bào)表生(shēng)成工(gōng)具,能®→(néng)夠展示豐富的(de)頁面內(nèi)容 ∏☆f0c;并将之轉換成PDF,HTML,XML,E∏¥↕xcel(通(tōng)過POI或JExcelAPI實現(xiàδ♥™n))和(hé)Rtf(通(tōng)過POI實現(xiàn)&σσ#xff09;格式。該庫完全由Java寫成ÿ≠®0c;可(kě)以用(yòng)于在各種Java應用(yòng)程序ÿ↔σ₩↔0c;包括J2EE,Web應用(yòng)程序中生(shēng)成→ 動态內(nèi)容。它的(de)主要(yào)目的(de)是(shì)輔α±<★助生(shēng)成面向頁面的(de)(page oriented)∏ε,準備付諸打印的(de)文(wén)檔。JasperReport借由定義于XML文(φ↔wén)檔中的(de)report design進行(♦π™xíng)數(shù)據組織。這(zhè)些(xiē)數(shù)據≠₹& 可(kě)能(néng)來(lái)自(zì)不(bù)同的(de)數(shù)據源&↕♥₽xff0c;包括關系型數(shù)據庫,coΩ¶§↕llections,java對(duì)象數(shù)組。通(tōng)過實©®★€現(xiàn)簡單的(de)接口,用(yòng)戶就(jiù)可φ¶¥(kě)以将report library插入到(dào)訂制(zhì)好(hǎo)的(de)≠≠數(shù)據源中。用(yòng)JasperReport進行(xíng)報(bào)表開(k≠€₽āi)發的(de)過程如(rú)下(xià)所示(Version=1♦&.0):

目前JasperReport最新的(de)版本是(shì)1.2.7,可(k¶'πě)以到(dào)Sourceforg網站(zhàn)下(xià)載其整個(gè)工(gōng≠✘>)程及代碼。其工(gōng)程文(wén)件(jiàn)目錄下§π≤£(xià)的(de)demo子(zǐ)目錄中包含很(hěn)多(duō)定義良好¶©↔(hǎo)的(de)例子(zǐ),可(kě)以實現(xiàn)各種所需功能(nén✔ ↔g)。鑒于它的(de)文(wén)檔收費(fèi),想學習(xí)使用(yò♥εng)JasperReport的(de)話(huà)我們♦∞也(yě)隻能(néng)以這(zhè)些(xiē)δ¥demo作(zuò)為(wèi)學習(xí)資料了(le)。
σ 但(dàn)是(shì)繁瑣的(de)XML标記和(hé)功能(néng)A↓π¥PI在提供強大(dà)的(de)動态及可(kě)擴展開(kāi)發的(de)÷∞₩Ω同時(shí)也(yě)帶來(lái)了(le)超高(gāo)的(de)複雜(zá)性→≤xff0c;在沒有(yǒu)免費(fèi)文(wén)檔的( ✔<de)情況下(xià),手工(gōng)編寫報(bào)表設計☆™(jì)所需的(de)XML文(wén)件(jiàn)是(shì)極其不(bù)明(míngε ←)智的(de)。不(bù)過正如(rú)我們用(yòng)JB♥πuilder(或其他(tā)可(kě)視(shì)化(huà)開(kāi)發工 ₹(gōng)具)編寫SwingGUI時(shí)一(£≤yī)樣,我們可(kě)以采用(yòng)iRe •±₽port進行(xíng)可(kě)視(shì)化(huà)的(de)報(bào)表設計(jγ $ì)來(lái)避免和(hé)可(kě)怕的(de)XML文(w÷≤én)件(jiàn)及實現(xiàn)細節打交道(dào)。雖然可(kě)能δδα✔(néng)會(huì)損失一(yī)些(xiē)動态生(shēn✔&g)成報(bào)表的(de)靈活性,但(dàn)$♥是(shì)大(dà)多(duō)數(shù)情況下(xià)∏↔♦★,我們隻需要(yào)靜(jìng)态的(de↕•¥₹)設計(jì)框架和(hé)動态的(de)裝填數(s✘↓∞hù)據而很(hěn)少(shǎo)需要(yào)動态的(de)報(bào)表↕α×∏框架,所以和(hé)我們所獲得(de)的(de)方便相(xià↑★÷ng)比,這(zhè)些(xiē)小(xiǎo)小(x¶¶iǎo)的(de)損失簡直可(kě)以忽略不(bù)計(jì)了(le)。當≤£β然如(rú)果确實需要(yào),且看(kàn)到(dào)下(x∞$ià)面的(de)東(dōng)西(xī)你(nǐ)不(bù)暈的(de)話(hu∏₽à),自(zì)己動手确實可(kě)以獲得(de)所π£ ÷需的(de)靈活性。
其中的(de)VerticalFilling和(h→λé)HorizontalFilling表示裝填數(shù)據的(d&¥e)順序。從(cóng)上(shàng)圖我們可(kě)以清楚地(dì)看(kàn)到(dào)&≤≤& #xff0c;一(yī)個(gè)報(bào)表的(de)設計(jì)主要(yào)由Page♥≥♠Header和(hé)報(bào)表內(nèi)容組成,報(bào)表內(nèi)容$£又(yòu)是(shì)由列組成,內(n&®èi)容既可(kě)以是(shì)一(yī)列也(yě)可(kě)©©§α以是(shì)多(duō)列,還(hái)可(kě)以是(✔γ"shì)Group。具體(tǐ)的(de)實例如(rú)下(xià):♥∑∏;

這(zhè)些(xiē)元素到(dào)底在JaserReport的(de)XML設計 ×£(jì)文(wén)件(jiàn)中的(de)定義為(wèi)何我并不(bù)想關心≈≈'ff0c;因為(wèi)這(zhè)都(dōu)由iReport←$負責操心了(le),我們隻需輕松的(de)像搭積木(mù)一(yī)樣利用(yòng∞★↓£)iReport添加各種可(kě)視(shì)化(huà)元素就(jiù)可(kě)以了(±&•le)。相(xiàng)信用(yòng)過之後你(nǐ)會(h₹ uì)對(duì)iReport愛(ài)不(bù)釋手,就(jiù)像我♠±±✘一(yī)樣。出于實際需要(yào),我會(huì)提供一(yī)個(gè)簡✔單的(de)動态表單的(de)生(shēng)成框架供各位參↕→'™考。
2 Web報(bào)表開(kāi)發
現(xiàn)今的(de)環境是(shì)Web大(dà)行(xíng)♠ε☆其道(dào),一(yī)個(gè)工× ∏¥(gōng)具如(rú)果不(bù)能(néng)融 $入Web功能(néng)就(jiù)無法立足。JasperReport的(¶✔de)開(kāi)發者顯然很(hěn)早就(jiù)意®→₹≈識到(dào)了(le)這(zhè)一(yī)點,所以在Jaspeγ₹©rReport1.0以前就(jiù)加入了(le)支持S∏≠σ©ervlet/JSP的(de)能(néng)力。也(yě)就(jiù)是(shì)說(shu≈₽ō),我們可(kě)以利用(yòng)Se α★rvlet/JSP将生(shēng)成好(hǎo)的(de)報(bào)表導出成HTML↕₹xff08;或PDF/RTF/EXCEL)格♣✔→©式供預覽或導出之用(yòng)。然而唯一(yī)的(de)缺憾在于∞JasperReport并未提供在客戶端直接打印的(de)功能(n>"©éng),而除了(le)使用(yòng)Applet之外(₽↓±wài)我們又(yòu)不(bù)能(néng)直接在客戶端顯®↔÷示JRViewer這(zhè)樣的(de)預覽窗(chuāng)口,如(rú)何解決Ω☆≠這(zhè)些(xiē)問(wèn)題呢(ne)?
2.1環境設置
在Servlet/JSP中使用(yòng)JasperReport無需更多(duō)的(de)設 $¶置,隻需要(yào)将JasperReport所用(yòng)到(dào)的(₹≈"de)jar包放(fàng)入工(gōng)程中的(de)WEB-INF/lib目錄下(xià<")即可(kě)。在程序運行(xíng)期,Servle t/JSP隻需能(néng)夠正确加載報(bào)表文(wén)件(ji¶≤àn),裝填數(shù)據并生(shēng)成JasperPring對₽↑(duì)象,利用(yòng)我下(xià)面給出的(de)導出框架稍加®"↓修改即可(kě)生(shēng)成一(yī)個(gè)帶有(yǒu)HTML/PDF/R↔σλTF/EXCEL導出功能(néng)以及可(kě)将HTML預覽進×☆¶行(xíng)分(fēn)頁的(de)功能(néng)模塊。
2.2報(bào)表預覽框架
<%@page contentType="t☆<αext/html; charset=UTF-8"%>
•®
<%@page import="javax.sγ↑ervlet.*"%>
<%@ page impo€↕>rt="net.sf.jasperreports.engine.*" % ₽>
<%@ page import=∞£♠;"net.sf.jasperreports.engine.util•€♦ .*" %>
<%@ page♣α↕δ import="net.sf.jasperrepor÷↔σ÷ts.engine.export.*" %>±;
<%@ page imp✘ 'ort="net.sf.jasperreports.j2ee.servlets.*★∞" %>
<%@ page imδ↓ port="java.util.*" %>
< ≥%@ page import="ja≈↑va.io.*" %>
<%
JasperPrint jasperPrint = (J£λγasperPrint)session.getAttribute("Jasper÷ ≈ Print");
session.setAttribute(ImageServlet.DEFAULπ®≈T_JASPER_PRINT_SESSION_ATTRIBUTE,jasperPrin¶ t);
String pageTitle = (String)session.geγtAttribute("pageTitle");
∞♣
JRHtmlExporter exporte≠∏→↓r = new JRHtmlExporter();
int p€ ageIndex = 0;
int lastPageInd®ex = 0;
if (jasperPrint.€✘getPages() != null){
↕± lastPageIndex = jasperPrint.getPΩ✔§≤ages().size() - 1;
}
Str↔★ing pageStr = request.getPar±₩ameter("pageIndex"§₩€₽);
try{
if( pageStr != $₽null)
pageIndex = Integer.₽→σ™parseInt(pageStr);
}catc£&h(Exception e){
//e.printStackTracε∑↓¥e();
}
if (pageIndex <©Ω¶♥; 0){
pageIndex = 0;
}
'
if (pageInd✘∑♣ex > lastPageIndex){
page÷¥∑Index = lastPageIndex;
}
φε♥
StringBuffe₹•r sbuffer = new StringBσ↕"uffer();
exporter.setParameter™× (JRExporterParameter.JASPER_PRINT, jasperPrin'≥t);
exporter.setParameter(JRExp★ orterParameter.OUTPUT_STRING_BUFFER, sbuffer) ;
exporter.setParameter(J'εRHtmlExporterParameter.IMAGES_URI,$✔ "ImageServlet?image=≥¶<★");
exporter.setPaφ↓₩₽rameter(JRExporterParameter.≥≥PAGE_INDEX, new Integer(pageIndex));
↔
exporter.setParamete€★σr(JRHtmlExporterParameter.H←≥TML_HEADER, "");
exp∏©★≈orter.setParameter(JRHtmlExporterParameter.BETWE↓★ EN_PAGES_HTML, "");
↔Ω exporter.setParameter(JRHtmlα∞ExporterParameter.HTML_FOOTER, &φ↓#34;");
try{
exporter.↑ exportReport();
}ca→'≥®tch(Exception e){
e.printStackTrace≤™→();
}
%&∞↑εgt;
這(zhè)部分(fēn)代碼用(yòng)于将Servlet生(sh™↔>ēng)成的(de)JasperReport對(duì)象導出成HTML格式✘"< f0c;導出所用(yòng)的(de)Servlet為(wèi)JasperReεγport自(zì)帶的(de)ImageServlet。要(yà↔$o)特别注意的(de)是(shì)我加了(le)顔色部分(fēn)的(de)代碼ÿ§φ0c;即一(yī)定要(yào)向Session變量 →♣'中放(fàng)入一(yī)個(gè)JasperPrint對(duì)象&δ ✘♥#xff0c;其關鍵字為(wèi)“
ImageServlet.DEFAULT_JASPER_PRINT_SESS ≈ΩION_ATTRIBUTE”,這(zhè)樣ImageServl✔¥×et就(jiù)可(kě)以獲取并自(zì)動導出報(bào)表了(le)。
¥β★
<html>
<head≠↕∑©>
<title><%=pageTitle %>'<π≥;</title>
<meta ht₩πtp-equiv="Content-Type" content&ε÷σ♣#61;"text/html; charset=UTF-8€¶34;>
<link rel=✔∑γλ34;stylesheet" type="text/css₩₩4; href="CSS/style.css"∑₽₽>
</head>
<body>≈¶₹♠
<table class=→∏♣×34;titleBarT">
&l☆∏ ♠t;tr>
<td> ÷>>
∑♥&✔ <%=pageTitle %>
¥ " </td>
</tr↓→>
</table>
₽←☆♣
<table width&€®∞≠#61;"98%" cellpadding="0÷♥34; cellspacing="0" bor£φder="0" height="22"&₩♠$gt;
<tr>
δ<td>
&βlt;div class="menu"><a href¶•1;"PdfServlet"><img s≈☆rc="Images/pdf.gif" border="✔÷;0"></a></div>
✘ ↓
<div classπ✔←₹1;"menu"><a href=≤δ34;RtfServlet"><img src="Imag♠'es/word.gif" border="0">&lγ't;/a></div>
♥♠Ω★ <div class="menu"><a h≤≠☆ref="XlsServlet"><img¥≥γφ src="Images/excel.gif" border=&©÷λ#34;0"></a></div>
δδ
$λ↑•<div class="menu&←±#34;><a href="">&★★nbsp; </a>&✔</div>
<div&σ∑∑ gt;
<%
γ↓←≤ if (pageIndex >•®✘>; 0)
{
φγ≠γ
%>
&l>←t;a href="本頁?pageIn" ☆dex=0"><img src="Imaσ♠× ges/FirstPage.gif" bordeδ××r="0"></a>
€σ <a href=&&♥ §#34;本頁?pageIndex=<%×'σ1;pageIndex - 1%>"><img ↑φsrc="Images/PreviousPa₹∏₽ge.gif" ></a>
•×π
<%
}
©
else
↔ε {
%&∞©gt;
<img src="Image≥©£Ωs/FirstPage_disabled.gif" border=♣☆★;"0"/>
<img s≠☆rc="Images/PreviousPage_disabled.gi→∑•$f" border="0"/>
¥₽ <%
}
α☆ if (pageIndex < ↑≈₩lastPageIndex)
{
&♦$σ %>
•< <a href="本頁?pageIndex=<Ω≈%=pageIndex + 1%>"><img s♦☆↔rc="Images/NextPage.gif" ><γ♣★/a>
<a href&>φ#61;"本頁?pageIndex=<%£π↔1;lastPageIndex%>"&α$γ§gt;<img src="Images/Las✔€ <tPage.gif" ></a>
± ♠↓<%
}
<₩< else
{
♣★₽ %>
<img srσ®≈c="Images/NextPage_disabled.gif" §€λborder="0">
←₽ <img src="Images/La≈ ←←stPage_disabled.gif" border=λ'↓↑;"0">
★₹ ÷<%
}
☆₩ %>
</div&<↑'gt;
</td>
λ×♣ </tr>
</table>
這(zhè)段代碼是(shì)将導出成
HTML
的(de)報(bào)表進行(xíng)分(fēn)頁顯示。
<table width="98%" cellpadding&$π×±#61;"0" cellspacingΩ•="0" border="0±✔←">
<tr>
<td wid∑≈¥♦th="50%">&a♥≥mp;nbsp;</td>
<td align=♦↑α;"left">
<%=sbuffer₩₩≥.toString()%>
</td>
≥∏
<td width="50%"Ω→φ;> </td>
•≤ </tr>
</table>
¥
</body>
</html>
這(zσ≈€hè)裡(lǐ)導出報(bào)表內(nèi)容的(de)代碼。φ>₽用(yòng)Tomcat作(zuò)為(wèi)WebCλ'<ontainer,顯示的(de)結果如(rú)下(xi↕ à):

利用(yòng)這(zhè)個(gè)框架我們可(k§₽ě)以輕易的(de)實現(xiàn)自(zì)動分(fēn)頁的(de☆←'π)功能(néng)并将報(bào)表導出成我們想要(∑ ×yào)的(de)格式:如(rú)PDF,Word,♥σ☆Excel的(de)等。

限于篇幅,這(zhè)裡(lǐ)我不(bù)能(né÷≠≠ng)夠展現(xiàn)報(bào)表開(kāi)發的(de)每一(yī)個•'(gè)細節和(hé)過程,但(dàn)是(shì)我已經盡量将Web報(bào)€♠£表開(kāi)發的(de)大(dà)概過程提取出來(lái)ÿ→¥0c;并著(zhe)重介紹數(shù)據源的(de)設 ₩↑λ制(zhì),交叉表的(de)設計(jì),以及W§↑§eb預覽框架這(zhè)些(xiē)相(xiàng)信每個(gè)做(zuò)Web報(bà>£ααo)表的(de)人(rén)都(dōu)會(huì)遇到(dào)的(de)問(wèn)題及其解 ₹×✘決方案,在JasperReport的(de)高(gāo)端使用(yòng≤)文(wén)檔相(xiàng)對(duì)匮乏的(de)情況下(xià),希望≤$Ω我的(de)努力能(néng)給你(nǐ)帶來(lái)一(✔✔>>yī)點幫助。