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](https://qiita-image-store.s3.amazonaws.com/0/179607/53c9be62-e3f6-5d79-e06e-ed1d28cb2200.png)





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

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

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/179607/228aa74b-f906-3a4f-0b8d-af84f7be842e.png)

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

- http://dl.bintray.com/domaframework/eclipse/ 

![image.png](https://qiita-image-store.s3.amazonaws.com/0/179607/2b12cd04-ff0f-67a4-3dbf-475c6e4d833e.png)

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/179607/e143d0cf-b588-e034-a673-c1d47db86b86.png)

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/179607/f44d5018-e47e-27b5-8877-cbffe1084e79.png)

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/179607/e2cd3122-d3df-10ff-7669-cc0b74c58f92.png)

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/179607/3cf5d28f-0fea-6628-0863-9337b0df2350.png)



## Eclipseプロジェクト作成

EclipseでMavenプロジェクトを作成します。（手順省略）
プロジェクト名はとりあえず、「domasimple」にしました。

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

```xml
	<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](https://qiita-image-store.s3.amazonaws.com/0/179607/79ce09cb-264a-b002-0310-c22cb0903706.png)

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/179607/2b52a72f-46a6-4e6d-7454-fbf21f8a2380.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を以下のように修正する。

```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の環境構築は省略。

```sql
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で自動生成を指定。

```java
@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は自動生成なので、ここでインターフェースを定義するだけです。

```java
@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のコンフィグを作成します。

```java
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のルートとデータソース等を定義します。


```xml: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>
```

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



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

Insert/Update/Select/Deleteを実行するCamelのプロセッサを作成します。
一通りの動きを確認するためだけなので、CRUD操作するデータは固定にしています。

```java: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);
	}
}
```


```java: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);
		}
	}
}

```


```java: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());
	}
}

```


```java: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

```sql:selectById.sql
select
    /*%expand*/*
from
    employee
where
id = /* id */0
```

```sql:selectAll.sql
select /*%expand*/* from employee order by id

```



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

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

```java: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