Bộ sưu tập

Tạo báo cáo với JasperReports


https://codersontrang.com/2013/06/16/tao-bao-cao-voi-jasperreports/

JasperReports là một trong những bộ thư viện mã nguồn mở của Java được sử dụng phổ biến trong việc tạo ra các file báo cáo với nhiều định dạng khác nhau (.pdf, .csv, .html,..). Bài viết này sẽ giới thiệu qua về JasperReports và cách tạo ra một báo cáo đơn giản.

Trong các ứng dụng, đặc biệt là các ứng dụng làm nhiệm vụ quản lý, thống kê thì việc yêu cầu ứng dụng có thể sinh ra báo cáo dưới các định dạng để có thể in ra là điều khó có thể tránh khỏi. Chút nữa chúng ta sẽ có một ví dụ nho nhỏ về cách tạo ra một report đơn giản liên quan đến việc thống kê các sinh viên có điểm trên trung bình và dưới trung bình trong một lớp học. Nhưng trước tiên chúng ta hãy nói về iReport, một công cụ hữu ích trong việc tạo ra các bản mẫu thiết kế của các báo cáo.

Để phục vụ cho ví dụ minh họa, đầu tiên ta tạo một database gồm hai bảng STUDENT và CLASS như sau

 CREATE DATABASE jasper_report_demo;  

 USE jasper_report_demo;  

 CREATE TABLE CLASS(  
      CLASS_ID NVARCHAR(4),  
      CLASS_NAME NVARCHAR(30),  
      PRIMARY KEY (CLASS_ID)  
 );  

 CREATE TABLE STUDENT(  
      STUDENT_ID INT AUTO_INCREMENT,  
      STUDENT_NAME NVARCHAR(20),  
      MARK DECIMAL,  
      CLASS_ID NVARCHAR(4),  
      PRIMARY KEY (STUDENT_ID)  
 );  

Để tạo ra một báo cáo bằng JasperReports, trước hết chúng ta phải tạo ra một bản mẫu cho báo cáo đó. Sau khi có bản mẫu thì việc tạo ra một báo cáo cụ thể thực chất chỉ ra việc truyền giá trị cho các tham số bên trong bản mẫu. Bản mẫu thiết kế sẽ là một file có đuôi .jrxml với nội dung được thể hiện dưới dạng XML. Vì chỉ là XML nên chúng ta hoàn toàn có thể chỉ dùng notepad mà tạo ra bản mẫu, tuy nhiên việc này rất công phu và tốn thời gian. Nếu không phải là trường hợp nào đó thật đặc biệt thì chúng ta hãy sử dụng công cụ iReport để tiết kiệm công sức và thời gian. iReport cung cấp cho chúng ta một giao diện thiết kế trực diện, thay vì phải viết XML, ta chỉ việc kéo thả và tinh chỉnh các thành phần vào trong bản mẫu. Sau đó công cụ sẽ tự sinh ra file .jrxml cho ta có thể sử dụng trong ứng dụng.

Ta có thể tải iReport tại đây. Trong mục iReport Designer, ta hãy chọn bản cài tương ứng với hệ điều hành mà ta đang dùng rồi tiến hành download về và cài đặt.

Sau khi cài đặt iReport thành công, ta bắt tay vào công việc xây dựng bản mẫu cho báo cáo. Trong iReport ta chọn File –> New, một loạt các bản mẫu hiện ra như hình dưới đây. Ta chọn một trong số chúng để làm bản mẫu cho báo cáo của mình.

jasperreport_1

Chọn Blank A4 –> Launch Report Wizard, đặt tên và vị trí để lưu file .jrxml. Trong ví dụ, ta đặt tên file thiết kế là StudentMark.jrxml, chọn Next sau đó ta sẽ được yêu cầu để chỉ ra kết nối đến database để lấy dữ liệu cho báo cáo.

jasperreport_2

Trong trường hợp chưa có một kết nối nào, ta có thể tạo ra một kết nối mới bằng việc click vào New sau đó chọn loại datasource tương ứng. Ở đây, ta kết nối đến mysql database server nên ta chọn vào mục Database JDBC connection

jasperreport_3

Click Next, sau đó đặt tên cho kết nối này là JasperReportDemo, sau đó chỉ ra các thông số kết nối như là JDBC Driver, JDBC URL, Username và Password.

jasperreport_4

Click Save, sau đó ta được yêu cầu để nhập vào Master Query cho report. Lưu ý là khi sinh ra báo cáo, nội dung của báo cáo sẽ bị lặp lại với số lần bằng số lượng bản ghi sinh ra bởi câu truy vấn master query. Vì vậy, hãy cân nhắc trong việc sử dụng master query với việc đảm bảo rằng số lượng bản ghi sinh ra bởi câu truy vấn là 1. Ở trong trường hợp này ta chọn câu master query là SELECT 1

jasperreport_5

Tiếp theo chọn Next đến khi xuất hiện và chọn Finish. Bản mẫu thiết kế sẽ được tạo và chúng ta tiến hành bắt tay vào thiết kế chi tiết cho bản mẫu này

jasperreport_6

Để ý thấy bản mẫu thiết kế là một trang A4, được chia ra làm nhiều phần như là Title, Page Header, Column Header, Detail 1, Column Footer, Page Footer và Summary. Trong đó các phần nội dung của Page Header, Column Header, Column Footer, Page Footer sẽ bi lặp lại ở các trang của báo cáo. Trong quá trình thiết kế, ta hãy loại bỏ những phần không sử dụng đến bằng cách click chuột phải vào phần đó và chọn Delete Band. Trong ví dụ ta chỉ sử dụng đến phần Title và phần Detail 1, nên bản mẫu của chúng ta sẽ có layout như sau:

jasperreport_7

Bây giờ là đến giai đoạn thiết kế chi tiết các thành phần chi tiết trong báo cáo. Các thành phần gồm có các nhãn, bảng biểu … tất cả các thành phần này nằm trong khung Palette (nếu không thấy khung này ta vào menu Window –> Palette) như hình dưới đây:

jasperreport_8

Ngoài ra, các thành phần bên trong báo cáo còn có thể là các tham số được truyền vào trong quá trình chạy chương trình, hay cũng có thể là một trường nào đó trong kết quả trả về từ một câu truy vấn trên data source. Trong ví dụ này, ta tạo 2 tham biến là classId và className bằng cách bên trong khung Report Inspector, click chuột phải vào mục Parameters –> Add Parameter như hình dưới đây:

jasperreport_9

Tiếp theo ta kéo các thành phần vào trong phần Title của báo cáo, các thành phần tên báo cáo, các nhãn là các static text, một tham số sẽ hiển thị trên bản thiết kế với dạng $P{param name}. Sau bước này, báo cáo sẽ giống như hình dưới đây

jasperreport_10

Trong phần nội dung của báo cáo, ta sẽ hiển thị danh sách các học sinh có điểm trên trung bình và danh sách các học sinh có điểm dưới trung bình. Mỗi một danh sách như thế sẽ được lấy ra từ một data set. Trước hết ta sẽ tạo một data set cho việc truy vấn danh sách các sinh viên có điểm trên trung bình bằng cách click chuột vào StudentMark trong khung Report Inspector sau đó chọn Add Dataset như hình dưới đây

jasperreport_11

Đặt tên cho data set là DataSetStudentAboveAverage, và chọn Create new dataset from a connection or datasource như hình dưới đây:

jasperreport_12

Click Next, sau đó trong panel mới hiện ra, ở mục Connections/ Data Sources ta chọn vào Data source mà ta đã tạo lúc đầu là JasperReportDemo. Trong mục Query ta viết câu truy vấn để lấy ra các sinh viên có điểm lớn hơn 5 như dưới đây

jasperreport_13

Click Next, trong mục tiếp theo ta sẽ lựa chọn các trường sẽ dùng trong dataset, ở đây ta dùng tất cả các trường nên ta chọn >> để cho các trường sẽ sang khung bên tay phải như hình dưới đây:

jasperreport_14

Click Next và sau đó chọn Finish, dataset sẽ được tạo ra và hiển thị ở trong mục Report Inspector. Nếu để ý chúng ta có thể thấy câu truy vấn trong dataset vừa tạo đang là hard code, chúng ta mong muốn dữ liệu trong dataset sẽ là danh sách sinh viên của một lớp mà có class id được truyền vào từ chương trình. Vậy chúng ta tạo một tham số mới trong dataset vừa tạo là dsClassId như sau:

jasperreport_15

Sau khi tạo một tham số, việc tiếp theo là phải sửa câu query trong dataset để nó ăn theo tham số này. Click chuột phải và dataset sau đó chọn Edit Query, sau đó sửa điều kiện câu query thành CLASS_ID = $P{dsClassId} như dưới đây

jasperreport_16

Click OK, bây giờ ta sẽ thiết kế một bảng để có thể hiển thị dữ liệu từ dataset này lên báo cáo. Trong khung Palette, ta chọn Table và sau đó kéo vào phần Detail 1 của báo cáo. Khung Table Wizard sẽ hiện ra, ta chọn dataset cho bảng này chính là DataSetStudentAboveAverage vừa được tạo ở trên như hình dưới đây:

jasperreport_17

Click Next, bước tiếp theo ta chọn các field trong dataset để hiển thị lên table, click Next ở bước này chọn vào mục Use the same connection used to fill the master report. Click Next, và ở bước cuối ta có một số tùy chọn cho cách hiển thị của table như hình dưới đây:

jasperreport_18

Click Finish, sau đó trong phần thiết kế cho table sẽ hiện ra trong tab Table 1. Ở phần này ta có thể tùy chỉnh table như là tên của các cột, độ rộng của các cột, padding, … như hình dưới đây:

jasperreport_19

Bây giờ sang bên tab Main report, việc tiếp theo ta phải làm là map tham số dsClassId của dataset với tham số classId của master datasource mà ta đã tạo ở trên. Click chuột phải vào table mà ta vừa tạo, chọn Edit table datasource. Chọn tab Parameters chọn Add, trong mục Dataset parameter name chọn dsClassId, trong mục Value expression ta nhập $P{classId} như hình dưới đây:

jasperreport_20

Nhấp OK, ta có thể thấy sự ánh xạ giữa các tham số từ dataset với các tham số của master datasource được hiển thị trong tab Parameters như hình dưới đây. Làm tương tự nếu như dataset của chúng ta còn có các tham số khác nữa

jasperreport_21

Làm tương tự các bước ở trên, tạo một dataset mới có tên là DataSetStudentBelowAverage, một table mới để hiển thị danh sách các sinh viên có điểm dưới trung bình. Thêm một số các nhãn vào phần Detail 1 của báo cáo, cuối cùng báo cáo của chúng ta sẽ giống như dưới đây

jasperreport_22

Chú ý là các thành phần ở phía dưới trong nội dung của phần Detail 1 sẽ được đặt lại giá trị của thuộc tính Position Type là Float thay cho giá trị mặc định của chúng là Fix to Relative Top như hình dưới đây. Điều này đảm bảo rằng việc hiển thị các thành phần ở phía dưới sẽ chỉ được bắt đầu khi các thành phần ở phía trên đã hiện ra hoàn toàn nội dung của nó.

jasperreport_23

Bây giờ chúng ta sẽ viết java code để sinh ra một file báo cáo dưới dạng PDF. Dùng Eclipse IDE sau đó tạo ra một Project có cấu trúc giống như hình dưới đây:

jasperreport_24

Thư mục lib lưu tất cả các thư viện cần dùng trong việc tạo report. File StudentMark.jrxml mà chúng ta tạo ở trên sẽ được đặt ở trong một source folder có tên là reports.

Nội dung của file Test.java sẽ giống như dưới đây:

Test.java

 package testjasperreport;  

 import java.io.FileOutputStream;  
 import java.io.OutputStream;  
 import java.sql.Connection;  
 import java.sql.DriverManager;  
 import java.sql.PreparedStatement;  
 import java.sql.ResultSet;  
 import java.util.HashMap;  
 import java.util.Map;  
 import net.sf.jasperreports.engine.JasperCompileManager;  
 import net.sf.jasperreports.engine.JasperExportManager;  
 import net.sf.jasperreports.engine.JasperFillManager;  
 import net.sf.jasperreports.engine.JasperPrint;  
 import net.sf.jasperreports.engine.JasperReport;  

 public class Test {  
      public static void main(String [] args){  
           String targetFolder = "D:/StudentMarks/";  
           Connection con = null;  
           try{  
                String query = "SELECT * FROM CLASS";  
                con = getConnection();  
                PreparedStatement stm = con.prepareStatement(query);  
                ResultSet rs = stm.executeQuery();
  
                while(rs.next()){  
                     String classId = rs.getString("CLASS_ID");  
                     String className = rs.getString("CLASS_NAME");  
                     exportStudentMarkToPdf(classId, className, targetFolder);  
                }  

                rs.close();  
                stm.close();  
           }catch(Exception e){  
                e.printStackTrace();  
           }finally{  
                try{  
                     if(con != null && !con.isClosed()){  
                          con.close();  
                     }  
                }catch(Exception e){  
                     e.printStackTrace();  
                }  
           }  
      }
  
      public static void exportStudentMarkToPdf(String classId, String className, String targetFolder){  
           Connection con = null;  
           try {  
                String source = "reports/StudentMark.jrxml";  
                JasperReport jr = JasperCompileManager.compileReport(source);  

                Map<String, Object> params = new HashMap<String, Object>();  
                params.put("classId", classId);  
                params.put("className", className); 
 
                con = getConnection();  
                JasperPrint jp = JasperFillManager.fillReport(jr, params, con);  
                OutputStream os = new FileOutputStream(targetFolder+"STUDENT_MARK_"+classId+".pdf");  
                JasperExportManager.exportReportToPdfStream(jp, os);  

                os.flush();  
                os.close();  
           } catch (Exception e) {  
                e.printStackTrace();  
           }finally{  
                try{  
                     if(con != null && !con.isClosed()){  
                          con.close();  
                     }  
                }catch(Exception e){  
                     e.printStackTrace();  
                }  
           }  
      }  

      public static Connection getConnection(){  
           Connection conn = null;  
           try {  
                Class.forName("com.mysql.jdbc.Driver");  
                conn = DriverManager.getConnection("jdbc:mysql://localhost/jasper_report_demo","root", "test12345");  
           } catch (Exception e) {  
                e.printStackTrace();  
           }  
           return conn;  
      }  
 }  

Trong đoạn code trên, ta sẽ truy vấn vào database để lấy ra danh sách các lớp và sau đó sẽ sinh ra báo cáo cho từng lớp một. Ta chú ý nội dung của hàm exportStudentMarkToPdf() để có thể nhận biết các bước để sinh ra một báo cáo. Cần phải nhắc lại là việc tạo báo cáo đơn giản là việc ta truyền giá trị vào các tham số bên trong báo cáo. Ở ví dụ này hai tham số ta cần dùng đến đó là classId và className sẽ được đặt vào trong một Map với key là tên của tham số, value là giá trị của tham số tương ứng. Sau đó map này được truyền vào trong API của JasperReports. Nếu như chúng ta truyền thiếu hoặc sai tên một tham số nào đó trong bản mẫu thì một exception sẽ xảy ra trong quá trình tạo báo cáo. Trong chương trình trên ta chỉ ra thư mục D:/StudentMarks/ là thư mục sẽ chứa các file báo cáo được xuất ra. Các file này có định dạng là PDF, mỗi file sẽ là thông tin báo cáo cho một lớp học.

Bây giờ chúng ta chèn một số dữ liệu vào hai bảng STUDENT và CLASS trong database để test chương trình trên như sau:

 INSERT INTO `jasper_report_demo`.`class` (`CLASS_ID`, `CLASS_NAME`)  
 VALUES ('C001','Class Of Computer Science');  
 INSERT INTO `jasper_report_demo`.`class` (`CLASS_ID`, `CLASS_NAME`)  
 VALUES ('C002','Class Of Software Engineering');  

 INSERT INTO `jasper_report_demo`.`student` (`STUDENT_NAME`, `MARK`, `CLASS_ID`)  
 VALUES ('Nguyen Van A', 8, 'C001');  
 INSERT INTO `jasper_report_demo`.`student` (`STUDENT_NAME`, `MARK`, `CLASS_ID`)  
 VALUES ('Nguyen Van B', 7, 'C001');  
 INSERT INTO `jasper_report_demo`.`student` (`STUDENT_NAME`, `MARK`, `CLASS_ID`)  
 VALUES ('Nguyen Van C', 6, 'C001');  
 INSERT INTO `jasper_report_demo`.`student` (`STUDENT_NAME`, `MARK`, `CLASS_ID`)  
 VALUES ('Nguyen Van D', 4, 'C001');  
 INSERT INTO `jasper_report_demo`.`student` (`STUDENT_NAME`, `MARK`, `CLASS_ID`)  
 VALUES ('Nguyen Van E', 3, 'C001');  

 INSERT INTO `jasper_report_demo`.`student` (`STUDENT_NAME`, `MARK`, `CLASS_ID`)  
 VALUES ('Nguyen Van F', 10, 'C002');  
 INSERT INTO `jasper_report_demo`.`student` (`STUDENT_NAME`, `MARK`, `CLASS_ID`)  
 VALUES ('Nguyen Van G', 3, 'C002');  

Sau đó tiến hành chạy file Test.java. Vào trong thư mục D:/StudentMarks/ ta sẽ thấy hai file báo cáo tương ứng với hai lớp học có mã là C001 và C002 như hình dưới đây:

jasperreport_25

Mở từng file để kiểm tra dữ liệu và xem báo cáo được xuất ra. Nội dung hai file báo cáo lần lượt như dưới đây:

STUDENT_MARK_C001.pdf

jasperreport_26

STUDENT_MARK_C001.pdf

jasperreport_27

Chú ý: có một số tùy chỉnh trong quá trình thiết kế báo cáo như sau

1. Mặc định, khi không có dữ liệu ở các câu truy vấn thì báo cáo sẽ hiện ra là một trang giấy trắng, ta có thể chỉnh sửa thuộc tính “When No Data” của báo cáo để đổi giá trị của nó từ “No Pages” sang “All Sections, No Detail“. Khi đó các phần của báo cáo sẽ được hiển thị ra đầy đủ ngay cả khi bản thân báo cáo không có dữ liệu.

2. Tương tự trong trường hợp của Table, bình thường khi bảng không có dữ liệu thì các tiêu đề của bảng cũng không hiện ra. Ta sửa thuộc tính “When no data type” từ giá trị mặc định là “Blank” sang “All sections, no detail” để đảm bảo khi không có dữ liệu thì bảng vẫn hiển thị đầy đủ tiêu đền gồm các tên cột của nó.

3. Khi dữ liệu quá dài so với kích thước của một ô trong bảng, theo mặc định, phần dữ liệu thừa sẽ bị cắt đi. Để đảm bảo dữ liệu được hiển thị nguyên vẹn ta chọn vào thuộc tính “Stretch With Overflow” như hình dưới đây. Khi đó phần dữ liệu tràn sẽ được đẩy xuống dòng dưới.

jasperreport_28

Như vậy bài viết này đã chỉ ra các bước để tạo ra một báo cáo đơn giản với Jasper Report. Vì trong phạm vị bài viết không thể nói hết, nên chỉ lấy ví dụ về việc xuất ra file báo cáo dưới định dạng PDF. JasperReports còn cung cấp rất nhiều API khác để xuất ra các file báo cáo dưới các định dạng khác nữa. Với những gì bài viết cung cấp, hi vọng bạn đọc sẽ có được những gì là cơ bản nhất để có thể tự tìm hiểu sâu hơn về JasperReports.

Good luck!

10 comments on “Tạo báo cáo với JasperReports

  1. bạn cho mình hỏi, mình gặp lỗi này không biết là lỗi gì?

    net.sf.jasperreports.engine.JRRuntimeException: Could not resolve style(s): table 1, table
    at net.sf.jasperreports.engine.fill.JRFillObjectFactory.checkUnresolvedReferences(JRFillObjectFactory.java:1577)
    at net.sf.jasperreports.engine.fill.JRFillObjectFactory.setStyles(JRFillObjectFactory.java:1504)
    at net.sf.jasperreports.engine.fill.JRBaseFiller.loadStyles(JRBaseFiller.java:913)
    at net.sf.jasperreports.engine.fill.JRBaseFiller.fill(JRBaseFiller.java:804)
    at net.sf.jasperreports.engine.fill.JRBaseFiller.fill(JRBaseFiller.java:746)
    at net.sf.jasperreports.engine.fill.JRFiller.fillReport(JRFiller.java:58)
    at net.sf.jasperreports.engine.JasperFillManager.fillReport(JasperFillManager.java:417)
    at testjasperreport.Test.exportStudentMarkToPdf(Test.java:67)
    at testjasperreport.Test.main(Test.java:33)

    Số lượt thích

  2. thanks! ở bài trên cho minh danh sách của bang trên la:
    STUDENT_ID STUDENT_NAME MARK
    1 LE VAN A 8
    2 NGUYEN VAN B 9
    3 VU THI C 7
    cho em hỏi giờ muốn bảng được xuất ra thể này thì phải làm sao:

    STUDENT_ID 1
    STUDENT_NAME LE VAN A
    MARK 9
    chỉ xuát duy nhất 1 hang dọc vây thôi thì phải làm sao .. em đang cần gấp.. Mong anh chị giúp đở. thanks!

    Số lượt thích

  3. thanks! ở bài trên cho minh danh sách của bang trên la:
    STUDENT_ID STUDENT_NAME MARK
    1 LE VAN A 8
    2 NGUYEN VAN B 9
    3 VU THI C 7
    cho em hỏi giờ muốn bảng được xuất ra thể này thì phải làm sao:

    STUDENT_ID 1
    STUDENT_NAME LE VAN A
    MARK 9
    chỉ xuát duy nhất 1 hang dọc vây thôi thì phải làm sao .. em đang cần gấp.. Mong anh chị giúp đở. thanks! z

    Số lượt thích

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất /  Thay đổi )

Google photo

Bạn đang bình luận bằng tài khoản Google Đăng xuất /  Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất /  Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất /  Thay đổi )

Connecting to %s