一般的使用法,就像我以前做的
1. 在iReport中寫死SQL查詢句(
SELECT xxx FROM yyy WHERE zzz
),然後靠JDBC自動抓取db的內容來設定field,然後把那些Field丟進表單中2. WHERE句所需要的最底限的資料靠HashMap丟進jasper中,讓jasper在跑的時候自動抓取資料庫內容
HashMap parameters = new HashMap(); parameters.put("FIELD1", something); JasperFillManager.fillReportToFile(JASPER檔案路徑, parameters, 某某資料庫的Connection);
但是上面的程序對DB不是很友善。
一般來說,我們再輸出之前都會想確定一下內容寫了什麼,所以會把資料先從資料庫叫出來一次。加上關聯式資料庫方式,一般來說都會JOIN一大堆內容。可是老實說,JOIN對資料庫非常的花時間(至少在MYSQL上是這樣的)
既然已經把db的內容叫出來了,那何必再叫一次呢?
程式流程會變成下面這樣
1. Java向DB查詢資料
2. DB將資料傳給Java程式
3. Java程式將所需要的資料填進jasper生成jrprint文件
一般來說,jrprint出來之後要轉成pdf或是其他的資料型式就不成問題了
但是你會說:用Parameter也可以把資料傳進去阿
是阿,沒錯,但是那樣的話parameter會變成一長串,程式沒有美感也沒有程度可言
而且如果你是從SQL轉過來的,光是改原來的report裡面的欄位就改到手酸
從
$F{FIELDS}
要改成$P{FIELDS}
那我們怎麼弄呢?很簡單,把
Connection
改成Datasource
,然後把原來Java裡面的內容靠Datasource丟進去。詳細就照著作比較快。我們用個班級的個人成績表作範例。個人成績表需要有個人姓名,學號,以及個人的各科成績。其中姓名與學號是每張單都不同的,而成績是靠學號來分辨,另外記在其他資料庫裡的。
1. 建立
Datasource
用的Javabean。在這個例子裡,我叫他Student
public class Student { private String name; private String serialNo; private List scores; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSerialNo() { return serialNo; } public void setSerialNo(String serialNo) { this.serialNo = serialNo; } public List getScores() { return scores; } public void setScores(List scores) { this.scores = scores; } }
我們還需要另外一個JavaBean來紀錄各科成績,姑且叫他Score
public class Score { private Integer point; private String name; public Integer getPoint() { return point; } public void setPoint(Integer point) { this.point = point; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
讓我們做個假資料來放上面的東西
public class ContentFactory { static public List create(){ Listfakelist = new ArrayList<>(); Student fakeStudent1 = new Student(); fakeStudent1.setName("孫甲"); fakeStudent1.setSerialNo("A001"); List fakeScoreList1 = new ArrayList<>(); Score fakeScore1 = new Score(); fakeScore1.setName("國語"); fakeScore1.setPoint(76); Score fakeScore2 = new Score(); fakeScore2.setName("數學"); fakeScore2.setPoint(100); fakeStudent1.setScores(fakeScoreList1); fakelist.add(fakeStudent1); return fakelist; } }
重頭戲來了,把資料填給jasper吧
JasperFillManager.fillReportToFile("reports/epsSinglePageOutput.jasper", parameters, new JRBeanCollectionDataSource(ContentFactory.create()));
放給Jasper的資料,自然不是那麼容易拿到。接下來就告訴你怎麼設定Jasper去接這些資料。
新建一個JasperReport,資料源留白(Empty datasource)。因為資料源已經從Java程式設定好了。
你會說:Fields裡面都沒有東西,那我要從哪裡叫資料阿?
沒錯,接下來,我們就是要設定Fields。手動設定所有你需要的Field,他們的class也必須符合Java裡面的形式
在我的範例裡面,你需要三個Field:name(java.lang.String),serialNo(java.lang.String),
scores(java.util.List)。他們分別對應了Student的三個變數name, serialNo, scores。
接下來,我們要設定一個副查詢(Sub-Dataset),而這個副查詢是原本主查詢中的資料List。
我們一如往常的在頁面上添加一個表格(Table)。iReport跳出來跟你說:喔,你家沒有副查詢喔,要不要自己加一個阿。
我們不理他,繼續加個空白表格(Just create an empty table)並選擇我們需要的欄位數。在這個例子中,我們需要兩欄。完成後,你會看到表格自動被展開並準備好排版。但是我們暫時還不想理他。
在看一次Inspector,你會看到剛剛自動生成的Dataset(Table Dataset 1), 名字非常難看,但是我們可以不理他(當然,你可以自己把他改成更好看一點)。
檢查Table Dataset裡面的Fields,當然,他還是沒有任何內容。
手動增加我們需要的兩個Field以對應Score的兩個Field:
name(java.lang.String), point(java.lang.Integer)
接下來的一個非常重要:右擊table叫出功能表,點擊Edit table Datasource
要注意:Connection / Datasource Expression是Use datasource expression。
接下來,另一個重頭戲來了。編輯datasource的方程式,讓他變成自動擷取主查詢裡面的變數List
將
new net.sf.jasperreports.engine.JREmptyDataSource(1)
改成
new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{scores})
大功告成。接下來你就可以自由的編輯你的排版,看看剛剛設定的值有沒有問題囉
剛剛設定的各Field也會自動的排進你的jasper囉