58
64

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

SpringBoot+Doma2+Gradleを試してみた。

Last updated at Posted at 2014-12-26

題材は「はじめてのSpring Boot」にある顧客管理システム。
最近よく聞くDomaを試してみたかったのもあり、
JPAじゃなくDoma2、MavenじゃなくGradleで挑戦しました。

ちなみにまだRestAPIができただけです^^;

このエントリでは「はじめてのSpring Boot」に載ってるところは説明省略します。
すごいわかりやすかったので買って読む価値はあると思いますよ。

環境

今回ためした環境は以下のとおりです。

Java8
SpringBoot1.1.9
Doma2.0.1
H2
Gradle2.2.1

構成

ソースはこちら。
https://github.com/nyasba/domaboot.git

一部省略してますが、こんなかんじです。

$ tree
.
├── build.gradle
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           ├── App.java
        │           ├── AppConfig.java
        │           ├── api
        │           │   ├── CustomerRequest.java
        │           │   └── CustomerRestController.java
        │           ├── datasource
        │           │   ├── CustomerRepository.java
        │           │   └── DomaRepository.java
        │           ├── domain
        │           │   └── CustomerEntity.java
        │           └── service
        │               └── CustomerService.java
        └── resources
            ├── META-INF
            │   └── com
            │       └── example
            │           └── datasource
            │               └── CustomerRepository
            │                   ├── findAllOrderByName.sql
            │                   └── findById.sql
            ├── data.sql
            ├── logback.xml
            ├── schema.sql
            └── templates
                └── dummy.html

Domaを使う

DB接続設定

今回はH2database(メモリ)を使ってます。
接続先の定義はDataSourceで書くだけで簡単ですね。

[12/26修正]
Domaの返却するDataSourceはそのままだとSpringの管理外になるため、実行時例外発生時にロールバックされなくなるとのこと。TransactionAwareDataSourceProxyで包むように修正。

AppConfig.java
package com.example;

import net.sf.log4jdbc.Log4jdbcProxyDataSource;
import org.seasar.doma.jdbc.Config;
import org.seasar.doma.jdbc.NoCacheSqlFileRepository;
import org.seasar.doma.jdbc.SimpleDataSource;
import org.seasar.doma.jdbc.SqlFileRepository;
import org.seasar.doma.jdbc.dialect.Dialect;
import org.seasar.doma.jdbc.dialect.H2Dialect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;


@Configuration
@EnableTransactionManagement
public class AppConfig {

    DataSource realDataSource(){
        SimpleDataSource dataSource = new SimpleDataSource();
        dataSource.setUrl("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
        dataSource.setUser("sa");
        return dataSource;
    }

	// 12/26修正
    @Bean
    DataSource dataSource(){
        return new TransactionAwareDataSourceProxy(
                    new Log4jdbcProxyDataSource(
                            this.realDataSource()
                    )
        );
    }

    @Bean
    Dialect dialect(){
        return new H2Dialect();
    }

    @Bean
    SqlFileRepository sqlFileRepository(){
        return new NoCacheSqlFileRepository();
    }

    @Bean
    Config config(){
        return new Config() {
            @Override
            public DataSource getDataSource() {
                return dataSource();
            }

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

            @Override
            public SqlFileRepository getSqlFileRepository(){
                return sqlFileRepository();
            }
        };
    }
}

Dao

DBにアクセスするためのInterfaceを作成します。
これらのクラスには@Daoをつけます。

ここで作ったメソッド1つ1つにSQLが対応します。

CustomerRepository.java
package com.example.datasource;

import com.example.domain.CustomerEntity;
import org.seasar.doma.*;
import org.seasar.doma.jdbc.Result;

import java.util.List;

@DomaRepository
@Dao
public interface CustomerRepository {

    @Select
    public List<CustomerEntity> findAllOrderByName();

    @Select
    public CustomerEntity findById(Integer id);

    @Insert
    public Result<CustomerEntity> insert(CustomerEntity customerEntity);

    @Update
    public Result<CustomerEntity> update(CustomerEntity customerEntity);

    @Delete
    public Result<CustomerEntity> delete(CustomerEntity customerEntity);
}

@DomaRepositoryはSpringのAutowiredに対応するためのアノテーションです。
CustomerServiceでAutowiredしているのでそのために必要になります。
SpringBootとDomaを連携するを参考に作りました)

DomaRepository.java
package com.example.datasource;

import org.seasar.doma.AnnotateWith;
import org.seasar.doma.Annotation;
import org.seasar.doma.AnnotationTarget;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@AnnotateWith(annotations = {
    @Annotation(target = AnnotationTarget.CLASS, type = Component.class),
    @Annotation(target = AnnotationTarget.CONSTRUCTOR, type= Autowired.class)
})
public @interface DomaRepository {
}

検索処理

SQLは、
src/main/resources/META-INF/<package名>/<class名>/<method名>.sql
という形式で作成します。

例えば以下のメソッドの場合は

    @Select
    public CustomerEntity findById(Integer id);

このようなファイルを作ります。
src/main/resources/META-INF/com/example/datasource/CustomerRepository/findById.sql

中身はこのようになってます。
DomaのSQLは2WaySQLといって、そのまま実行できることが特徴です。

findById.sql
select * from customers where id = /* id */1;

[参考] http://doma.seasar.org/reference/sqlfile.html

SQLファイルは、SQL文を格納したテキストファイルで、Daoのメソッドにマッピングされます。 SQLのブロックコメント(/* */)や行コメント(--)を使用することで、バインド変数や動的なSQLのための条件分岐を表現できます。 SQLのツールでそのままそのSQLを実行できるように、バインド変数にはテスト用のデータを指定します。テスト用のデータは、実行時には使用されません。 たとえば、SQLファイルには次のようなSQL文が格納されます。

select * from employee where employee_id = /* employeeId */99

ここでは、ブロックコメントで囲まれた employeeIdがDaoインタフェースのメソッドのパラメータに対応し、 直後の 99はテスト用の条件になります。

結果を格納するオブジェクト(Entity)

結果はEntityに格納されます。
今回はオブジェクトをimmutableにしてます。

Selectで抽出したカラムとオブジェクトの変数との関係は@Entityのnamingで指定しています。
NamingType.SNAKE_UPPER_CASEを使うと、lastNameという変数にはLAST_NAMEというカラムの値が入ります。
@Tableのテーブル名はSQLファイルに書かれてるので検索に関しては使ってないです。

CustomerEntity.java
package com.example.domain;

import lombok.Getter;
import org.seasar.doma.*;
import org.seasar.doma.jdbc.entity.NamingType;

@Entity(naming = NamingType.SNAKE_UPPER_CASE, immutable = true)
@Table(name = "customers")
public class CustomerEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(sequence = "seq_customers")
    @Getter
    private final Integer id;

    @Getter
    private final String lastName;

    @Getter
    private final String firstName;

    public CustomerEntity( String lastName, String firstName ){
        // DBの自動採番を使うためにnullを設定
        this.id = null;
        this.lastName = lastName;
        this.firstName = firstName;
    }

    public CustomerEntity( Integer id ,String lastName, String firstName ){
        this.id = id;
        this.lastName = lastName;
        this.firstName = firstName;
    }
}

登録/更新処理

登録/更新処理は専用のアノテーションを付けることで実現していますので、別にSQLを作る必要はありません。登録/更新したいEntityを引数で渡して実行するだけです。

    @Insert
    public Result<CustomerEntity> insert(CustomerEntity customerEntity);

    @Update
    public Result<CustomerEntity> update(CustomerEntity customerEntity);

ただ、IDの自動採番については少々はまりました。
Entityの中で@GeneratedValueを使って、H2のシーケンステーブルから採番するように記載していましたが、Entityをimmutableにしていたため、何を設定するかで困りました。

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(sequence = "seq_customers")
    @Getter
    private final Integer id;

適当な値(1とか)ではダメで、nullを設定しておかないとInsert時に自動採番できないようです。
そのためコンストラクタを2つ用意しています。

Lombokの自動生成とも相性が悪く、IntelliJでは@AllArgsConstructor使えませんでした(Eclipseでは使えるらしい)

ビルド

DomaのSQLファイルをコンパイルより前に出力先Dirにコピーするために以下のように依存関係を変える必要があるようです。

build.gradle
processResources.destinationDir = compileJava.destinationDir
compileJava.dependsOn processResources

まとめ

SpringBootでDoma,Gradleを試してみました。
Domaは思った以上に簡単に使えたので気に入りました d(゚∀゚d)
簡単なアプリならこれで十分ですね。

SpringBootで作ったRestAPI/Webアプリのテストを書いてみた」につづく。

58
64
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
58
64

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?