【コラム】

Java API、使ってますか?

10 JSR 295: Beans Bindingの参照実装を試す

10/60

JSR 295: Beans Bindingの参照実装公開

本コラムの第6回では、「JSR 295: Beans Binding」の概要を紹介した。JSR 295は、複数のJavaBeansのプロパティを同期させるためのバインディングAPIである。第6回ではその参考としていくつかのバインディングフレームワークの使い方を解説した。

4月に入り、java.net内にあるプロジェクトサイトにおいてJSR 295の参照実装とAPIドキュメントが公開された。今回はこの参照実装を使って、Beansバインディングを用いたSwingプログラムを作ってみようと思う。なおJSR 295はJCPのステージ上ではまだ早期ドラフトすら公開されていない段階であり、今回公開されたのはあくまでも開発版である。今後大きな変更が加えられる可能性もあるので注意していただきたい。

JTextFieldとJLabelを使ったバインディング

JSR 295のプロジェクトサイトのドキュメントセクションからダウンロードすることができるAPIドキュメントを見てもわかるように、この実装にはJSR 295のベースとなる以下の4つのパッケージと、それに関連したcom.sun.*パッケージが含まれている。

  • javax.beans.binding
    2つのBeanのプロパティをバインドするためのベースとなるクラス群
  • javax.beans.binding.ext
    バインディングAPIを独自に拡張するための機能を提供する
  • javax.el
    「Unified Expression Language(Unified EL)」を使用するためのクラス群
  • javax.swing.binding
    Swingコンポーネントのバインディングをサポート

このうちのjavax.elは、JSP 2.1やJSF 1.2などで利用されてるUnified ELのためのAPIである。JSR 295ではBeanどうしのバインディング定義にUnified ELを使用できる。javax.elパッケージはそのためのもので、これはGlassfishプロジェクトの成果物を元にしてBeans Binding用に多少の修正を加えたものであるとのことだ。

それでは、実際にBeanのプロパティを同期させてみよう。リスト1はJTextFieldのtextプロパティとJLabelのtextプロパティをバインディングする例だ。

バインディングの定義はjavax.beans.binding.Bindingクラスによって行う。Bindingクラスのコンストラクタには、第1 - 第4引数でバインド対象のBeanオブジェクトとプロパティを指定する。5番目以降は可変長の引数で、Beanに応じたオプションを指定できるようになっている。javax.swing.binding.SwingBindingSupportクラスには各種Swingコンポーネントのためのパラメータが定義されており、ここではテキストフィールドのテキストが変更された際にパラメータの値の同期を実行するよう指定している。

リスト1 TextfieldBindingSample.java - テキストフィールドとラベルのバインディング

package apisample;

import java.awt.GridLayout;
import javax.beans.binding.Binding;
import javax.swing.*;
import javax.swing.binding.SwingBindingSupport;

public class TextfieldBindingSample {       
    public TextfieldBindingSample() {
        JFrame frame = new JFrame("TextfieldBindingSample");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel panel = new JPanel(new GridLayout(2,1));        
        JTextField jTextField = new JTextField();
        JLabel jLabel = new JLabel("Please input.");

        // テキストフィールドとラベルのプロパティをバインディング
        Binding binding = new Binding(jLabel, "${text}", jTextField, "text", 
                SwingBindingSupport.TextChangeStrategyParameter, 
                SwingBindingSupport.TextChangeStrategy.CHANGE_ON_TYPE);
        binding.bind();

        panel.add(jLabel);
        panel.add(jTextField);
        frame.getContentPane().add(panel);
        frame.setSize(250, 100);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        TextfieldBindingSample sampple = new TextfieldBindingSample();
    }
}

このプログラムを実行すると図1のように表示される。テキストフィールドのテキストを変更すると即座にラベルに反映され、両者が同期していることがわかる。

図1 TextfieldBindingSampleの実行例

JTableを使ったバインディング

SwingBindingSupportクラスでは、JTextFieldとJLabel以外にもJCheckBox、JComboBox、JList、JSlider、JTable、JTree、その他のJTextComponentなどのバインディングをサポートしている。リスト2に示すのは、JavaBeanオブジェクトのプロパティとJTableに表示する値をバインドした例だ。

リスト2 TableBindingSample.java - Personオブジェクトのリストとテーブルのバインディング

package apisample;

import java.util.*;
import javax.beans.binding.Binding;
import javax.swing.*;
import javax.swing.binding.SwingBindingSupport;

public class TableBindingSample {       
    public TableBindingSample() {
        JFrame frame = new JFrame("TableBindingSample");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        List persons = new ArrayList();
        persons.add(new Person("Takaaki", "SUGIYAMA"));
        persons.add(new Person("Daichi", "GOTO"));
        JTable jTable = new JTable();

        // Personオブジェクトのリストとテーブルをバインディング
        Binding binding = new Binding(persons, null, jTable, "elements");
        binding.addBinding("${firstName}", null, 
                      SwingBindingSupport.TableColumnParameter, 0);
        binding.addBinding("${lastName}", null, 
                      SwingBindingSupport.TableColumnParameter, 1);
        binding.bind();

        frame.getContentPane().add(jTable);
        frame.setSize(250, 150);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        TableBindingSample sampple = new TableBindingSample();
    }
}

ここで使用しているPersonクラスはリスト3のようなもので、はじめにそのオブジェクトを要素に持つLitsを作成している。そして、まずはこのListオブジェクトとJTableのバインディングを定義したBindingを作成する。続いてListの要素であるPersonの各要素と、JTableの各カラムとのバインディングの定義を、addBinding()メソッドによって行っている。

リスト3 Person.java

package apisample;

public class Person {
    private String firstName;
    private String lastName;

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    /** SetterおよびGetterは省略 **/
}

このプログラムを実行すると図2のように表示される。

図2 TableBindingSampleの実行例

Java Persistence APIとの組み合わせ

最後に、JSR 295をJava Persistence API(JPA)と組み合わせて使用する例を紹介する。JPAはJavaオブジェクトの永続化のためのAPIであり、Swingコンポーネントを永続化オブジェクトと同期させれば、ユーザからの入力を容易にデータベースに反映させることができる。JPAの使用方法については特集記事を参照してほしい。ここではJPAの実装としてGlassfishに付属するToplink Essentialsを、データベースエンジンとしてH2 Database Engineを使用する。

まず、永続化エンティティとしてリスト4のようなJavaBeanを用意する。PropertyChangeEventをサポートするためにフィールドにPropertyChangeSupportを宣言してあるが、これは永続化の対象とならないように@Transientを指定している。

リスト4 PersonEntity.java

package apisample;

import java.beans.*;
import java.io.Serializable;
import javax.persistence.*;

@Entity
public class PersonEntity implements Serializable {
    @Transient
    private PropertyChangeSupport changes = new PropertyChangeSupport(this);

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String firstName;
    private String lastName;

    public PersonEntity() {
    }

    public Long getId() {
        return this.id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public void setFirstName(String firstName) {
        String oldFirstName = this.firstName;
        this.firstName = firstName;
        changes.firePropertyChange("firstName", oldFirstName, this.firstName);
    } 
    public String getFirstName() {
        return this.firstName;
    }
    public void setLastName(String lastName) {
        String oldLastName = this.lastName;
        this.lastName = lastName;
        changes.firePropertyChange("lastName", oldLastName, this.lastName);
    } 
    public String getLastName() {
        return this.lastName;
    }

    public void addPropertyChangeListener(PropertyChangeListener l) {
        changes.addPropertyChangeListener(l);
    }
    public void removePropertyChangeListener(PropertyChangeListener l) {
        changes.removePropertyChangeListener(l);
    }

    @Override
    public String toString() {
        return "PersonEntity[id:" + this.id + ", " + this.firstName + ", " + this.lastName + "]";
    }
}

persistence.xmlはリスト5のようになる。データベース名などは各自の環境に応じて設定すること。

リスト5 persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
                http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
  <persistence-unit name="BeansBindingSamplePU" transaction-type="RESOURCE_LOCAL">
    <provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider>
    <class>apisample.PersonEntity</class>
    <properties>
      <property name="toplink.jdbc.user" value="sa"/>
      <property name="toplink.jdbc.password" value=""/>
      <property name="toplink.jdbc.url" value="jdbc:h2:tcp://localhost/bindingsample"/>
      <property name="toplink.jdbc.driver" value="org.h2.Driver"/>
      <property name="toplink.ddl-generation" value="create-tables"/>
    </properties>
  </persistence-unit>
</persistence>

PersonEntityを使ったSwingプログラムはリスト6のようにした。ここでは、まず最初にデータベースからすべてのPersonEntityオブジェクトをListとして取得し、それをBindingクラスを使用してJTableに関連付けている。各PersonEntityオブジェクトにはPropertyChangeListenerが追加してある。PropertyChangeListenerでは、プロパティが変更されたEntityManagerのmerge()メソッドを利用してデータベースに反映させるようになっている。

リスト6 BindingWithJPA.java - JPAとの組み合わせ

package apisample;

import java.beans.*;
import java.util.List;
import javax.beans.binding.Binding;
import javax.persistence.*;
import javax.swing.*;
import javax.swing.binding.SwingBindingSupport;

public class BindingWithJPA {
    private EntityManagerFactory factory;
    private EntityManager manager;

    public BindingWithJPA() {
        // エンティティマネージャを取得
        factory = Persistence.createEntityManagerFactory("BeansBindingSamplePU");
        manager = factory.createEntityManager();

        JFrame frame = new JFrame("BindingWithJPA");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JTable jTable = new JTable();

        // データベースからデータを取得してリストを初期化
        Query query = manager.createNativeQuery("select * from PersonEntity p order by p.id", PersonEntity.class);
        List results = (List)query.getResultList();
        PropertyChangeListener listener = new PersonPropertyChangeListener();
        for(PersonEntity person : results) {
            person.addPropertyChangeListener(listener);
        }

        // リストのPersonEntityオブジェクトとテーブルをバインディング
        Binding binding = new Binding(results, null, jTable, "elements");
        binding.addBinding("${id}", null, 
                      SwingBindingSupport.TableColumnParameter, 0);
        binding.addBinding("${firstName}", null, 
                      SwingBindingSupport.TableColumnParameter, 1);
        binding.addBinding("${lastName}", null, 
                      SwingBindingSupport.TableColumnParameter, 2);
        binding.bind();

        frame.getContentPane().add(jTable);
        frame.setSize(250, 150);
        frame.setVisible(true);
    }

    class PersonPropertyChangeListener implements PropertyChangeListener {
        public void propertyChange(PropertyChangeEvent e) {
            PersonEntity person = (PersonEntity)e.getSource();
            EntityTransaction transaction = manager.getTransaction();
            transaction.begin();
            manager.merge(person);
            transaction.commit();
        }
    }

    public static void main(String[] args) {
        BindingWithJPA sample = new BindingWithJPA();
    }
}

準備ができたら実際に実行してみよう。データベースにはPersonEntityクラスに対応した"PERSONENTITY"というテーブルを用意し、初期状態は図3のようにしておく。

図3 データベースの初期状態

この状態でBindingWithJPAを実行すると、最初は図4のように表示される。ここから、たとえば図5のようにデータを修正すれば、それがPersonEntityオブジェクトのプロパティに同期され、PropertyChangeListenerが呼び出されて図6のようにデータベースに反映される。

図4 BindingWithJPAの実行例1

図5 BindingWithJPAの実行例2 - データを修正する

図6 修正結果がデータベースに反映されている

JSR 295のエキスパートグループは現在JCPレビューのためのドラフトの作成を進めている。今回ひと足早く参照実装が公開されたのは、開発者からのフィードバックをドラフトに反映させるためだという。プロジェクトサイトではそのためのメーリングリストも設置されているため、何か気づいた点があればコンタクトを取ってみるといいだろう。

10/60

インデックス

連載目次
第60回 どうなる? 今後のJavaプラットフォーム(Java SE編)
第59回 どうなる? 今後のJavaプラットフォーム(Java EE編)
第58回 Java SE 7の要注目機能"クロージャ"はどうなるのか その6
第57回 Java SE 7の要注目機能"クロージャ"はどうなるのか その5
第56回 Java SE 7の要注目機能"クロージャ"はどうなるのか その4
第55回 Java SE 7の要注目機能"クロージャ"はどうなるのか その3
第54回 Java SE 7の要注目機能"クロージャ"はどうなるのか その2
第53回 Java SE 7の要注目機能"クロージャ"はどうなるのか
第52回 Early Draftが公開されたJSF 2.0
第51回 EJBから独立したJava Persistence 2.0
第50回 モバイルJavaの新しい潮流となるか - MSA 2.0のドラフト公開
第49回 やっぱり基本はServlet - Servlet 3.0のEarly Draftを読む
第48回 JOGLで3Dプログラミング その4
第47回 JOGLで3Dプログラミング その3
第46回 JOGLで3Dプログラミング その2
第45回 JOGLで3Dプログラミング
第44回 JARファイルを効率的にネットワーク転送するためのPack200形式
第43回 Early Draftで把握するEJB 3.1の新機能
第42回 次世代の携帯端末向けJava仕様"MIDP 3.0"はどうなるか その2
第41回 次世代の携帯端末向けJava仕様"MIDP 3.0"はどうなるか その1
第40回 リソースアダプタによる接続の仕組み
第39回 JCAを利用したシステム間接続
第38回 Java EEと外部システムの接続性を支えるJCAがバージョンアップ
第37回 Javaのモジュラリティ強化を担う"スーパーパッケージ"とは
第36回 JSR 308対応のコンパイラを試す
第35回 公開されたJSR 308のEarly Draftを検証する
第34回 スクリプト言語とJavaを結びつけるJSR 223
第33回 Java EE環境に統一されたコンポーネントモデルを提供するJSR 299 その2
第32回 Java EE環境に統一されたコンポーネントモデルを提供するJSR 299 その1
第31回 Javaの文法がそのまま使えるスクリプト言語"BeanShell"
第30回 Javaアプリケーションにオブジェクトのキャッシュ機構を提供するJCache API
第29回 Javaアプリケーションからのリソース管理を可能にするJSR 284
第28回 XMLデータソースへの問い合わせはJSR 225で
第27回 Portlet Specification 2.0をもっと手軽に利用する
第26回 次期Javaポートレット仕様となるJSR 286
第25回 JSFとポートレットをつなげるJSR 301
第24回 Webサービス向けのポートレット仕様「WSRP」
第23回 高い相互運用性を実現するポートレットAPI - JSR 168
第22回 Java EE環境でタスクのスケジューリングを可能にするJSR 236
第21回 Java EE環境でのスレッドプログラミングを可能にするJSR 237
第20回 音声認識/合成のためのAPI - Java Speech APIとJSR 113
第19回 JSR 291でJavaプラットフォームにダイナミックコンポーネントモデルを導入
第18回 JAX-RSで簡単RESTful - JSR 311
第17回 待望のServlet 3.0がJSRに登場 - JSR 315
第16回 アノテーションを使ってバグ退治 - JSR 305
第15回 アノテーションをさらに広い範囲で利用可能にするJSR 308
第14回 Webアプリケーション開発の要となるか - JSF 2.0がJSRに登場
第13回 Webサービス経由でのJMX Agentへの接続を可能にするJSR 262
第12回 Javaアプリケーションのモジュール化をサポートするJava Module System
第11回 "NIO.2"がやってきた - JSR 203: More New I/O APIs for the Java Platform
第10回 JSR 295: Beans Bindingの参照実装を試す
第9回 けっこう便利! 単位を扱うAPI -- JSR 275: Units Specification
第8回 アノテーションでバリデーション - JSR 303: Bean Validator
第7回 Swing開発の救世主となるか - Swing Application Framework
第6回 JavaBeansのプロパティを同期させるバインディングAPI
第5回 誰よりも早く"Java SE 7"を睨む
第4回 日時情報の取り扱いを改善する JSR 310: Date and Time API
第3回 古いAPIも進化している!? - JSR 919: JavaMail 1.4
第2回 JSR 1 リアルタイムJava仕様
第1回 JCPによって進められるJava関連技術の標準化

もっと見る

提供:マイナビ

会員登録はこちら

大学・大学院・短大・専門学生向けの就職情報サイト「マイナビ2010」「マイナビ2009」に今すぐ登録しよう!  大手企業からベンチャー企業までの約13,000社の企業情報を公開、エントリーが可能です。2010年卒予定の方は「マイナビ2010」に、2009年卒予定の方は「マイナビ2009」に登録してください。

毎日コミュニケーションズはプライバシーマークを取得しています。



転職ノウハウ

あなたが本領発揮できる仕事を診断
あなたの仕事適性診断

シゴト性格・弱点が20の質問でサクッと分かる!

「仕事辞めたい……」その理由は?
「仕事辞めたい……」その理由は?

71%の人が仕事を辞めたいと思った経験あり。その理由と対処法は?

3年後の年収どうなる? 年収予報
3年後の年収どうなる? 年収予報

今の年収は適正? 3年後は? あなたの年収をデータに基づき予報します。

激務な職場を辞めたいが、美女が邪魔して辞められない
激務な職場を辞めたいが、美女が邪魔して辞められない

美人上司と可愛い過ぎる後輩に挟まれるエンジニアの悩み

人気記事

一覧

イチオシ記事

新着記事

求人情報