2010年12月 6日 #java #snippet #spring #howto #tutorial

DataSource Beanを定義する際applicationContext.xmlの書き方と、Javaで呼び出すコードのメモです。ずいぶん昔のメモです。。

こんなjdbc.propertiesファイルがクラスパスにあるとします。

jdbc.driverClassName=org.h2.Driver
jdbc.url=jdbc:h2:~/test
jdbc.username=sa
jdbc.password=

そしてSpringのapplicationContext.xmlにはこう書きます。

<context:property-placeholder location="jdbc.properties"/>

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
</bean>


<bean id="accountDao" class="test.dao.AccountDao">
    <property name="dataSource" ref="dataSource" />
</bean>

Javaでの呼び出し:

ApplicationContext context = new ClassPathXmlApplicationContext(
        "applicationContext.xml");

AccountDao accountDao = (AccountDao) context.getBean("accountDao");

// other code...

一つ以上の項目がPKとなったケースでHibernateはどう設定すればいいのかを説明します。
ずいぶん昔のメモになります。

こんなテーブルがあるとします。

create table Account (
    code varchar(255) not null,
    number integer not null,
    description varchar(255),
    primary key (code, number)
);

PKは'code'と'number'です。

方法は三つあります

まずPKを表すクラスを作ります。PKクラスは以下の条件を満足しなければなりません。

  • publicクラス
  • デフォルトのコンストラクタ
  • シリアライズを実装
  • hashCode()とequals()を実装

そしてエンティティクラスは以下の三つの方法のうちどれかで実装します。

  • PKクラスを@Embeddableアノテーションで記述し、エンティティクラスのプロパティとして書いて@Idとマーク
  • PKクラスをエンティティクラスにプロパティとして書いて@EmbeddableIdとマーク
  • PKを表す全ての項目をエンティティクラスのプロパティとして登録し@Idとマーク

それぞれのソースコードをリストします。

一番目@Embeddable

ここはAccountとそのPKを表すクラスAccountPkを作成しました。
メリットはPKクラスを再利用できるところです。
もっとも自然的なアプローチだそうです。

package sample.annotations;

import java.io.Serializable;

import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.Id;


@Entity
public class Account {
    private String description;
    private AccountPk id;

    public Account(String description) {
        this.description = description;
    }

    protected Account() {
    }

    @Id
    public AccountPk getId() {
        return this.id;
    }

    public String getDescription() {
        return this.description;
    }

    public void setId(AccountPk id) {
        this.id = id;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Embeddable
    public static class AccountPk implements Serializable{
        private String code;
        private Integer number;

        public AccountPk() {
        }

        public String getCode() {
            return this.code;
        }

        public Integer getNumber() {
            return this.number;
        }

        public void setNumber(Integer number) {
            this.number = number;
        }

        public void setCode(String code) {
            this.code = code;
        }

        public int hashCode() {
            int hashCode = 0;
            if (code != null)
                hashCode ^= code.hashCode();
            if (number != null)
                hashCode ^= number.hashCode();
            return hashCode;
        }

        public boolean equals(Object obj) {
            if (!(obj instanceof AccountPk))
                return false;
            AccountPk target = (AccountPk) obj;
            return ((this.code == null) ? (target.code == null) : this.code
                    .equals(target.code))
                    && ((this.number == null) ? (target.number == null)
                            : this.number.equals(target.number));
        }
    }
}

検証するクラスを作成しました。

package sample.annotations;

import java.util.Iterator;
import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

import sample.annotations.Account.AccountPk;

public class TestAccount {
    public static void main(String** args) {
        SessionFactory sessionFactory = new AnnotationConfiguration()
                .configure().buildSessionFactory();
        Session session = sessionFactory.openSession();
        session.beginTransaction();

        Account account = new Account("This is the first type.");

        // construct pk value
        AccountPk accountPk = new AccountPk();
        accountPk.setCode("kinopyo001");
        accountPk.setNumber(12345);
        // set pk
        account.setId(accountPk);

        // save
        session.save(account);
        session.getTransaction().commit();
        System.out.println("Commit");

        // load
        List list = session.createQuery("from Account").list();
        Iterator i = list.iterator();
        while (i.hasNext()) {
            Account a = (Account) i.next();
            System.out.println("code: " + a.getId().getCode() + ", number: "
                    + a.getId().getNumber() + "  Description: "
                    + a.getDescription());
        }
        session.close();

    }
}

設定が正しければこんなログが出るはずです。

Commit
code: kinopyo001, number: 12345  Description: This is the first type.

二番目@EmbeddableId(抜粋)

@EmbeddedId
public AccountPk getId() {
    return this.id;
}

三番目@IdClass(抜粋)

@Entity
@IdClass(Account.AccountPk.class)
public class Account {

    private String description;
    private String code;
    private Integer number;

    public Account(String description) {
        this.description = description;
    }

    protected Account() {
    }

    @Id
    public String getCode() {
        return this.code;
    }

    @Id
    public Integer getNumber() {
        return this.number;
    }

    public String getDescription() {
        return this.description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public void setNumber(Integer number) {
        this.number = number;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public static class AccountPk {
    // ...
}
2010年11月29日 #java #database #h2db #spring

Springのコンテキストファイルにjdbc:embedded-databaseタグで
type="H2"でbeanを登録します。

jdbc:scriptタグで初期化時に実行したいSQLファイルを指定できます。
Sprintって、本当に便利ですね。

<jdbc:embedded-database id="dataSource" type="H2">
    <jdbc:script location="classpath:schema.sql"/>
    <jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>

ちなみにjdbcのnamespaceの登録も忘れずに。

xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc
        http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd">

H2以外でもHSQL、Derbyがサポートされています。

2010年4月16日 #java

久々にJavaを書いたので、表題のExceptionにハマっちゃいました。
こんなコードでした。

String** array = {"a", "b", "c"};
List<String> list = Arrays.asList(array);
list.add("d");

ちょこっとググッてみたらasListで作ったListに
add()で要素を追加すると上記のExceptionになるそうです。
asListのJavadocでの記載は

指定された配列を基にする固定サイズのリストを返します。返されたリストへの変更は、そのまま配列に書き込まれます。(後は省略)

なるほど。。。
とりあえず深く考えずに下記のように書き換えて回避しました。

String** array = {"a", "b", "c"};
List<String> list = new ArrayList<String>();
list.addAll(Arrays.asList(array));
list.add("d");
2010年4月16日 #java

ハマっちゃったException第二弾です。
実際はハマったワケではありませんが、
一応こんなExceptionがあると。

String blank = "";

int i = Integer.parseInt(blank);

これで余裕に下記のExceptionが出ます。

java.lang.NumberFormatException: For input string: ""

何も考えずにあるJavascriptの処理をJavaに書き換える作業で発生したんです。
JavascriptはブランクをparseIntするとNaN(Not a Number)になるが、
Javaは完全におちますね。。。

2010年2月17日 #java

基本

多分ほとんどの人は分かっていると思いますが、
jspファイル内のコメントの記述は以下になります。

<%-- JSPのコメント --%>

そしてHTMLのコメントの記述は

<!-- HTMLのコメント -->

問題

SP内でもHTMLのコメント記述が使えますが、
コメントアウトした部分にJavaプログラムが埋め込んだ場合
そのコードは実はコンパイルされてしまいます。
例えば、

<!-- コメントアウト
<input type="text" value="<%= request.getNotExistMethod() %>" />
 -->

ここでは全く存在しないJavaのメソッドを書きました。
このままだとJSPがコンパイルエラーになります。
"The method getNotExistMethod() is undefined for the type HttpServletRequest"というメッセージです。

正しい方法

Javaプログラムが埋め込んだコードをコメントアウトするには
JSPのコメント式を記述します。
上記の例で言うとこうなります。

<%-- JSPコメントアウト
<input type="text" value="<%= request.getNotExistMethod() %>" />
-%>

補足

下記のようなコンパイルが通るJavaコードが書かれた場合は、
もちろんエラーは出ないし、 HTMLの部分もコメントアウトされたため画面には何も表示されません。

<!-- コメントアウト
<input type="text" value="<%= request.getContextPath() %>" />
->
2010年1月15日 #java

@ComponentアノテーションはJavaのクラスにつけるもので
このクラスをコンテナが管理するBeanとして登録する機能です。
@Serviceアノテーションも現状では@Componentと違いはありませんが、
通常サービス層のコンポネントは@Serviceアノテーションをつけます。

その理由は主に以下の二つです。

  • コンポーネントがサービス層のコンポーネントを明確にする意味で
  • @Serviceアノテーションを付与したBeanには自動的にトランザクション管理の機能が付与されるなどの機能拡張も期待される

Spring3.0のリリースノートを見たところ特に@Serviceに関する変更はないみたいです。
なのでサービス層のクラスに対して@Componentか@Serviceか悩んでるんだっだら
@Serviceをつけた方がいいでしょう。

参考サイト:
Spring 2.5の新機能を使ってアプリケーションを作る(その1)

2009年12月28日 #java

なぜDIコンテナを使うのかの勉強メモです。

SpringをはじめとするDIフレームワークでは、他クラスへの依存はインターフェイスを介したものになります。インターフェイスを介するため、呼び出す側はそのインターフェイスを実装しているクラスの完成を待つ必要がなくなります。DIコンテナを利用すればDIコンテナが設定ファイルの記述に従ってインターフェイスの実装クラスをセットします。そのため、設定ファイルの内容を切り替えることで単体テスト用のモックオブジェクト(ダミーオブジェクト)に切り替えることができます。

サンプル

JuchuImplクラスはZaikoHikiateImplクラスに依存しています。
そのためJuchuImpl単体のテストを行おうとしても、
ZaikoHikiateImplクラスのhikiate()メソッドにバグがある場合テストができませんでした。

単体テストを行いにくいクラス

public class JuchuImpl implements Juchu {
    public void juchu(Integer syohinCode, Integer suryo) {
        ZaikoHikiate hikiate = new ZaikoHikiateImpl();
        hikiate.hikiate(syohinCode, suryo);
        // 受注を処理
    }
}

DIにより依存を低めたクラス

public class JuchuImpl implements Juchu {

    private ZaikoHikiate hikiate;

    public void setHikiate(ZaikoHikiate hikiate) {
        this.hikiate = hikiate;
    }
    public void juchu(Integer syohinCode, Integer suryo) {
        hikiate.hikiate(syohinCode, suryo);
        // 受注を処理
    }
}

JuchuクラスはZaikoHikiateImplクラスが完成していなくても
ZaikoHikiateインターフェイスを実装したモックオブジェクトを
DIコンテナにセットしてJuchuImplの単体テストを実行できます。