Edited at

Doma2+Spring5+Camel2 環境構築・サンプルプログラム(メモ)


はじめに

Doma2+Spring5+Camel2での環境構築とサンプルプログラムのメモです。

基本は以下のページを参考に構築しています。

はじめよう! (Eclipse)

https://doma.readthedocs.io/en/2.20.0/getting-started-eclipse/

今回の目的はDoma2のお勉強。

Doma2は2way SQLやJRE 以外のライブラリへの依存がないことが特徴のORマッパーです。

他に有名なORマッパー(MyBatis, Hibernate)はあるのですが、重厚長大で学習コストが高いので今回Doma2を試してみます。


環境

使用したライブラリ・フレームワーク・ミドルウェア・IDEは以下のとおり。


  • Doma 2.24.0

  • Oracle JDK8

  • Spring 5

  • Apache Camel 2.23.0

  • PostgreSQL 9.6

  • Pleiades 4.8.0(Photon)


環境構築


JDK8をインストール

JDK8をインストールしましょう(省略)

※Eclipse内蔵のJavaでも良いのですが、一応インストールし後でEclipseに設定しておきます。


Pleiades(日本語化Eclipse)をインストール

少し古いですが、以下のURLから「Photon (4.8.0) Windows 64bit Full Java」をダウンロードし、Cドライブ直下に展開。

http://mergedoc.osdn.jp/

image.png


Eclipse プラグイン Doma Tools のインストール

Domaを使う上で便利なEclipse用プラグイン「Doma Tools」をインストールします。

まずは、Eclipseを起動して「ヘルプ」-「新規ソフトウェアのインストール」を選択します。

image.png

「インストール」ダイアログが表示されるので、「作業対象」に以下のURLを入力して「追加」ボタンを押します。

image.png

「リポジトリーの追加」ダイアログが表示されるので、ここでは「Doma」(任意)と入力して、「追加」ボタンを押します。

image.png

「Doma Tools」にチェックを入れて「次へ」ボタンを押します。

image.png

「次へ」ボタンを押します。

image.png

「使用条件の条項に同意します」を選択し、「完了」ボタンを押します。

image.png


Eclipseプロジェクト作成

EclipseでMavenプロジェクトを作成します。(手順省略)

プロジェクト名はとりあえず、「domasimple」にしました。

pom.xmlにDoma2を追加します。他のライブラリは(それほど意味はありませんが)後で追加します。

    <dependencies>

<dependency>
<groupId>org.seasar.doma</groupId>
<artifactId>doma</artifactId>
<version>2.24.0</version>
</dependency>
</dependencies>

以下のURLを参考にプロジェクトの設定を行います。

Doma ビルド

https://doma.readthedocs.io/en/2.20.0/build/

domasimpleプロジェクトのプロパティーで、「Javaコンパイラー」-「注釈処理」を選択し、以下のように設定します。


  • 「プロジェクト固有の設定を可能にする」にチェックを入れる

  • 「注釈処理を使用可能にする」にチェックを入れる

image.png

「Javaコンパイラー」-「注釈処理」-「ファクトリー・パス」を選択し、以下のように「doma-2.24.0.jar」を追加します。

image.png


サンプルプログラムの作成

Insert/Update/Select/Deleteを実行するだけのサンプルプログラムを作成します。

一通りの動きを確認するためだけなので、CRUD操作するデータは固定にしています。

ファイルの構成は以下のとおり。

>tree /F

│ pom.xml
├─src
│ ├─main
│ │ ├─java
│ │ │ └─example
│ │ │ └─doma
│ │ │ │ AppConfig.java
│ │ │ │ Main.java
│ │ │ │
│ │ │ ├─dao
│ │ │ │ DomaRepository.java
│ │ │ │ EmployeeDao.java
│ │ │ │
│ │ │ ├─entity
│ │ │ │ Employee.java
│ │ │ │
│ │ │ └─processors
│ │ │ DeleteEmployee.java
│ │ │ InsertEmployee.java
│ │ │ SelectEmployee.java
│ │ │ UpdateEmployee.java
│ │ │
│ │ └─resources
│ │ │ camel-context.xml
│ │ │ log4j2.xml
│ │ │
│ │ └─META-INF
│ │ └─example
│ │ └─doma
│ │ └─dao
│ │ └─EmployeeDao
│ │ selectAll.sql
│ │ selectById.sql

まず、pom.xmlを以下のように修正する。

    <properties>

<camel.version>2.23.0</camel.version>
<log4j2.version>2.8.2</log4j2.version>
</properties>

<dependencies>

<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring</artifactId>
<version>${camel.version}</version>
</dependency>

<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.seasar.doma</groupId>
<artifactId>doma</artifactId>
<version>2.24.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.5</version>
</dependency>
</dependencies>


テーブルを作成する

employeeテーブルをPostgreSQLに作成します。

PostgreSQLの環境構築は省略。

create table employee (

id serial not null primary key,
name varchar(255) not null,
age integer not null,
version integer not null);


Entityを作成する

employeeテーブルに対応した、Entityクラスを作成します。

getter/setterは不要です。

@Idで主キーになります。@GeneratedValueで自動生成を指定。

@Entity

public class Employee {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer id;
public String name;
public Integer age;

@Version
public Integer version;

@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", age=" + age
+ ", version=" + version + "]";
}
}


DAOインターフェースを作成する

DAOインターフェースを作成します。

実装クラスは自動で生成されるため、作成不要。これは楽だ。

それぞれ、@Select, @Insert, @Update, @Deleteのアノテーションを付与します。

SelectのSQLは自動生成されないため、後でSQLファイルにSQLを書きます。

それ以外のSQLは自動生成なので、ここでインターフェースを定義するだけです。

@Dao

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

@Select
List<Employee> selectAll();

@Select
Employee selectById(Integer id);

@Insert
int insert(Employee employee);

@Update
int update(Employee employee);

@Delete
int delete(Employee employee);
}


Domaのコンフィグを作成する

Domaのコンフィグを作成します。

public class AppConfig implements Config {

private DataSource dataSource;
private Dialect dialect;
private SqlFileRepository sqlFileRepository;

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

public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}

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

public void setDialect(Dialect dialect) {
this.dialect = dialect;
}

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

public void setSqlFileRepository(SqlFileRepository sqlFileRepository) {
this.sqlFileRepository = sqlFileRepository;
}
}


Camelのルートを定義する

Camelのルートとデータソース等を定義します。


camel-context.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:camel="http://camel.apache.org/schema/spring"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd"
>

<context:component-scan base-package="example.doma.*" />

<bean id="domaConfig"
class="example.doma.AppConfig">
<property name="dataSource" ref="dataSource" />
<property name="dialect" ref="dialect" />
<property name="sqlFileRepository" ref="sqlFileRepository" />
</bean>

<bean id="dialect"
class="org.seasar.doma.jdbc.dialect.PostgresDialect" />

<bean id="sqlFileRepository"
class="org.seasar.doma.jdbc.GreedyCacheSqlFileRepository" />

<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="jdbcUrl" value="jdbc:postgresql://192.168.20.71:5432/testdb" />
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="minimumIdle" value="10" />
<property name="maximumPoolSize" value="100" />
<property name="connectionTimeout" value="30000" />
<property name="idleTimeout" value="600000" />
<property name="maxLifetime" value="1800000" />
<property name="username" value="postgres" />
<property name="password" value="postgres" />
<property name="connectionInitSql" value="SELECT 1" />
<property name="autoCommit" value="false" />
<property name="validationTimeout" value="5000" />
<property name="registerMbeans" value="true" />
<property name="poolName" value="testHikariPool" />
</bean>

<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"
destroy-method="close">
<constructor-arg ref="hikariConfig" />
</bean>

<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>

<bean id="insertEmployee" class="example.doma.processors.InsertEmployee" />
<bean id="updateEmployee" class="example.doma.processors.UpdateEmployee" />
<bean id="selectEmployee" class="example.doma.processors.SelectEmployee" />
<bean id="deleteEmployee" class="example.doma.processors.DeleteEmployee" />

<camelContext
xmlns="http://camel.apache.org/schema/spring">

<route>
<from uri="timer:trigger?repeatCount=1" />
<transacted />
<process ref="insertEmployee" />
<process ref="deleteEmployee" />
<process ref="updateEmployee" />
<process ref="selectEmployee" />
</route>
</camelContext>
</beans>


ルート中の「」でトランザクションが開始されます。


Camelのプロセッサを作成する

Insert/Update/Select/Deleteを実行するCamelのプロセッサを作成します。

一通りの動きを確認するためだけなので、CRUD操作するデータは固定にしています。


InsertEmployee.java

@Component

public class InsertEmployee implements Processor {

private Logger logger = LoggerFactory.getLogger(InsertEmployee.class);

@Autowired
private EmployeeDao dao;

@Override
public void process(Exchange exchange) throws Exception {
Employee employee = new Employee();
employee.age = 20;
employee.name = "hoge";

dao.insert(employee);
}
}



DeleteEmployee.java

@Component

public class DeleteEmployee implements Processor {

private Logger logger = LoggerFactory.getLogger(DeleteEmployee.class);

@Autowired
private EmployeeDao dao;

@Override
public void process(Exchange exchange) throws Exception {
List<Employee> list = dao.selectAll();

Optional<Employee> emp = list.stream().findFirst();

if (emp.isPresent()) {
Employee employee = emp.get();
dao.delete(employee);
}
}
}



SelectEmployee.java

@Component

public class SelectEmployee implements Processor {

private Logger logger = LoggerFactory.getLogger(SelectEmployee.class);

@Autowired
private EmployeeDao dao;

@Override
public void process(Exchange exchange) throws Exception {
Employee emp = dao.selectById(11); // がちがち固定!

logger.info(emp.toString());
}
}



UpdateEmployee.java

@Component

public class UpdateEmployee implements Processor {

private Logger logger = LoggerFactory.getLogger(UpdateEmployee.class);

@Autowired
private EmployeeDao dao;

@Override
public void process(Exchange exchange) throws Exception {
List<Employee> list = dao.selectAll();

Optional<Employee> emp = list.stream().findFirst();

if (emp.isPresent()) {
Employee employee = emp.get();
employee.age += 10;
dao.update(employee);
}
}
}



Doma用のSQLファイルを作成する

DAOインターフェースに対応した2つのSELECT文を作成します。

作成先ディレクトリは、DAOインターフェースと対応したディレクトリになります。

 src/main/resources/META-INF/example/doma/dao/EmployeeDao


selectById.sql

select

/*%expand*/*
from
employee
where
id = /* id */0


selectAll.sql

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



サンプルプログラムを動かす

サンプルプログラムを動かすために以下のメインクラスを作成し実行します。


Main.java

public class Main {

static Logger logger = LoggerFactory.getLogger(Main.class);

public static void main(String[] args) throws Exception {
logger.info("start ");
try (ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("camel-context.xml")) {
applicationContext.start();
Thread.sleep(30000);
}
}
}


動かしてみるだけのメモなので雑ですがこれで終わり。今後いろいろ試行して使えるレベルまで学習するつもり。

感想

・Domaの2way SQLは控えめに言って最高。というか、他に楽なORマッパーがない。

・Camelのトランザクション管理は簡単・らくちん。生産性の高さでDomaとも相性良さそう。

・今回は使用しなかったが、Doma-Genも使えばさらに楽に開発できそう。

最後にサンプルプログラムは以下のサンプルプロジェクトを参考にしています。

simple-boilerplate

https://github.com/domaframework/simple-boilerplate/blob/master/src/main/resources/META-INF/boilerplate/dao/AppDao/create.script