doma2を使ってみた

2015-03-10T00:00:00+09:00 Java doma

http://doma.readthedocs.org/ja/latest/getting-started を見ながらやってみた

※ターゲットバージョンは2.x

構造

.
├── build.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── sample
    │   │       ├── AppConfig.java
    │   │       ├── dao
    │   │       │   └── SampleDao.java
    │   │       └── entity
    │   │           └── Sample.java
    │   └── resources
    │       └── META-INF
    │           └── sample
    │               └── dao
    │                   └── SampleDao
    │                       ├── find.sql
    │                       └── findAll.sql
    └── test
        └── java
            └── sample
                └── dao
                    └── SampleDaoTest.java

build.gradle

apply plugin: "java"

processResources.destinationDir = compileJava.destinationDir
compileJava.dependsOn processResources

repositories {
    mavenCentral()
}

dependencies {
    runtime "mysql:mysql-connector-java:+"
    compile "org.seasar.doma:doma:2.1.0"
    testCompile "junit:junit:+"
    testCompile "org.hamcrest:hamcrest-all:+"
}

公式ドキュメントにはEclipseを使う際の設定なりが記述されてるけど、使わないのですこは全面的にスキップで(詳しくは https://github.com/domaframework/simple-boilerplate を読めばいい)

でdomaの方式としてはDAO(インターフェース)とSQLを書いてそのソースから実装をアノテーションプロセッサー周りを使ってクラスを生成する仕組みになっている模様

で実行環境はこんなもんで、DAO(とエンティティクラスを含む)とSQLとデータソースなり回りなAppConfigクラスを作成する

AppConfig.java

package sample;

import javax.sql.DataSource;

import org.seasar.doma.SingletonConfig;
import org.seasar.doma.jdbc.Config;
import org.seasar.doma.jdbc.dialect.Dialect;
import org.seasar.doma.jdbc.dialect.MysqlDialect;
import org.seasar.doma.jdbc.tx.LocalTransactionDataSource;
import org.seasar.doma.jdbc.tx.LocalTransactionManager;
import org.seasar.doma.jdbc.tx.TransactionManager;

@SingletonConfig
public class AppConfig implements Config {

    private static final AppConfig INSTANCE = new AppConfig();

    private final Dialect dialect;
    private final LocalTransactionDataSource dataSource;
    private final TransactionManager transactionManager;

    private AppConfig() {
        dialect = new MysqlDialect();
        dataSource = new LocalTransactionDataSource("jdbc:mysql://localhost:3306/sample", "kinjouj", "1234");
        transactionManager = new LocalTransactionManager(dataSource.getLocalTransaction(getJdbcLogger()));
    }

    @Override
    public Dialect getDialect() {
        return dialect;
    }

    @Override
    public DataSource getDataSource() {
        return dataSource;
    }

    @Override
    public TransactionManager getTransactionManager() {
        return transactionManager;
    }

    public static AppConfig singleton() {
        return INSTANCE;
    }
}

大体な所を http://doma.readthedocs.org/ja/latest/config/#id20 を参考にして作成。DIコンテナ依存するような場合のケースは今後検証するので今回はパス

Sample.java

package sample.entity;

import java.util.Date;
import org.seasar.doma.Column;
import org.seasar.doma.Entity;
import org.seasar.doma.GeneratedValue;
import org.seasar.doma.GenerationType;
import org.seasar.doma.Id;
import org.seasar.doma.Version;
import org.seasar.doma.jdbc.entity.NamingType;

@Entity(naming = NamingType.LOWER_CASE)
public class Sample {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String name;

    @Column(name = "created_at")
    private Date createdAt;

    @Version
    private int version;

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getCreatedAt() {
        return createdAt;
    }
}

変数名がそのままカラム名になるのでマッピングする際にあたって名称によって問題なる場合には@Columnアノテーションで紐付けするカラム名をマッピングするようにすれば良い。あとテーブル名もデフォルトではクラス名同等の名前になる模様。そういう問題が出るような場合(insert等のSQLを自動生成して実行するような場合)にはnamingパラメーターでクラス内で実行するSQLを生成する際に参照するテーブル名などの方式を設定すれば良い。上記だとクラス名をlowercaseした値でSQLを実行する

※@Tableアノテーションで指定した名前で利用することも可能っぽい

SampleDao.java

package sample.dao;

import java.util.List;

import org.seasar.doma.Dao;
import org.seasar.doma.Insert;
import org.seasar.doma.Select;

import sample.AppConfig;
import sample.entity.Sample;

@Dao(config = AppConfig.class)
public interface SampleDao {

    @Select
    List<Sample> findAll();


    @Select
    Sample find(int id);

    @Insert
    // へたに引数の変数名にパッケージ名等に存在するような名称使うとコンパイルエラーになる
    int save(Sample s);
}

で@Selectアノテーションで使うSQLファイルを定義する必要がある。resources/META-INF/DAOパッケージ名/メソッド名.sqlで定義すれば良い模様

resources/META-INF/sample/dao/SampleDao/findAll.sql

select
    /*%expand*/*
from
    sample
order by id

/%expand/っていう形として、オブジェクトが持つカラムを展開してくれる

resources/META-INF/sample/dao/SampleDao/find.sql

SELECT
    /*%expand*/*
FROM
    sample
WHERE
    id = /* id */0

/* id */の箇所にメソッドの引数で指定した値が展開される

っていう感じでこれで実装終わりなので、テスト書いて実際にデータベースの問い合わせをテストしてみる

SampleDaoTest.java

package sample.dao;

import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.seasar.doma.jdbc.tx.TransactionManager;

import sample.AppConfig;
import sample.entity.Sample;

import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;

public class SampleDaoTest {

    private final SampleDao dao = new SampleDaoImpl();
    private TransactionManager tm;

    @Before
    public void setUp() {
        tm = AppConfig.singleton().getTransactionManager();
    }

    @Test
    public void test_findAll() {
        tm.required(() -> {
            List<Sample> samples = dao.findAll();
            assertThat(samples.size(), greaterThan(0));

            Sample sample = samples.get(0);
            assertThat(sample, notNullValue());
            assertThat(sample.getId(), is(1));
            assertThat(sample.getName(), is("hoge"));
        });
    }

    @Test
    public void test_find() {
        tm.required(() -> {
            Sample sample = dao.find(1);
            assertThat(sample, notNullValue());
            assertThat(sample.getName(), is("hoge"));
        });
    }

    @Test
    public void test_save() {
        tm.required(() -> {
            Sample sample = new Sample();
            sample.setName("hoge fuga foobar");

            dao.save(sample);
        });
    }
}

終わり。とりあえずGetting Startedをやってみただけなので今後もドキュメント読みを遂行しつつやってく予定っていうことで

doma2を使ってみた (2) - DI Containerを使う場合 - Polymerをやってみた