Top > Blog > Programming

BeanUtils で JavaBeans のプロパティを一気にコピーする

Web アプリなんかを作っていると、よく DB 側のサービス層から取得したオブジェクトを Web 層側のフォームBean に変換して使用したり、またその逆を行ったりという事をよくやります。 こんな感じですね↓

SimpleDateFormate sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

// サービスクラスから id をキーにして UserModel を取得
UserModel model = userService.getUser(id);

// フォームBeanを作成して、プロパティを1つ1つコピー
UserForm form = new UserForm();
form.setName(model.getName());
form.setLoginDate(sdf.format(model.getLoginDate()));

...

UserForm も UserModel も同じ名前のプロパティ(name, loginDate)を持っているのですが、 UserForm クラスは Web層で使用するフォームBean の為、 全てのプロパティが String で構成 (※) されています。一方、 UserModel クラスはドメインモデルであり、 そのまま DB とも対応したりしている為、それぞれのプロパティが適切な型に設定されています。

※Struts なんかでは String の他に boolean も使って良いよ、とStrutsの設計者は言ってますが…、なぜ boolean はOKなのか、意味不明です。全部Stringにしちまえ~というのなら分かりますが。。[戻る]

この例のようにプロパティが2つぐらいなら大した手間ではありませんが、 大きなオブジェクトになるとプロパティは10個にも20個にもなります。 それを上記の例のように1つ1つコピーしていると、 プログラムを書く上でミスが生じる可能性も高くなりますし、 可読性も下がります。何よりめんどくさいです…

そんな時に便利なのが、 Jakarta Commons BeanUtils コンポーネントです。BeanUtils は Struts プロジェクトの副産物として生まれたコンポーネントですが、 このライブラリを使うと、下記のようにコードがすっきりします。

// サービスクラスから id をキーにして UserModel を取得
UserModel model = userService.getUser(id);

// フォームBeanを作成して、BeanUtils でプロパティをコピー
UserForm form = new UserForm();
BeanUtils.copyProperties(form, model);
...

本当にコピーされているのかどうか、 下記のようなプログラム[ サンプルソース ]を作成して試してみました。

-+-+-+-+-+-+-+-+-+-+-
UserModel.java
-+-+-+-+-+-+-+-+-+-+-
package jp.javable.sample.beanutils;

import java.io.Serializable;
import java.util.Date;

public class UserModel implements Serializable {
    private String name;
    private Date loginDate;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Date getLoginDate() {
        return loginDate;
    }
    public void setLoginDate(Date loginDate) {
        this.loginDate = loginDate;
    }
}
-+-+-+-+-+-+-+-+-+-+-
UserForm.java
-+-+-+-+-+-+-+-+-+-+-
package jp.javable.sample.beanutils;

import java.io.Serializable;

public class UserForm implements Serializable {
    private String name;
    private String loginDate;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getLoginDate() {
        return loginDate;
    }
    public void setLoginDate(String loginDate) {
        this.loginDate = loginDate;
    }
    public String toString() {
        return this.getClass().getName() + "@"
         + Long.toHexString(System.identityHashCode(this))
         + "[name=" + this.getName() 
         + ",loginDate=" + this.getLoginDate() + "]";
    }
}
-+-+-+-+-+-+-+-+-+-+-
Main.java
-+-+-+-+-+-+-+-+-+-+-
package jp.javable.sample.beanutils;

import org.apache.commons.beanutils.BeanUtils;

public class Main {

    public static void main(String[] args) throws Exception {
        UserModel model = new UserModel();
        model.setName("溝口");
        model.setLoginDate(new java.util.Date());

        UserForm form = new UserForm();
        // 間違えやすいですが、このメソッドは右から左(第2引数から第1引数)にコピーします
        BeanUtils.copyProperties(form, model);

        System.out.println(form);
    }
}

実行してみます。

D:\eclipse_workspace\test> java -classpath bin;lib\commons-beanutils.jar;
lib\commons-collections.jar;
lib\commons-logging.jar jp.javable.sample.beanutils.Main
(↑実際にはすべて1行です)

jp.javable.sample.beanutils.UserForm@1027b4d[name=溝口,
            loginDate=Thu May 19 23:02:18 JST 2005]

ちゃんとコピーされました! しかし、最初の例では loginDate は yyyy/MM/dd HH:mm:ss 形式の文字列にしていたのですが、 このケースでは java.util.Date#toString() の結果がそのまま格納されています。 このままでは使えません。 これを何とかする方法は、 BeanUtils で独自のコンバーターを使用する に書きます。

ちなみに上記の実行例でも分かりますが、 BeanUtils は Commons Collection Commons Logging を内部で使用しているので、 実行時には上記 2つのライブラリが必要 (※)となります。

※ BeanUtils のバージョン 1.7 からは、使用している Commons Collections のクラスが一部同梱されているようです。 1.7 以降は Commons Logging(1.0.3以上)があれば OK です。[戻る]

コメントの投稿