IBM®
本文へジャンプ
    Japan [変更]    ご利用条件
 
 
検索範囲検索:    
    ホーム    製品    サービス & ソリューション    サポート & ダウンロード    マイアカウント    
skip to main content

developerWorks Japan  >  Information Management  >

pureXMLでJasperReportsを楽しもう

pureXMLとJasperReportsのXML連携

developerWorks
ページオプション

JavaScript を要するドキュメントオプションは表示されません

ダウンロード


レベル: 中級

小川 直人 (naogawa@fsi.co.jp), 主任, 富士ソフト株式会社

2009年 6月 19日

JasperReportsは、SQLを発行し、その結果をレポーティングすることができます。しかし、XQueryはサポートしていません。簡単な拡張で、JasperReportsでXQueryが使えるようになります。

イントロダクション

JasperReportsはとても優れた帳票ツールです。機能が豊富で、拡張が容易です。たとえば、JasperReportsはデータベースからデータを取得するSQLを指定することができます。JasperReportsにデータベースコネクションを渡せば、指定したSQLを発行し、データを取得し、帳票を作成することができます。これにはJavaコーディングが不要です。しかし、残念ながら、JasperReportsはXQueryを発行することができません。PureXMLに格納されたXML文書から帳票を作るには、XML文書を取得するJavaコーディングが必要ですが、 JasperReportsの強力な拡張機能を利用すれば、JasperReportsでXQueryを扱えるようになります。




上に戻る


システム要件とシナリオ

この記事のソースは、次の環境で実行、テストされています。


Table 1.環境
ModuleVersion
OSWindows XP Professinal Version 2002 Service Pack 3
JavaSUN JVM 1.6.0_13
DatabaseDB2 v9.5.200.315 Fix Pax 2 (Express-C)
JasperReports3.1.2(jasperreports-3.1.2-project.zipのダウンロードをお勧めします。依存するjarが含まれているからです。)

DB2では、SAMPLEデータベースが提供されています。そのデータベースのCustomerテーブルを利用することにします。まず、テーブル定義を確認しましょう。


Listing 1. SAMPLEデータベースのCustomerテーブルの定義

|--------10--------20--------30--------40--------50--------60--------70--------80--------|
db2 =>describe table customer
                                Data type                     Column
Column name                     schema    Data type name      Length     Scale Nulls
------------------------------- --------- ------------------- ---------- ----- ------
CID                             SYSIBM    BIGINT                       8     0 No
INFO                            SYSIBM    XML                          0     0 Yes
HISTORY                         SYSIBM    XML                          0     0 Yes
-------------------------------------------------------------------------------------

            

CustomerテーブルのINFO列に顧客情報がXML型として格納されています。この顧客情報XMLから、顧客ID、顧客名、電話番号、電話タイプを表形式でPDF出力することを考えていきましょう。最終的なレポートは以下に示すFigure 1.のようなPDFになります。


Figure 1. Customerテーブルの情報を一覧化したPDFファイル
顧客情報XMLから、顧客ID、顧客名、電話番号、電話タイプを表形式でPDF出力したレポート



上に戻る


カスタマイズのアイデア

カスタマイズの方法はとてもシンプルです。まず、XQueryステートメントを含む帳票定義ファイル(.jrxmlファイル)を作成します。次にカスタムのQuery Executerを作成します。このQuery Executerで、jrxmlファイルからXQueryを読み込み、発行し、XML文書をデータベースから取得し、その文書をXMLDataSourceに渡します。XMLDataSourceはJasperReportsに実装されていますので、後は実行するのみです。




上に戻る


XPath DataSource

カスタマイズの作業に取り掛かる前に、XMLマッピングについて確認しましょう。次のXQueryをPureXMLで発行するとします。


Listing 2. SAMPLEデータベースから顧客情報を取得するXQuery

|--------10--------20--------30--------40--------50--------60--------70--------80--------|
xquery
let $inst := db2-fn:xmlcolumn("CUSTOMER.INFO")
return <customers>{$inst}
</customers>

            

このXQueryから次のXML文書が取得できます。


Listing 3. Listing 2の結果

|--------10--------20--------30--------40--------50--------60--------70--------80--------|
<?xml version="1.0" encoding="UTF-8" ?>
<customers>
 <customerinfo xmlns="http://posample.org" Cid="1000">
  <name>Kathy Smith</name>
   <addr country="Canada">
    <street>5 Rosewood</street>
    <city>Toronto</city>
    <prov-state>Ontario</prov-state>
    <pcode-zip>M6W 1E6</pcode-zip>
   </addr>
  <phone type="work">416-555-1358</phone>
 </customerinfo>
 <customerinfo xmlns="http://posample.org" Cid="1001">
  <name>Kathy Smith</name>
  <addr country="Canada">
   <street>25 EastCreek</street>
   <city>Markham</city>
   <prov-state>Ontario</prov-state>
   <pcode-zip>N9C 3T6</pcode-zip>
  </addr>
 <phone type="work">905-555-7258</phone>
 </customerinfo>
 
 <!-- 他のcustomerinfoは省略 -->
 
</customers>

            

ここで、Listing 3.と同様なXML文書があり、この文書のファイル名をmydata.xmlと仮定しましょう。このXML文書を帳票にマッピングするために、次のような帳票定義ファイルが必要となります。ここでは詳細には立ち入りませんが、この帳票定義ファイルはiReportを使用して作成しました。


Listing 4. 帳票定義ファイル(reportXPath.jrxml)

|--------10--------20--------30--------40--------50--------60--------70--------80--------|
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports
 http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
 name="report name" pageWidth="595" pageHeight="842" columnWidth="535"
 leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20">
 <queryString language="xPath">                                   <!--    **(1)**    -->
  <![CDATA[/customers/customerinfo]]>
 </queryString>
 <field name="Cid" class="java.lang.String">
  <fieldDescription><![CDATA[@Cid]]></fieldDescription>      <!-- **(2-1)** -->
 </field>
 <field name="name" class="java.lang.String">
  <fieldDescription><![CDATA[normalize-space(name/child::text())]]></fieldDescription>
   <!-- **(2-2)** -->
 </field>
 <field name="phone" class="java.lang.String">
  <fieldDescription><![CDATA[normalize-space(phone/child::text())]]></fieldDescription>
   <!-- **(2-3)** -->
 </field>
 <field name="type" class="java.lang.String">
  <fieldDescription><![CDATA[phone/@type]]></fieldDescription>  <!-- **(2-4)** -->
 </field>
<background>
  <band/>
 </background>
 <title>
  <band height="79"/>
 </title>
 <pageHeader>
  <band height="35"/>
 </pageHeader>
 <columnHeader>
  <band height="61"/>
 </columnHeader>
 <detail>
  <band height="23">
   <textField>
    <reportElement x="97" y="3" width="100" height="20"/>
    <textElement/>
    <textFieldExpression class="java.lang.String"><![CDATA[$F{name}]]>
</textFieldExpression>
   </textField>
   <textField>
    <reportElement x="0" y="3" width="100" height="20"/>
    <textElement/>
    <textFieldExpression class="java.lang.String"><![CDATA[$F{Cid}]]>
</textFieldExpression>
   </textField>
   <textField>
    <reportElement x="197" y="3" width="100" height="20"/>
    <textElement/>
    <textFieldExpression class="java.lang.String"><![CDATA[$F{phone}]]>
</textFieldExpression>
   </textField>
   <textField>
    <reportElement x="297" y="3" width="100" height="20"/>
    <textElement/>
    <textFieldExpression class="java.lang.String"><![CDATA[$F{type}]]>
</textFieldExpression>
   </textField>
  </band>
 </detail>
 <columnFooter>
  <band height="45"/>
 </columnFooter>
 <pageFooter>
  <band height="54"/>
 </pageFooter>
 <summary>
  <band height="42"/>
 </summary>
</jasperReport>

            

帳票定義でポイントとなるのは、上記(1)で示したqueryString要素です。このqueryString要素のlanguage要素の値はxPathです。これは、JasperReportsがXPath用のDataSourceを使うことを意味しています。queryString要素の内容は、XML文書中のどのノードの集合を基準とするかを示しています。この基準からどのノードを取得するかをfieldDescription要素にて指定します(上記(2-1)から(2-4)を参照)。以下に述べるListing 6.でqueryString要素を修正します。

さて、PDFを生成するために、次のようなJavaコードが必要です。


Listing 5. PDF出力するコード(ReportXMLFile.java)

|--------10--------20--------30--------40--------50--------60--------70--------80--------|
package com.example;

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;
import net.sf.jasperreports.engine.query.JRXPathQueryExecuterFactory;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.engine.util.JRXmlUtils;

import org.w3c.dom.Document;

public class ReportXMLFile {

 @SuppressWarnings("unchecked")
 public static void main(String[] args) throws Exception {
  JasperReport jasperReport;
  JasperPrint jasperPrint;
  jasperReport = JasperCompileManager
    .compileReport("reports/reportXPath.jrxml");//---------(1)

  Map params = new HashMap();
  Document document = JRXmlUtils.parse(JRLoader
    .getLocationInputStream("data/mydata.xml"));
  params.put(JRXPathQueryExecuterFactory.PARAMETER_XML_DATA_DOCUMENT,
    document);//-------------------------------------------(2)

  jasperPrint = JasperFillManager.fillReport(jasperReport, params);
  JasperExportManager.exportReportToPdfFile(jasperPrint,
    "reports/pdfXPath.pdf");//-----------------------------(3)

 }

}

            

このコーディングは次の通りになります。

  1. reportXPath.jrxmlファイルをコンパイルします。
  2. XML文書をJasperReportsに設定します。
  3. PDFを生成します。

これで、mydata.xmlファイルがある前提のもと、Figure 1.のPDFが生成されます。




上に戻る


JasperReportsのカスタマイズ

上記の準備を踏まえ、JasperReportsをカスタマイズしましょう。

まず、帳票定義ファイル(.jrxml)から始めましょう。XQueryステートメントを記述する必要があるので、Listing 4.のqueryString要素を次のように変更します。


Listing 6. XQueryを発行する記述(reportXQuery.jrxmlの一部)

|--------10--------20--------30--------40--------50--------60--------70--------80--------|
<property name="basepath" value="/customers/customerinfo"/>   <!--  **(1)**  -->
<queryString language="XQuery">   <!--  **(2)**  -->
  <![CDATA[xquery let $inst := db2-fn:xmlcolumn("CUSTOMER.INFO") return <customers>{$inst}
</customers>]]>
</queryString>

            

Listing 6.の(2)を見てください。queryString要素のlanguage属性にXQueryを指定します。この要素の内容にListing 2.で示したXQueryを記述します。次にListing 6.の(1)を見てください。listing 4.で示していたXML文書中のどのノードの集合を基準とするかを示す情報(language属性にXPath属性をもつqueryString要素)を、property要素の属性に記述を移動しました。property要素のname属性は、これから作成するQueryExecutorにて使用します。なお、JasperReportsのDTDを読むと、property要素を使って、任意の情報をJasperReportsに渡せることが分かります。

次に、XQueryを発行するためのQueryExecutorを作成しましょう。このカスタムクラスを、XQueryQueryExecutorと呼ぶことにします。XQueryQueryExecutorクラスは、JRQueryExecutorインターフェースをインプリメントする必要があります。実装は下記のようになります。


Listing 7. XQueryQueryExecutor.java

|--------10--------20--------30--------40--------50--------60--------70--------80--------|
package com.example;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;

import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRDataset;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JRValueParameter;
import net.sf.jasperreports.engine.data.JRXmlDataSource;
import net.sf.jasperreports.engine.query.JRQueryExecuter;

public class XQueryQueryExecuter implements JRQueryExecuter {

 private Connection connection;
 protected final JRDataset dataset;
 @SuppressWarnings("unchecked")
 private final Map parametersMap;

 @SuppressWarnings("unchecked")
 public XQueryQueryExecuter(JRDataset dataset, Map parameters) {
  this.dataset = dataset;
  this.parametersMap = parameters;
  JRValueParameter parameter = (JRValueParameter) this.parametersMap
    .get(JRParameter.REPORT_CONNECTION);
  this.connection = (Connection) parameter.getValue();
 }

 @Override
 public boolean cancelQuery() throws JRException {
  return false;
 }

 @Override
 public void close() {
 }

 @Override
 public JRDataSource createDatasource() throws JRException {
  JRDataSource dataSource = null;
  String query = dataset.getQuery().getText();//------------------------------(1)
  Statement statement = null;
  try {
   statement = this.connection.createStatement();
   ResultSet rs = statement.executeQuery(query);//----------------------------(2)
   rs.next();
   InputStream is = rs.getAsciiStream(1);

   String expression = dataset.getPropertiesMap().getProperty("basepath");//--(3)

   dataSource = new JRXmlDataSource(is, expression);//------------------------(4)
  } catch (SQLException e) {
   throw new JRException(e);
  }
  return dataSource;
 }

}

            

コンストラクタでは、渡されたパラメータとデータベースコネクションをこのクラスのフィールドに設定します。処理の中心となるcreateDataSourceメソッドを確認しましょう。

  1. JRDataSetよりXQueryのテキストを取得します。これはListing 6.のqueryString要素の内容です。(Listing 6. (2)を参照)
  2. XQueryを発行します。結果セットよりXML文書のInputStreamを取得します。XMLは結果セットの1行目の1列目にあるので、nextメソッド呼び出し後に、getAsciiStreamメソッドにパラメータ1を渡しています。
  3. JRDataSetよりXML文書中のどのノードの集合を基準とするかを示す情報を取得します。これはListing 6.のproperty要素のvalue属性です。(Listing 6. (1)を参照)
  4. 上記2., 3.で取得した内容を使い、JRXmlDataSourceのインスタンスを生成します。

次に、ファクトリクラスを準備する必要があります。ファクトリクラスは、JRQueryExecutorFactoryインターフェースを実装します。


Listing 8. XQueryExecutorFactory.java

|--------10--------20--------30--------40--------50--------60--------70--------80--------|
package com.example;

import java.util.Map;

import net.sf.jasperreports.engine.JRDataset;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.query.JRQueryExecuter;
import net.sf.jasperreports.engine.query.JRQueryExecuterFactory;

public class XQueryExcecuterFactory implements JRQueryExecuterFactory {

 @Override
 public JRQueryExecuter createQueryExecuter(JRDataset dataset, Map parameters)
   throws JRException {
  return new XQueryQueryExecuter(dataset, parameters);
 }

 @Override
 public Object[] getBuiltinParameters() {
  return null;
 }

 @Override
 public boolean supportsQueryParameterType(String className) {
  return false;
 }

}

            

このクラスの解説は特に必要ないでしょう。

最後に、JasperReportsを実行するコード(ReportXQuery.java)を作成しましょう。SAMPLEデータベースはローカルマシンに存在するとします。このデータベースのユーザ名、パスワードはそれぞれfoo,barとします。


Listing 9. ReportXQuery.java

|--------10--------20--------30--------40--------50--------60--------70--------80--------|
package com.example;

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.HashMap;
import java.util.Map;

import net.sf.jasperreports.engine.JRParameter;
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;
import net.sf.jasperreports.engine.util.JRProperties;

public class ReportXQuery {

 @SuppressWarnings("unchecked")
 public static void main(String[] args) throws Exception {

  JRProperties.setProperty(
    "net.sf.jasperreports.query.executer.factory.XQuery",
    "com.example.XQueryExcecuterFactory");-----------(1)
  JasperReport jasperReport;
  JasperPrint jasperPrint;
  jasperReport = JasperCompileManager
    .compileReport("reports/reportXQuery.jrxml");//

  Map params = new HashMap();
  Connection con = getConnection();
  params.put(JRParameter.REPORT_CONNECTION, con);//-----------(2)

  jasperPrint = JasperFillManager.fillReport(jasperReport, params);
  JasperExportManager.exportReportToPdfFile(jasperPrint,
    "reports/pdfXQuery.pdf");

  con.close();

 }

 private static Connection getConnection() throws Exception {
  Class.forName("com.ibm.db2.jcc.DB2Driver");
  String url = "jdbc:db2://127.0.0.1:50000/sample";
  String user = "foo";
  String password = "bar";
  Connection con = null;
  con = DriverManager.getConnection(url, user, password);
  return con;
 }

}

            

コーディングはListing 5.とほとんど同じですが、相違点を解説します。

  1. JaspreReportsのインスタンスに、先ほど作成したファクトリクラスを設定します。ここで設定するキーのnet.sf.jasperreports.query.executer.factory.XQueryは、JRQueryExecuterFactoryインターフェースのQUERY_EXECUTER_FACTORY_PREFIXで定義された変数の内容にqueryString要素のlanguage属性をピリオドで連結したものです。
  2. データベースコネクションをJaspreReportsのインスタンスに設定します。



上に戻る


終わりに

SAMPLEデータベースを開始し、ReportXQuery(Listing 9.)を実行してください。Figure 1.で示したPDFが得られます。もちろん、今回作成したXMLファイルやJavaクラス、それからDB2のJDBCドライバへのクラスパスの設定を忘れずに。

このように、ちょっとした工夫で、JasperReportsとpureXMLをシームレスに連携させることができ、pureXMLのポテンシャルを実感することができると言えるでしょう。





上に戻る


ダウンロード

内容ファイル名サイズダウンロード形式
JasperReprots_XQueryjasperReprots_XQuery.zip7.86KBHTTP
ダウンロード形式について


参考文献



著者について

富士ソフト株式会社IT事業本部勤務。社内のJava,XML関連のプロジェクトへの支援作業に従事。




記事の評価


サイト改善のため、ご意見をお寄せください。こちらのフォームからお願いいたします。



 


 


不充分・不完全である大変素晴らしい
 


この記事を共有する

del.icio.us del.icio.us newsing newsing FC2ブックマーク FC2ブックマーク
Choix! Choix! ニフティクリップ ニフティクリップ Yahoo!ブックマーク Yahoo!ブックマーク
MM/memo MM/memo CZブックマーク CZブックマーク livedoorクリップ livedoorクリップ
はてなブックマーク はてなブックマーク Buzzurl(バザール) Buzzurl(バザール)




上に戻る


    日本IBMについて プライバシー お問い合わせ