SyntaxHighlighter

2012-11-26

如何iReport中簡單的設定List成為Datasource

大多數人使用iReport生成都會使用SQL來抓取資料庫檔案,但是iReport畢竟只是個設計表單用的程式,資料還是要靠Java餵給他。

一般的使用法,就像我以前做的
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(){
        List fakelist = 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

一般來說,Datasource會被設定到剛剛的Table Dataset 1上,就像下圖一樣

要注意: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囉

0 件のコメント:

人気の投稿