posts - 310, comments - 6939, trackbacks - 0, articles - 3
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          使用JasperReport與iBATIS開發Web報表

          Posted on 2008-01-25 09:32 詩特林 閱讀(7007) 評論(10)  編輯  收藏 所屬分類: BI
          應該IT168寫的專稿:http://publish.itpub.net/j/2008-01-24/200801241020641.shtml
           

          使用JasperReportiBATIS開發Web報表

           

          JasperReport是一種采用純Java實現的快速且非常流行的生成報表的類庫。而對于任何的報表方案,取得數據并傳遞給報表引擎是其中最重要且最值得關心的方面。但遺憾的是,在這方面JasperReport本身似乎有一定的不足。而如今的很多Java應用程序,采用數據獲取框架來進行數據的匹配與動態生成SQL。例如iBATIS數據映射框架。當然,如果只是使用JasperReport獲取數據及管理數據的默認機制的話,不足以與現成的數據框架進行很好的平衡。但可喜的是,可以通過使用傳遞給JasperReport一個數據庫的連接進行代替,當然這種連接可以通過使用XML進行非常方便的管理與配置。

          源代碼下載:http://cid-7b377ace522ff6c7.skydrive.live.com/self.aspx/iBatisJasper/iBatisJasper.rar

          一、準備工作

          Hibernate類似,iBATIS也是一個ORM解決方案,不同的是兩者各有側重。Hibernate提供了Java對象到數據庫表之間的直接映射,開發者無需直接涉及數據庫操作的實現細節,實現了一站式的ORM解決方案。而iBATIS則采取了另一種方式,即提供Java對象到SQL(面向參數和結果集)的映射實現,實際的數據庫操作需要通過手動編寫SQL實現。

          iBATIS是又一個O/R Mapping解決方案,j2eeO/R方案真是多,和Hibernate相比,iBATIS最大的特點就是小巧,上手很快。如果你不需要太多復雜的功能,iBATIS是能滿足你的要求又足夠靈活的最簡單的解決方案。在本文的示例中,采用Spring+JSF+iBATIS的模式進行示例的開發。所使用的lib如下圖所示:


          1.所使用的jar

          二、在iReport中可視化定制模板

          定制報表格式有二種方式,一種就是寫jrxml文件,其實就是xml文件,只不過是后綴名不一樣罷了。另一種方式更直接,就是生成一個JasperDesign類的實例,在japsperDesign中自己定義模板。jrxml文件也是通過一個JRXmlLoad加載過來,轉成JasperDesign類的實例。也就是說寫jrxml文件還需要進行解析,加載。現實中我們使用的報表一般格式比較固定,因而可以通過先使用iReport工具生成模板,再加載解析的方式。這種方式簡單,而且可見性強。

          iReport做為一個優秀的報表設計器,有著功能非常強大的特性。作為開源的Java程序,不但有適合于Windows安裝的應用程序,同時,還提供完全開放的源代碼,可供參考及原理分析。在本文中,主要通過圖形界面中的模板設計,以及與數據庫的連接等一系列的操作,來介紹如何定制一定要求的報表模板。

          通過iReport可初見化的圖形界面,可以設計出各種各樣的簡單或復雜的報表。通過iReport的這種可視化界面設計,可以為JasperReport提供優秀的報表模板,而無須去理解或是掌握那些復雜的XML語法。如此則可以Web報表開發節省大量的開發時間。

          在進行iReport模板設計之前,需要編寫JavaBean類:MonthlySalesBean.java,代碼如下:

          import java.math.BigDecimal;
          import java.util.ArrayList;
          import java.util.List;


          public class MonthlySalesBean {

              
          private int employeeID;;
              
          private String last = null;
              
          private String first = null;
              
          private BigDecimal total = null;
              
          private List sales = null;
              
          private LatestSale latestSale = null;
              
              
          public static List createBeanCollection () {
                  List list 
          = new ArrayList ();
                  
                  MonthlySalesBean msb 
          = new MonthlySalesBean ();
                  msb.setEmployeeID(
          1);
                  msb.setFirst(
          "John");
                  msb.setLast(
          "Doe");
                  msb.setTotal(
          new BigDecimal ("1600.50"));
                  
                  LatestSale ls 
          = new LatestSale ();
                  ls.setAmount(
          new BigDecimal ("32.21"));
                  msb.setLatestSale(ls);
                  
                  list.add(msb);
                  
                  
          return list;
              }

              
          public int getEmployeeID() {
                  
          return employeeID;
              }

              
          public void setEmployeeID(int employeeID) {
                  
          this.employeeID = employeeID;
              }

              
          public String getFirst() {
                  
          return first;
              }

              
          public void setFirst(String first) {
                  
          this.first = first;
              }

              
          public String getLast() {
                  
          return last;
              }

              
          public void setLast(String last) {
                  
          this.last = last;
              }

              
          public BigDecimal getTotal() {
                  
          return total;
              }

              
          public void setTotal(BigDecimal total) {
                  
          this.total = total;
              }

              
          public List getSales() {
                  
          return sales;
              }

              
          public void setSales(List sales) {
                  
          this.sales = sales;
              }

              
          public LatestSale getLatestSale() {
                  
          return latestSale;
              }

              
          public void setLatestSale(LatestSale latestSale) {
                  
          this.latestSale = latestSale;
              }
              
          }


           

          將上面的類打成一個jar包,并置于classpath目錄下,則iReport可以進行訪問。使用JavaBean做為數據源,為了在設計報表時能夠看到數據,在程序中要為iReport提供一個靜態方法,該方法返回上面定義JavaBean的一個結果集,這個靜態方法可能在程序運行中并不是必須的,但是在iReport中它確實必須的,換句話說,這個靜態方法是專門為iReport量身定做的,為了iReport在設計報表時能夠調用這個靜態方法返回相應的JavaBean結果集,以便設計的報表放在Java項目中之前就能像使用SQL數據庫數據源一樣可以瀏覽。在iReport中先進行數據源的連接配置,此處采用是JavaBeans set data source連接方式:

          2.iReport進行數據源的連接

           

          三、處理iBati返回數據

           

          如果iBATIS沒有采用JavaBean作為返回對象,則可以采用java.util.map作為數據的返回對象。采用java.util.Map對象,需要額外的一些步驟。下面的代碼則說明了iBATISselect語句返回的java.util.Map對象。Src/ iBATIS.xml

          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 1.0//EN"
              "http://iBATIS.apache.org/dtd/sql-map-2.dtd"
          >
          <sqlMap>

              
          <select id="salesByListOfMapsSQL" resultClass="java.util.HashMap">
                  SELECT
                      E.EMPLOYEE_ID "ID",
                      E.FIRST_NAME "FIRST",
                      E.LAST_NAME "LAST",
                      MS.TOTAL_SALES "TOTAL",
                      MS.LATEST_SALE
                  FROM
                      EMPLOYEE E,
                      MONTHLY_SALES MS
                  WHERE
                      E.EMPLOYEE_ID = MS.EMPLOYEE_ID
                      AND MS.MONTH = #value#
              
          </select>
              
              
          <resultMap id="searchResultList" class="MonthlySalesBean">
                      
          <result property="employeeID" column="ID"/>
                      
          <result property="first" column="FIRST"/>
                      
          <result property="last" column="LAST"/>
                      
          <result property="total" column="TOTAL"/>
                      
          <result property="latestSale.amount" column="LATEST_SALE"/>
              
          </resultMap>    
              
              
          <select id="salesByJavaBeansSQL" resultMap="searchResultList">
                  SELECT
                      E.EMPLOYEE_ID "ID",
                      E.FIRST_NAME "FIRST",
                      E.LAST_NAME "LAST",
                      MS.TOTAL_SALES "TOTAL",
                      MS.LATEST_SALE
                  FROM
                      EMPLOYEE E,
                      MONTHLY_SALES MS
                  WHERE
                      E.EMPLOYEE_ID = MS.EMPLOYEE_ID
                      AND MS.MONTH = #value#
              
          </select>    
          </sqlMap>

           

          上面的代碼返回的對象即為map對象。請注意,map對象中的Key值直接來自于select語句,因此,像TO_CHARMS.TOTAL_SALES)這樣的表達式在報表中不提倡使用。因此,比較人性化的為字段命名,是一件很值得的事情。因為mapkey值是作為java.lang.Object類型來進行存儲的,因此有必要對字段返回類型進行一下整理。

          真正的數據填充類應該是ServiceLocatorBean.java類,其代碼如下所示:

          import java.sql.Connection;
          import java.sql.SQLException;
          import java.sql.Statement;

          import javax.servlet.ServletContext;

          import org.springframework.context.ApplicationContext;
          import org.springframework.web.context.support.WebApplicationContextUtils;

          public class ServiceLocatorBean implements ServiceLocatorIF {
              
              
          private static final long serialVersionUID = -7166271873610635886L;

              
          //the Spring application context
              private ApplicationContext appContext;
                  
              DAO dao 
          = null;
              
              
          public ServiceLocatorBean() {
                  
                  
          try {
                  
                      
          // get the spring context
                      ServletContext context = FacesUtils.getServletContext();
                      
          this.appContext = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
                      
                      
          // create instance of the business object
                      this.dao = (DAO) this.lookupService("dao");
                      
                      Connection conn 
          = this.dao.getSqlMapClient().getDataSource().getConnection();
                      
                      conn.setAutoCommit(
          false);
              
                      
          /*
                         Creating a statement lets us issue commands against
                         the connection.
                       
          */

                      Statement s 
          = conn.createStatement();
                      
                      
          // just in case old tables from prior run (after first run which
                      
          // will create the USER1 schema)
                      try {
                          s.execute(
          "drop table employee");
                          s.execute(
          "drop table monthly_sales");
                      }
           catch (Exception ex) {
                          
          // not to be concerned (at least in this example
                      }

              
                      
          /*
                         We create a table, add a few rows, and update one.
                       
          */

                      s.execute(
          "create table employee (employee_id int, first_name varchar(40), last_name varchar(40))");
                      
                      s.execute(
          "insert into employee values (1,'sterning', 'chen')");
                      s.execute(
          "insert into employee values (2,'yuxuan', 'Wand')");
                      s.execute(
          "insert into employee values (3,'Mickey', 'Li')"); 
                      
                      s.execute(
          "create table monthly_sales (employee_id int, total_sales numeric(16, 2), latest_sale numeric(8, 2), month int)");
                      
                      s.execute(
          "insert into monthly_sales values (1, 1600.50, 32.50, 1)");
                      s.execute(
          "insert into monthly_sales values (2, 1544.20, 12.50, 1)");
                      s.execute(
          "insert into monthly_sales values (3, 18814.80, 78.65, 1)");
                      
                      s.execute(
          "insert into monthly_sales values (1, 1450.50, 10.65, 2)");
                      s.execute(
          "insert into monthly_sales values (2, 2004.25, 52.10, 2)");
                      s.execute(
          "insert into monthly_sales values (3, 9819.00, 40.65, 2)"); 
                      
                      s.close();
                      conn.commit();            
                  
                  }
           catch (SQLException sqle) {
                      
          // just means the tables already exist
                      sqle.printStackTrace();
                  }
           catch (Exception ex) {
                      ex.printStackTrace();
                  }

                  
              }
              
              
              
          public DAO getDao() {
                  
          return this.dao;
              }

              
              
          public Object lookupService(String serviceBeanName) {
                  
          return appContext.getBean(serviceBeanName);
              }


          }


           

          四、將iBATIS數據填入JasperReport

          就通常而言,采用Java Bean作為iBATIS的返回對象,相比起java.util.Map對象來說,更加的方便與可行。很多的開發人員采用iBATIS的這種方式來進行數據的映射,同時,此方法還可以無縫的將iBATISJapserReport集成起來。

          JasperReport中,提供了一個JRDataSource的實現,從而開發人員可以通過此類來傳遞iBATISlist對象給JasperReport模板。而JRBeanCollectionDataSource類使用JavaBean來構造,從而可以通過循環查找collection并獲得相應的bean屬性。如下的代碼示例說明了如何在調用JasperReport引擎時實例化JRBeanCollectionDataSource對象。

          import java.io.File;
          import java.util.HashMap;
          import java.util.List;

          import net.sf.jasperreports.engine.JRRuntimeException;
          import net.sf.jasperreports.engine.JasperFillManager;
          import net.sf.jasperreports.engine.JasperPrint;
          import net.sf.jasperreports.engine.JasperReport;
          import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
          import net.sf.jasperreports.engine.util.JRLoader;


          public class SearchBean {
              
              
          private final static String JAVA_BEAN_REPORT = "monthly_sales_java_beans.jasper";
              
          private final static String LIST_OF_MAP_REPORT = "monthly_sales_list_of_maps.jasper"

              
          public String generateFromJavaBeans () {
                  
                  
          try {
                      ServiceLocatorIF sl 
          = (ServiceLocatorIF) FacesUtils
                          .getManagedBean(
          "serviceLocatorBean");
                      
                      List list 
          = sl.getDao().getSqlMapClient().queryForList("salesByJavaBeansSQL", month);
                      
                      FacesUtils.setSessionAttribute(
          "JASPER_PRINT", generateReport (list, JAVA_BEAN_REPORT));
                      
                      viewReport 
          = "true";
                  }
           catch (Exception ex) {
                      ex.printStackTrace();
                  }

                  
                  
          return null;
              }

              
              
          public String generateFromListOfMaps () {
                  
                  
          try {
                      ServiceLocatorIF sl 
          = (ServiceLocatorIF) FacesUtils
                          .getManagedBean(
          "serviceLocatorBean");    
                      
                      List list 
          = sl.getDao().getSqlMapClient().queryForList("salesByListOfMapsSQL", month);
                      
                      FacesUtils.setSessionAttribute(
          "JASPER_PRINT", generateReport (list, LIST_OF_MAP_REPORT));
                      
                      viewReport 
          = "true";
                  }
           catch (Exception ex) {
                      ex.printStackTrace();
                  }

                  
                  
          return null;
              }


              
          private JasperPrint generateReport (List dataList, String reportName) {
                  JasperPrint jasperPrint 
          = null;
                  
                  
          try {
                      
                      String localPath 
          = FacesUtils.getServletContext().getRealPath("/");
                      
                      File reportFile 
          = new File(localPath + "WEB-INF" + File.separator + reportName);
                      
                      
          if (!reportFile.exists())
                          
          throw new JRRuntimeException(".jasper file not found. The report design must be compiled first.");
                                  
                      JasperReport jasperReport 
          = (JasperReport)JRLoader.loadObject(reportFile.getPath());
                      
                      
          if (reportName.equals(JAVA_BEAN_REPORT)) {
                          
                          jasperPrint 
          = JasperFillManager.fillReport(
                                  jasperReport,
                                  
          new HashMap(), 
                                  
          new JRBeanCollectionDataSource (dataList));        
                          
                      }
           else {
                          
                          jasperPrint 
          = JasperFillManager.fillReport(
                                  jasperReport,
                                  
          new HashMap(), 
                                  
          new CustomJRDS (dataList));
                          
                      }

                  
                  }
           catch (Exception ex) {
                      ex.printStackTrace();
                  }

                  
                  
          return jasperPrint;
              }
              

              
          public String getMonth() {
                  
          return month;
              }


              
          public void setMonth(String month) {
                  
          this.month = month;
              }


              
          public String getViewReport() {
                  
          return viewReport;
              }


              
          public void setViewReport(String viewReport) {
                  
          this.viewReport = viewReport;
              }

              
              
          private String month = null;
              
          private String viewReport = null;    
          }


           

          在上面的代碼中,定義的參數map,是在運行時傳遞相關的參數值給JasperReport。例如,可以在報表模板中定義一個名為REPORT_TITLE的參數,然后在運行時傳遞這一參數的值給它,傳遞的方式一般是健/值對的形式。例如Key=REPORT_TITLEValue=Sale Report。當然,參數是傳遞給fillReport方法。然后,JasperReport會加載已經編譯好的Jasper模板文件(.jasper)。最后調用靜態的fillReport方法。

          JasperPrint對象是在數據展示或顯示時需要用到的。而在本例中,采用了JRPdfExporter來作為輸出的格式,即輸出為PDF格式文件,請參考PdfServlet.java文件,代碼如下所示:

          import java.io.ByteArrayOutputStream;
          import java.io.IOException;
          import java.util.ArrayList;
          import java.util.List;

          import javax.servlet.ServletException;
          import javax.servlet.ServletOutputStream;
          import javax.servlet.http.HttpServlet;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;

          import net.sf.jasperreports.engine.JRException;
          import net.sf.jasperreports.engine.JRExporterParameter;
          import net.sf.jasperreports.engine.JasperPrint;
          import net.sf.jasperreports.engine.export.JRPdfExporter;


          public class PdfServlet extends HttpServlet {

              
          public void service(HttpServletRequest request, HttpServletResponse response)
                      
          throws IOException, ServletException {

                  JasperPrint jasperPrint 
          = (JasperPrint) request.getSession()
                          .getAttribute(
          "JASPER_PRINT");

                  List jasperPrintList 
          = new ArrayList();

                  jasperPrintList.add(jasperPrint);

                  JRPdfExporter exporter 
          = new JRPdfExporter();
                  exporter.setParameter(JRExporterParameter.JASPER_PRINT_LIST,
                          jasperPrintList);

                  ByteArrayOutputStream baos 
          = new ByteArrayOutputStream();
                  exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos);

                  
          try {
                      exporter.exportReport();
                  }
           catch (JRException e) {
                      
          throw new ServletException(e);
                  }


                  
          byte[] bytes = baos.toByteArray();

                  
          if (bytes != null && bytes.length > 0{
                      response.setContentType(
          "application/pdf");
                      response.setContentLength(bytes.length);
                      ServletOutputStream ouputStream 
          = response.getOutputStream();

                      
          try {
                          ouputStream.write(bytes, 
          0, bytes.length);
                          ouputStream.flush();
                      }
           finally {
                          
          if (ouputStream != null{
                              
          try {
                                  ouputStream.close();
                              }
           catch (IOException ex) {
                              }

                          }

                      }

                  }

              }

          }


           

          盡管上面的JasperReport機制可以將iBATIS連接起來,但應該根據項目報表的需要對JavaBean進行修改與調整。而JasperReport字段對象可以很好的與普通的JDBC字段進行匹配。例如,JasperReportOraclenumeric字段類型對應的轉成java.math.BigDecimal對象類型。而在iBATISBean屬性應該與JasperReport中定義的字段類型進行很好的匹配。需要對字段的類型進行認真仔細的選擇,因為不同類型或是不同表達式對數據的展示有不同的效果。例如,BigDecimal類型比String類型更加適合貨幣格式。

          五、代碼運行效果

          1.系統主界面

           


          3.報表運行主界面

          2.采用JavaBean生成報表


          4.采用JavaBean生成報表

          六、小結

          在本文中,筆者展示了如何使用比較成熟的iBATIS數據框架來對JasperReport進行數據填充。iBATIS最大的特點是簡單,而iBATIS所擁有的易維護及易配置特性,在JasperReport中充分的體現出來了。這種簡單與靈活性,正好彌補了JasperReport在這方面的不足,從而達到靈活開發Web報表的目的。



          評論

          # re: 使用JasperReport與iBATIS開發Web報表  回復  更多評論   

          2008-01-25 11:26 by ci
          不錯....

          # re: 使用JasperReport與iBATIS開發Web報表  回復  更多評論   

          2008-01-25 14:55 by cnfox
          rar文件被破壞?

          # re: 使用JasperReport與iBATIS開發Web報表  回復  更多評論   

          2008-01-26 19:48 by julycoolboy
          大概看了一下,博主做的不錯,我來談談我使用JR的心得,JR本身分為推和拉的方式來填充模板,使用BEAN做為數據源,就是先取出數據后推到JR中去,我這里返回的是一個自定義Iterator,里面使用的ResultSet,這個好處就是不會一次性把數據取出來,生成報表的時候會一條條取,這樣就能突破推方式的數據量極限了(正常方式下,一個萬條數據的LIST,我們的開發PC都容易內存溢出)

          # re: 使用JasperReport與iBATIS開發Web報表  回復  更多評論   

          2008-04-30 16:38 by regale
          不錯....

          # re: 使用JasperReport與iBATIS開發Web報表  回復  更多評論   

          2008-07-02 17:14 by wanxinge
          發一份給我可以嗎?萬分感謝啊,下載rar被破壞。我的郵箱:wangxinge_5689@163.com

          # re: 使用JasperReport與iBATIS開發Web報表  回復  更多評論   

          2008-12-05 11:52 by ireport
          發份我,不勝感激!
          lujuju@qq.com

          # re: 使用JasperReport與iBATIS開發Web報表  回復  更多評論   

          2008-12-26 04:09 by renbao
          我也需要,謝謝22624223@qq.com

          # re: 使用JasperReport與iBATIS開發Web報表  回復  更多評論   

          2009-10-07 03:00 by Wonner
          千萬不要用iBATIS.

          # re: 使用JasperReport與iBATIS開發Web報表  回復  更多評論   

          2014-11-26 16:10 by 阿浪
          正在學習這個,想參考一下,能發一份給我嗎?
          1572669095@qq.com

          # re: 使用JasperReport與iBATIS開發Web報表  回復  更多評論   

          2015-01-16 16:42 by hanck
          學習中,望前輩們多多指導啊,可以發一份給我嗎?不勝感激!
          389610110@qq.com
          主站蜘蛛池模板: 开化县| 商都县| 咸丰县| 巩留县| 准格尔旗| 浦东新区| 杭锦后旗| 乳山市| 井陉县| 黎川县| 宝鸡市| 化德县| 建昌县| 洛阳市| 麻江县| 屯昌县| 云安县| 迭部县| 黔西县| 尤溪县| 繁峙县| 陵川县| 香河县| 澄城县| 荣成市| 尉犁县| 揭阳市| 嘉定区| 南汇区| 宣威市| 兴山县| 兴安盟| 禹城市| 松原市| 大冶市| 台江县| 西乌| 淳安县| 黄山市| 靖江市| 延川县|