今回、JaperReportsをネタに出したのは、実はこれが理由です。サブレポートの扱いがよくわからなくて、仕事中に困っていたんですよね。
サブレポートはiReport上の「Palette」で、文字通り「Subreport」Elementをドラッグ&ドロップすることで追加できます。サブレポートを追加すると、新規サブレポート作成ウィザードが起動するので、レイアウトテンプレートなどを選んで、適当に作成します。作成したサブレポートも、デフォルトの言語は「Groovy」になっているので、必要であれば「Java」に変更してください。
で、ここでメインレポートのプレビューを見ても…サブレポートがハマった形でプレビューできません。これの意味が、最初よくわかりませんでした。これは、レポートがデフォルトではデータ(フィールドとして利用できる値)がない時は、何もページを返さないように設定されているのが原因です。
レポート自体のプロパティのうち、「When No Data」が「No Pages」になっているかと思いますので、とにかく出力してみたいのであれば「All Sections, No Detail」を選択しましょう。これで、メインのレポートのプレビューでサブレポートが含まれた形でプレビューできるはずです。
続いて、メインレポートからサブレポートにパラメータを渡すには、メインレポート上から編集できるサブレポートのプロパティで、「Parameters」を編集します。
今回は、サブレポートのパラメータ「title」にメインレポートのフィールド「title」を渡しています。これで、繰り返しごとにフィールドの値がサブレポートに渡る、ということですね。よって、サブレポート側では$P{title}で参照できるようになります。
続いて、データソースについて。
今回は、コネクションではなくコレクションベースのデータソースを使いたいのですが、どうやってサブレポートにデータソースを渡したらいいのかでちょっと困っていました。
そこで参考にしたのが、こちらの記事です。
http://zam.seesaa.net/article/53193513.html
なるほど、サブレポートのプロパティ、「Connection Type」を「Use a datasource expression」にして、「Data Source Expression」に適切な式表現を与えてあげればよいのですね。
で、先のサイトの指示通りに
new JRBeanCollectionDataSource($F{フィールド名})
と書いてみたものの、実行時にJRBeanCollectionDataSourceなんてクラス知らないよ!と怒られてしまいます。importの追加が必要っぽいですね。
というわけで、メインレポートのプロパティにJRBeanCollectionDataSourceクラスのインポートを追加します。
これで、サブレポートにデータソースが渡せるようになりました。サブレポートの「Data Source Expression」プロパティには、以下のようにフィールド「sub○」を設定するようにしました。サブレポートは今回2枚使用しているので、○の欄には1、2が入ります…。
では、作成したメインプログラム。
import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.beanutils.BeanUtils; import net.sf.jasperreports.engine.JasperCompileManager; import net.sf.jasperreports.engine.JasperFillManager; import net.sf.jasperreports.engine.JasperExportManager; import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.JasperReport; import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; import net.sf.jasperreports.engine.data.JRMapCollectionDataSource; public class JasperReportsExample { public static void main(String[] args) { try { JasperReport jasperReport = JasperCompileManager.compileReport("pdf/input/sample_report2.jrxml"); Map<String, Object> main = new HashMap<String, Object>(); main.put("title", "MainTitle"); main.put("SUBREPORT_DIR", "pdf/input/"); List<Map<String, Object>> mainDetails = new ArrayList<Map<String, Object>>(); Map<String, Object> detail1 = new HashMap<String, Object>(); detail1.put("name", "name1"); detail1.put("title", "title1"); List<PageDetail> sub1 = new ArrayList<PageDetail>(); PageDetail d1 = new PageDetail(); d1.setName("detail1"); d1.setPrice(300); sub1.add(d1); detail1.put("sub1", sub1); detail1.put("sub2", sub1); mainDetails.add(detail1); @SuppressWarnings("unchecked") JasperPrint print = JasperFillManager.fillReport(jasperReport, main, new JRMapCollectionDataSource((Collection)mainDetails)); JasperExportManager.exportReportToPdfFile(print, "pdf/output/sample_report2.pdf"); } catch (Throwable e) { e.printStackTrace(); } } }
前回のプログラムを、ちょこっと変えただけです。パラメータに渡すオブジェクトは、面倒なのでもうMapにしてしまいました。あとはサブレポートに渡すデータソースを、
detail1.put("sub1", sub1); detail1.put("sub2", sub1);
のように設定しています。
作成したレポートテンプレート3枚は、こんな感じです。メインレポート、サブレポート1、サブレポート2の順に並んでいます。
しょーもないPDFですが、一応サブレポートがはめ込まれています。
サンプルコードがかなりのやっつけコードなのですが、個人的には目標を達成したので、よしとしましょう…?