はじめに
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ドライブ直下に展開。
Eclipse プラグイン Doma Tools のインストール
Domaを使う上で便利なEclipse用プラグイン「Doma Tools」をインストールします。
まずは、Eclipseを起動して「ヘルプ」-「新規ソフトウェアのインストール」を選択します。
「インストール」ダイアログが表示されるので、「作業対象」に以下のURLを入力して「追加」ボタンを押します。
「リポジトリーの追加」ダイアログが表示されるので、ここでは「Doma」(任意)と入力して、「追加」ボタンを押します。
「Doma Tools」にチェックを入れて「次へ」ボタンを押します。
「次へ」ボタンを押します。
「使用条件の条項に同意します」を選択し、「完了」ボタンを押します。
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コンパイラー」-「注釈処理」を選択し、以下のように設定します。
- 「プロジェクト固有の設定を可能にする」にチェックを入れる
- 「注釈処理を使用可能にする」にチェックを入れる
「Javaコンパイラー」-「注釈処理」-「ファクトリー・パス」を選択し、以下のように「doma-2.24.0.jar」を追加します。
サンプルプログラムの作成
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のルートとデータソース等を定義します。
<?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操作するデータは固定にしています。
@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);
}
}
@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);
}
}
}
@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());
}
}
@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
select
/*%expand*/*
from
employee
where
id = /* id */0
select /*%expand*/* from employee order by id
サンプルプログラムを動かす
サンプルプログラムを動かすために以下のメインクラスを作成し実行します。
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