TERASOLUNA5.xのブランクプロジェクトをPostgreSQL対応に変更する方法


## 1. はじめに
[TERASOLUNA Server Framework for Java (5.x)](http://terasolunaorg.github.io/guideline/) のブランクプロジェクトの初期状態では、データベースとしてインメモリのh2dbを利用する設定になっています。今回はデータベースをPostgreSQLに変更する方法について、個人的な備忘録として残したいと思います。

### 1.1. 検証環境
* Java 1.8.0_131
* TERASOLUNA Server Framework for Java 5.3.0
* PostgreSQL 9.3
* Apache Tomcat 8.0.44

### 1.2. PostgreSQL対応の変更作業の流れ
ブランクプロジェクトをPostgreSQL対応に変更する作業の流れを示します。
なお、今回はアプリケーションサーバ（Servletコンテナ）のデータソースに定義したコネクションプールではなく、アプリケーション内で用意したコネクションプールを利用する方法とします。

| 項番 | 作業 | 概要 |
|:---:|:-----|:-----|
| 1 |[ブランクプロジェクトの作成](#2-ブランクプロジェクトの作成) |TERASOLUNAのガイドラインに従ってブランクプロジェクトを作成します。 |
| 2 |[利用するデータベースの準備](#3-利用するデータベースの準備) |localhostに立てたPostgreSQLにデータベースとユーザを作成します。 |
| 3 |[利用するデータベースのライブラリを親pomの依存関係に追加](#4-利用するデータベースのライブラリを親pomの依存関係に追加) |PostgreSQLのJDBCドライバを読み込むように依存関係を追加します。 |
| 4 |[アプリケーション起動時のDB初期化処理を無効](#5-アプリケーション起動時のdb初期化処理を無効) |初期状態ではh2dbのデータベース初期化処理が設定されているため、これを無効にします。 |
| 5 |[（オプション）log4jdbcによるJDBCログ書き込みを無効](#6-オプションlog4jdbcによるjdbcログ書き込みを無効) |デバッグ用のlog4jdbcの設定を無効にします。 |
| 6 |[データベース接続先の変更](#7-データベース接続先の変更) |今回用意したデータベースに接続するように設定します。 |
| 7 |[利用するデータベースのライブラリをdomainプロジェクトの依存関係に追加](#8-利用するデータベースのライブラリをdomainプロジェクトの依存関係に追加) |domainのテスト時にもJDBCドライバを読み込むように依存関係を追加します。 |
| 8 |[（オプション）initdbプロジェクトの修正](#9-オプションinitdbプロジェクトの修正) |initdbにもデータベースの接続情報の設定箇所があるためこれを修正します。 |
| 9 |[（オプション）initdbプロジェクトを利用したデータベースの構築](#10-オプションinitdbプロジェクトを利用したデータベースの構築) |initdbを利用して2で準備したデータベースの構築,初期化処理を行います。 |
| 10 |[お試しビルド](#11-お試しビルド) |設定に不備がないことを確認するためお試しビルドを行います。 |
| 11 |[お試し起動](#12-お試し起動) |設定に不備がないことを確認するためお試し起動を行います。 |

## 2. ブランクプロジェクトの作成
公開されているガイドラインの「[3.1.2. 開発プロジェクトの作成](http://terasolunaorg.github.io/guideline/5.3.0.RELEASE/ja/ImplementationAtEachLayer/CreateWebApplicationProject.html#createwebapplicationproject)」に記載されている手順でブランクプロジェクトを作成します。
今回、プロジェクトの設定は以下の通りとし、`C:\work`をカレントディレクトリとしてブランクプロジェクトを作成することとします。

| 項番 | 項目 | 値 | 備考 |
|:---:|:-----|:---|:----|
| 1 | archetypeArtifactId | terasoluna-gfw-multi-web-blank-mybatis3-archetype |MyBatisを利用したマルチプロジェクトで作成 |
| 2 | archetypeVersion | 5.3.0.RELEASE |バージョンは5.3.0 |
| 3 | groupId | com.example.demo.postgresql | |
| 4 | artifactId | demo-database-postgresql | |

```console:mvnコマンドでプロジェクトを作成
mvn archetype:generate -B^
 -DarchetypeGroupId=org.terasoluna.gfw.blank^
 -DarchetypeArtifactId=terasoluna-gfw-multi-web-blank-mybatis3-archetype^
 -DarchetypeVersion=5.3.0.RELEASE^
 -DgroupId=com.example.demo.postgresql^
 -DartifactId=demo-database-postgresql^
 -Dversion=1.0.0-SNAPSHOT
```

```console:実行結果
... 省略 ...
[INFO] Parameter: groupId, Value: com.example.demo.postgresql
[INFO] Parameter: artifactId, Value: demo-database-postgresql
[INFO] Parent element not overwritten in C:\work\demo-database-postgresql\demo-database-postgresql-env\pom.xml
[INFO] Parent element not overwritten in C:\work\demo-database-postgresql\demo-database-postgresql-domain\pom.xml
[INFO] Parent element not overwritten in C:\work\demo-database-postgresql\demo-database-postgresql-web\pom.xml
[INFO] Parent element not overwritten in C:\work\demo-database-postgresql\demo-database-postgresql-initdb\pom.xml
[INFO] Parent element not overwritten in C:\work\demo-database-postgresql\demo-database-postgresql-selenium\pom.xml
[INFO] Project created from Archetype in dir: C:\work\demo-database-postgresql
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 26.941 s
[INFO] Finished at: 2017-08-31T10:12:00+09:00
[INFO] Final Memory: 10M/48M
[INFO] ------------------------------------------------------------------------

C:\work>
```

`BUILD SUCCESS`と表示され、`C:\work`ディレクトリ配下にartifactIdで指定した`demo-database-postgresql`ディレクトリが作成されていれば成功です。

## 3. 利用するデータベースの準備
利用するデータベースやユーザを作成し、DDLが流せる状態にします。
今回はlocalhostに用意したPostgreSQLに、以下の設定でデータベースを準備しました。

| 項番 | 項目 | 値 |
|:---:|:-----|:---|
| 1 | ポート番号 | 5432 |
| 2 | データベース名 | demodb |
| 3 | ユーザID | demoUser |
| 4 | パスワード | demoPass |

## 4. 利用するデータベースのライブラリを親pomの依存関係に追加
初期状態ではOracleとPostgreSQLの設定がコメントアウトされた状態で記述されています。
PostgreSQLのコメントを外して有効にします。

* 対象ファイル：`C:\work\demo-database-postgresql\pom.xml`

```xml:修正前
            <!-- == Begin Database == -->
<!--             <dependency> -->
<!--                 <groupId>org.postgresql</groupId> -->
<!--                 <artifactId>postgresql</artifactId> -->
<!--                 <version>${postgresql.version}</version> -->
<!--             </dependency> -->
<!--             <dependency> -->
<!--                 <groupId>com.oracle</groupId> -->
<!--                 <artifactId>ojdbc7</artifactId> -->
<!--                 <version>${ojdbc.version}</version> -->
<!--             </dependency> -->
            <!-- == End Database == -->
```

```xml:修正後
            <!-- == Begin Database == -->
             <dependency>
                 <groupId>org.postgresql</groupId>
                 <artifactId>postgresql</artifactId>
                 <version>${postgresql.version}</version>
             </dependency>
<!--             <dependency> -->
<!--                 <groupId>com.oracle</groupId> -->
<!--                 <artifactId>ojdbc7</artifactId> -->
<!--                 <version>${ojdbc.version}</version> -->
<!--             </dependency> -->
            <!-- == End Database == -->
```

## 5. アプリケーション起動時のDB初期化処理を無効
ブランクプロジェクトの初期状態ではDIコンテナの起動時つまりアプリケーションの起動時に、`<jdbc:initialize-database>`で指定されたDDLファイルを実行するようになっています。
インメモリデータベースを利用する場合には必要ですが、一般的なWebアプリケーションではこの機能は不要かと思います。
`<jdbc:initialize-database>`の記述をコメントアウトして無効にします。

* 対象ファイル：`C:\work\demo-database-postgresql\demo-database-postgresql-env\src\main\resources\META-INF\spring\demo-database-postgresql-env.xml`

```xml:修正前
    <jdbc:initialize-database data-source="dataSource"
        ignore-failures="ALL">
        <jdbc:script location="classpath:/database/${database}-schema.sql" encoding="UTF-8" />
        <jdbc:script location="classpath:/database/${database}-dataload.sql" encoding="UTF-8" />
    </jdbc:initialize-database>
```

```xml:修正後
    <!--
    <jdbc:initialize-database data-source="dataSource"
        ignore-failures="ALL">
        <jdbc:script location="classpath:/database/${database}-schema.sql" encoding="UTF-8" />
        <jdbc:script location="classpath:/database/${database}-dataload.sql" encoding="UTF-8" />
    </jdbc:initialize-database>
    -->
```

## 6. （オプション）log4jdbcによるJDBCログ書き込みを無効
`@Transactional(readOnly = true)`でトランザクションを読み取り専用にするとlog4jdbcのログ書き込みでエラーメッセージのログが出力されます。

```console:出力されるエラーメッセージのログ
date:2017-08-31 20:04:25	thread:http-nio-8080-exec-2	X-Track:b67e7712dcc143f398f5e51b762e3dea	level:ERROR	logger:jdbc.audit                                      	message:2. Connection.setReadOnly(true)
org.postgresql.util.PSQLException: トランザクションの最中に読み出し専用プロパティを変えることはできません。
	at org.postgresql.jdbc2.AbstractJdbc2Connection.setReadOnly(AbstractJdbc2Connection.java:741)
	at org.apache.commons.dbcp2.DelegatingConnection.setReadOnly(DelegatingConnection.java:562)
	at org.apache.commons.dbcp2.DelegatingConnection.setReadOnly(DelegatingConnection.java:562)
	at net.sf.log4jdbc.ConnectionSpy.setReadOnly(ConnectionSpy.java:374)
... 省略 ...
```

アプリケーションで「読み取り専用」に設定しているのに、log4jdbcでは「読み書き可能」なトランザクションを必要としているため当然と言えば当然です。
エラーメッセージがログに出力されるだけなので、アプリケーションの処理に影響はありませんが、個人的に気になるため、いつもlog4jdbcを無効にしています。

無効にする方法は簡単で、Log4jdbcProxyDataSourceのBean定義を削除し、BeanIdのdataSourceはdbcp2のデータソースを指定するように変更するだけです。

* 対象ファイル：`C:\work\demo-database-postgresql\demo-database-postgresql-env\src\main\resources\META-INF\spring\demo-database-postgresql-env.xml`

```xml:修正前
    <bean id="realDataSource" class="org.apache.commons.dbcp2.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${database.driverClassName}" />
        <property name="url" value="${database.url}" />
        <property name="username" value="${database.username}" />
        <property name="password" value="${database.password}" />
        <property name="defaultAutoCommit" value="false" />
        <property name="maxTotal" value="${cp.maxActive}" />
        <property name="maxIdle" value="${cp.maxIdle}" />
        <property name="minIdle" value="${cp.minIdle}" />
        <property name="maxWaitMillis" value="${cp.maxWait}" />
    </bean>


    <bean id="dataSource" class="net.sf.log4jdbc.Log4jdbcProxyDataSource">
        <constructor-arg index="0" ref="realDataSource" />
    </bean>
```

```xml:修正後
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${database.driverClassName}" />
        <property name="url" value="${database.url}" />
        <property name="username" value="${database.username}" />
        <property name="password" value="${database.password}" />
        <property name="defaultAutoCommit" value="false" />
        <property name="maxTotal" value="${cp.maxActive}" />
        <property name="maxIdle" value="${cp.maxIdle}" />
        <property name="minIdle" value="${cp.minIdle}" />
        <property name="maxWaitMillis" value="${cp.maxWait}" />
    </bean>

    <!--
    <bean id="dataSource" class="net.sf.log4jdbc.Log4jdbcProxyDataSource">
        <constructor-arg index="0" ref="realDataSource" />
    </bean>
    -->
```

## 7. データベース接続先の変更
今回はアプリケーション内のdbcp2のコネクションプールを利用する方法とします。
そのためデータベースの接続情報（URL、ポート番号、ユーザID、パスワード等）はアプリケーション内で管理する必要があります。
TERASOLUNA5.xのデフォルトでは`***infra.properties`に定義します。
なお、JNDIでアプリケーションサーバのdatasourceを利用する場合は別の機会に説明する予定です。

* 対象ファイル：`C:\work\demo-database-postgresql\demo-database-postgresql-env\src\main\resources\META-INF\spring\demo-database-postgresql-infra.properties`

```properties:修正前
database=H2
database.url=jdbc:h2:mem:demo-database-postgresql;DB_CLOSE_DELAY=-1
database.username=sa
database.password=
database.driverClassName=org.h2.Driver
# connection pool
cp.maxActive=96
cp.maxIdle=16
cp.minIdle=0
cp.maxWait=60000
```

```properties:修正後
### default setting is H2
# database=H2
# database.url=jdbc:h2:mem:demo-database-postgresql;DB_CLOSE_DELAY=-1
# database.username=sa
# database.password=
# database.driverClassName=org.h2.Driver

### setting for postgreSQL
database=POSTGRESQL
database.url=jdbc:postgresql://127.0.0.1:5432/demodb
database.username=demoUser
database.password=demoPass
database.driverClassName=org.postgresql.Driver

# connection pool
cp.maxActive=96
cp.maxIdle=16
cp.minIdle=0
cp.maxWait=60000
```

## 8. 利用するデータベースのライブラリをdomainプロジェクトの依存関係に追加
初期状態ではOracleとPostgreSQLの設定がコメントアウトされた状態で記述されています。
PostgreSQLのコメントを外して有効にします。

* 対象ファイル：`C:\work\demo-database-postgresql\demo-database-postgresql-domain\pom.xml`

```xml:修正前
        <!-- == Begin Database == -->
<!--         <dependency> -->
<!--             <groupId>org.postgresql</groupId> -->
<!--             <artifactId>postgresql</artifactId> -->
<!--             <scope>test</scope> -->
<!--         </dependency> -->
<!--         <dependency> -->
<!--             <groupId>com.oracle</groupId> -->
<!--             <artifactId>ojdbc7</artifactId> -->
<!--             <scope>test</scope> -->
<!--         </dependency> -->
        <!-- == End Database == -->
```

```xml:修正後
        <!-- == Begin Database == -->
         <dependency>
             <groupId>org.postgresql</groupId>
             <artifactId>postgresql</artifactId>
             <scope>test</scope>
         </dependency>
<!--         <dependency> -->
<!--             <groupId>com.oracle</groupId> -->
<!--             <artifactId>ojdbc7</artifactId> -->
<!--             <scope>test</scope> -->
<!--         </dependency> -->
        <!-- == End Database == -->
```

## 9. （オプション）initdbプロジェクトの修正
initdbプロジェクトにもデータベースの接続情報を保持しています。といってもinitdbプロジェクト自体がデータベースの初期構築を行うためのものであるため、initdbを利用しない場合は修正の必要はありません。
修正する箇所はデータベースの接続情報を設定する`<db.url>、<db.username>、<db.password>`になります。

* 対象ファイル：`C:\work\demo-database-postgresql\demo-database-postgresql-initdb\pom.xml`

```xml:修正前
        <profile>
            <id>local-postgres</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <db.encoding>UTF8</db.encoding>
                <db.basedir>${project.basedir}/src/main/sqls/postgres</db.basedir>
                <db.url>jdbc:postgresql://127.0.0.1:5432/projectName</db.url>
                <db.username>postgres</db.username>
                <db.password>P0stgres</db.password>
                <db.driver>org.postgresql.Driver</db.driver>
                <db.groupId>org.postgresql</db.groupId>
                <db.artifactId>postgresql</db.artifactId>
                <db.version>${postgresql.version}</db.version>
                <db.delimiterType>row</db.delimiterType>
            </properties>
        </profile>
```

```xml:修正後
        <profile>
            <id>local-postgres</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <db.encoding>UTF8</db.encoding>
                <db.basedir>${project.basedir}/src/main/sqls/postgres</db.basedir>
                <db.url>jdbc:postgresql://127.0.0.1:5432/demodb</db.url>
                <db.username>demoUser</db.username>
                <db.password>demoPass</db.password>
                <db.driver>org.postgresql.Driver</db.driver>
                <db.groupId>org.postgresql</db.groupId>
                <db.artifactId>postgresql</db.artifactId>
                <db.version>${postgresql.version}</db.version>
                <db.delimiterType>row</db.delimiterType>
            </properties>
        </profile>
```

## 10. （オプション）initdbプロジェクトを利用したデータベースの構築

### 10.1. データベース構築で利用するDDLの作成
データベース構築で利用するDDLは前述の`<db.basedir>`で指定したディレクトリに格納します。
今回であれば`C:\work\demo-database-postgresql\demo-database-postgresql-initdb\src\main\sqls\postgres`になります。初期状態では存在しないのでこのディレクトリを作成します。

| 項番 | ファイル名 | 備考 |
|:----:|:----------|:----|
| 1 | 00000_drop_all_tables.sql | テーブルを削除するDDLを記述したファイルです。 |
| 2 | 00100_create_all_tables.sql | 必要なテーブルを作成するDDLを記述したファイルです。 |
| 3 | 00200_insert_employee.sql | 特定テーブル（employee）の初期データを登録するSQLを記述したファイルです。 |

ファイル名でソートして順次実行されるため、実行順序を意識してファイル名を付与します。
今回は作成していませんが、必要であればシーケンスやインデックスを作成するDDLについても用意してください。

```sql:00000_drop_all_tables.sql

DROP TABLE IF EXISTS employee;
```

```sql:00100_create_all_tables.sql

-- 社員テーブル --
CREATE TABLE employee (
    -- 社員ID
    employee_id    varchar(10)    NOT NULL,
    -- 氏名
    name           varchar(30)    NOT NULL,
    -- 入社日
    register_date  date           NOT NULL,
    -- ポイント
    point          numeric(3,0)   NOT NULL,
    -- 主キー制約
    CONSTRAINT EMPLOYEE_PK PRIMARY KEY(employee_id)
);
```

```sql:00200_insert_employee.sql

INSERT INTO employee (employee_id, name, register_date, point) VALUES ('0000000777', '試験 太郎', to_date('2016/04/01', 'yyyy/MM/dd'), 10);
COMMIT;
```

### 10.2. mvn sql:executeコマンドでデータベースを構築
initdbプロジェクトで`mvn sql:execute`コマンドを実行すると、前述で用意したDDLが実行され、データベースの構築、初期化処理が行われます。

```console:実行結果
C:\work\demo-database-postgresql\demo-database-postgresql-initdb>mvn sql:execute
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building demo-database-postgresql-initdb 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- sql-maven-plugin:1.5:execute (default-cli) @ demo-database-postgresql-initdb ---
[INFO] Executing file: C:\work\demo-database-postgresql\demo-database-postgresql-initdb\src\main\sqls\postgres\00000_dro
p_all_tables.sql
[INFO] Executing file: C:\work\demo-database-postgresql\demo-database-postgresql-initdb\src\main\sqls\postgres\00100_cre
ate_all_tables.sql
[INFO] Executing file: C:\work\demo-database-postgresql\demo-database-postgresql-initdb\src\main\sqls\postgres\00200_ins
ert_employee.sql
[INFO] 3 of 3 SQL statements executed successfully
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.217 s
[INFO] Finished at: 2017-08-31T10:56:24+09:00
[INFO] Final Memory: 8M/20M
[INFO] ------------------------------------------------------------------------

C:\work\demo-database-postgresql\demo-database-postgresql-initdb>
```

`3 of 3 SQL statements executed successfully`や`BUILD SUCCESS`を確認し、正常に終了したことを確認してください。

## 11. お試しビルド
PostgreSQLに対応させるため、多くの設定ファイルを修正しました。不備がないか確認するため、お試しビルドを行います。

```console:実行結果
C:\work\demo-database-postgresql>mvn package
[INFO] Scanning for projects...

... 省略 ...

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] TERASOLUNA Server Framework for Java (5.x) Web Blank Multi Project (MyBatis3) SUCCESS [  0.015 s]
[INFO] demo-database-postgresql-env ....................... SUCCESS [  1.248 s]
[INFO] demo-database-postgresql-domain .................... SUCCESS [  0.359 s]
[INFO] demo-database-postgresql-web ....................... SUCCESS [  4.883 s]
[INFO] demo-database-postgresql-initdb .................... SUCCESS [  0.031 s]
[INFO] demo-database-postgresql-selenium .................. SUCCESS [  2.090 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 9.048 s
[INFO] Finished at: 2017-08-31T10:57:46+09:00
[INFO] Final Memory: 35M/84M
[INFO] ------------------------------------------------------------------------

C:\work\demo-database-postgresql>
```

`BUILD SUCCESS`と表示されればビルド時に判明する箇所に不備がないことが確認できました。

## 12. お試し起動
お試しビルドが正常に終了していれば`
C:\work\demo-database-postgresql\demo-database-postgresql-web\target\demo-database-postgresql-web.war`が存在します。
`Tomcatのインストールディレクトリ/webapps`に`demo-database-postgresql-web.war`をコピーしてデプロイします。

`Tomcatのインストールディレクトリ/bin/startup.bat`を実行し、Tomcatを起動します。
Webブラウザで http://localhost:8080/demo-database-postgresql-web/ にアクセスし、デフォルト画面の「Hello world!」が表示されれば成功です。

## 13. さいごに
今回はTERASOLUNA5.xのブランクプロジェクトをPostgreSQL対応に変更する方法について説明しました。
通常、利用するデータベースの設定はプロジェクト作成の初回に行うだけなので、行う機会は少ないかと思います。
しかし、プロジェクトの初期設定が完了しないと、アプリケーション（業務処理）の開発に進めないため、すばやく対応したいものです。