JasperReports:棘手的部分
如果您使用Java進(jìn)行編程的時間足夠長,則有可能需要為業(yè)務(wù)用戶生成報告。 就我而言,我已經(jīng)看到幾個項(xiàng)目使用JasperReports?庫來生成PDF和其他文件格式的報告。 最近,我榮幸地觀察了Mike和他的團(tuán)隊(duì)使用上述報告庫及其面臨的挑戰(zhàn)。
簡而言之JasperReports
簡而言之,使用JasperReports(JR)生成報告涉及三個步驟:
在Java代碼中,看起來像這樣。
JasperReport compiledReport = JasperCompileManager.compileReport("sample.jrxml"); Map<String, Object> parameters = ...; java.sql.Connection connection = dataSource.getConnection(); try {JasperPrint filledReport = JasperFillManager.fillReport(compiledReport, parameters, connection);JasperExportManager.exportReportToPdf(filledReport, "report.pdf"); } finally {connection.close(); }多虧了facade類,這看起來很簡單。 但是外表可能是騙人的!
鑒于以上代碼段(以及概述的三個步驟),您認(rèn)為哪些部分需要最多的時間和內(nèi)存? (聽起來像面試問題)。
如果您回答(#2)填寫數(shù)據(jù),那是對的! 如果您回答了#3,那也是正確的,因?yàn)?#xff03;3與#2成正比。
恕我直言 ,大多數(shù)在線教程僅顯示簡單的部分。 就JR而言 ,似乎缺少對較困難和棘手的部分的討論。 在這里,與Mike的團(tuán)隊(duì)一起,我們遇到了兩個困難:內(nèi)存不足錯誤和長期運(yùn)行的報告。 使這些困難特別令人難忘的是,它們僅在生產(chǎn)過程中出現(xiàn)(而不在開發(fā)過程中)。 我希望通過共享它們,將來可以避免它們。
內(nèi)存不足錯誤
第一個挑戰(zhàn)是報告內(nèi)存不足。 在開發(fā)過程中,與實(shí)際操作數(shù)據(jù)相比,我們用于運(yùn)行報告的測試數(shù)據(jù)將太小。 因此, 為此設(shè)計 。
在我們的例子中,所有報告都使用JRVirtualizer運(yùn)行。 這樣,當(dāng)達(dá)到內(nèi)存中的頁面/對象最大數(shù)量時,它將刷新到磁盤/文件。
在此過程中,我們還了解到虛擬器需要清理。 否則,周圍會有幾個臨時文件。 而且,只有在報告導(dǎo)出到文件后 ,我們才能清理這些臨時文件。
Map<String, Object> parameters = ...; JRVirtualizer virtualizer = new JRFileVirtualizer(100); try {parameters.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);...... filledReport = JasperFillManager.fillReport(compiledReport, parameters, ...);// cannot cleanup virtualizer at this pointJasperExportManager.exportReportToPdf(filledReport, ...); } finally {virtualizer.cleanup(); }有關(guān)更多信息,請參見Virtualizer Sample – JasperReports 。
請注意,當(dāng)我們在運(yùn)行報表時遇到內(nèi)存不足的錯誤時,JR 并不總是罪魁禍?zhǔn)住?有時,即使在使用JR之前,我們也會遇到內(nèi)存不足錯誤。 我們看到了如何濫用JPA來加載報告的整個數(shù)據(jù)集( Query.getResultList()和TypedQuery.getResultList() )。 同樣,由于數(shù)據(jù)集仍然很小,因此在開發(fā)期間不會顯示該錯誤。 但是,當(dāng)數(shù)據(jù)集太大而無法容納在內(nèi)存中時,我們會遇到內(nèi)存不足錯誤。 我們選擇避免??使用JPA生成報告。 我猜我們只需要等待JPA 2.2的Query.getResultStream()可用即可。 我希望JPA的Query.getResultList()返回Iterable 。 這樣,就有可能一次映射一個實(shí)體,而不是整個結(jié)果集。
現(xiàn)在,避免加載整個數(shù)據(jù)集。 一次加載一個記錄。 在此過程中,我們返回了良好的JDBC。 不錯,JR很好地使用了ResultSet 。
長期運(yùn)行的報告
第二個挑戰(zhàn)是長期運(yùn)行報告。 同樣,在開發(fā)過程中可能不會發(fā)生這種情況。 充其量,將運(yùn)行10秒鐘左右的報告視為冗長。 但是,有了實(shí)際的運(yùn)行數(shù)據(jù),它可以運(yùn)行大約5-10分鐘。 當(dāng)根據(jù)HTTP請求生成報告時,這尤其麻煩。 如果報告可以在超時時間段內(nèi)(通常為60秒或最多5分鐘)開始寫入響應(yīng)輸出流,那么它很有可能被請求用戶(通常是通過瀏覽器)接收。 但是,如果填寫報告需要5分鐘以上的時間,而導(dǎo)出到文件又需要8分鐘,那么用戶將只會看到超時的HTTP請求,并將其記錄為錯誤。 聽起來有點(diǎn)熟?
請記住,報告可能會運(yùn)行幾分鐘。 因此, 為此設(shè)計 。
就我們而言,我們在單獨(dú)的線程上啟動報告。 對于通過HTTP請求觸發(fā)的報告,我們將以一個頁面進(jìn)行響應(yīng),該頁面包含指向所生成報告的鏈接。 這樣可以避免超時問題。 當(dāng)用戶單擊此鏈接而報告尚未完成時,他/她將看到仍在生成報告。 但完成的報告時,他/她就可以看到生成的報告文件。
ExecutorService executorService = ...; ... = executorService.submit(() -> {Map<String, Object> parameters = ...;try {...... filledReport = JasperFillManager.fillReport(compiledReport, parameters, ...);JasperExportManager.exportReportToPdf(filledReport, ...);} finally {...} });我們還必須添加停止/取消運(yùn)行報告的功能。 好東西,JR有檢查Thread.interrupted()代碼。 因此,僅中斷線程將使其停止。 當(dāng)然,您需要編寫一些測試來進(jìn)行驗(yàn)證(期望JRFillInterruptedException和ExportInterruptedException )。
在討論過程中,我們重新發(fā)現(xiàn)了將“監(jiān)聽器”添加到報告生成中的方法(例如FillListener和JRExportProgressMonitor )并為用戶提供一些進(jìn)度信息。
我們還創(chuàng)建了實(shí)用程序測試類,以通過反復(fù)重復(fù)給定的數(shù)據(jù)來生成大量數(shù)據(jù)。 這對于幫助團(tuán)隊(duì)的其他成員開發(fā)專為處理長期運(yùn)行和內(nèi)存不足錯誤而設(shè)計的JR應(yīng)用程序很有用。
進(jìn)一步的設(shè)計考慮
要考慮的另一件事是填寫報告時需要打開和關(guān)閉所需的資源。 這可以是JDBC連接,Hibernate會話,JPA EntityManager或文件輸入流(例如CSV,XML)。 下圖是我的設(shè)計注意事項(xiàng)的粗略草圖。
1. Compiling- - - - - - - - - - - - - -\- - - -\ \ 2. Filling > open-close \- - - -/ resource > swap to file/ 3. Exporting /- - - - - - - - - - - - - -/我們要隔離#2并定義裝飾器,這些裝飾器將打開資源,填充報告并在finally塊中關(guān)閉打開的資源。 打開的資源可能取決于報表中的<queryString>元素(如果存在)。 在某些情況下,如果沒有<queryString>元素,則可能無需打開資源。
<queryString language="hql"><![CDATA[ ... ]]> </queryString> ... <queryString language="csv"><![CDATA[ ... ]]> </queryString>此外,我們還希望將#2和#3組合為一種抽象。 這種單一的抽象使您可以更輕松地進(jìn)行增強(qiáng)裝飾,例如將創(chuàng)建的頁面對象刷新為文件,并在導(dǎo)出過程中將其加載回。 如前所述,這就是JRVirtualizer所做的。 但是我們希望使用結(jié)合#2和#3的抽象對對象透明的設(shè)計。
致謝
目前為止就這樣了。 再次感謝Mike和他的團(tuán)隊(duì)分享了他們的經(jīng)驗(yàn)。 是的,他是將自己應(yīng)用的收入捐贈給慈善機(jī)構(gòu)的那個人 。 另外,還要感謝克萊爾(Claire)通過一次又一次重復(fù)給定數(shù)據(jù)進(jìn)行測試的想法。 相關(guān)代碼段可以在GitHub上找到 。
翻譯自: https://www.javacodegeeks.com/2018/01/jasperreports-tricky-parts.html
總結(jié)
以上是生活随笔為你收集整理的JasperReports:棘手的部分的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux修改权限的命令(linux修改
- 下一篇: flac安卓车载能播放吗(flac安卓)