Apache Derby + DBFlute IntroでDB構築＆配布する

# はじめに
データベースに接続するアプリケーションを作成しようとした場合、環境構築面で色々と面倒なことが多いです。
ちょっとO/Rマッパを試してみようとしても、DBサーバをローカルにインストールしたり、テーブルを作成してデータ投入したりといった具合です。
最近はDocker等コンテナ技術を使って楽に環境構築を行えるようにすることも可能な様ですが、教育用等でサンプルアプリケーションを配布して、できるだけ楽に環境構築して早く実行してもらいたいといった場合に少々敷居が高くなってしまいます。

できれば小容量で配布しやすく、即実行できる環境がほしい……そんな悩みを解決してくれそうなのが**Apache Derby**です。

### [Apache Derby](https://db.apache.org/derby/)とは
Apache Derbyとは、Java純製の軽量のRDBです。ファイルアクセスorメモリアクセスのみで手軽に実行でき、教育用やサンプルアプリケーション配布用にとても向いています。

### [DBFlute](http://dbflute.seasar.org/ja/introduction/index.html#about)とは
RDBの開発支援ツールで、ざっくり次の２つの機能があります。

- O/Rマッパー
- DB管理支援ツール

本記事は２つ目のDB管理支援ツールをメインに扱います。
これを使うとテーブル作成およびデータ投入を１バッチ実行で行えるようになります。
なお、O/Rマッパとしてのサンプルコードも少し書きます。

### [DBFlute Intro](http://dbflute.seasar.org/ja/manual/function/generator/intro/index.html#about)とは
DBFluteは様々な機能を備えており、それらを遂行するための専用エンジンが存在します。
そのため、DBFluteを最初に使う際、エンジンのダウンロードや環境設定等が必要が必要となりますが、そのような環境を簡単に整えるためのツールです。
実行可能jar形式で配布されており、Javaが使える環境であればすぐに動かすことができます（ https://github.com/dbflute/dbflute-intro/releases ）。

### お試しするO/Rマッパ
DBアクセス環境を整えたあと、お試しとして下記３種類のO/Rマッパを使ってみます。

- DBFlute
- MyBatis
- Spring JDBC

### バージョンについて
本記事では下記バージョンを使用します。

- Java 11（AdoptOpenJDK）
- Spring Boot 2.1.4
- DBFlute Intro 0.3.3
- Apache Derby Database Engine and Embedded JDBC Driver 10.14.2.0 ※
- DBFlute 1.1.9
- MyBatis 3.5.1 （MyBatis-Spring-Boot-Starter 2.0.1）
- Spring JDBC 5.1.6

※本記事を書いている時点で、Apache Derbyはより新しいバージョン10.15系がリリースされていますが、10.14系までのDriverクラス（org.apache.derby.jdbc.EmbeddedDriver）が使えなくなってしまっているため、10.14系を使うことにします。

### 本記事のソースコードについて
[ここから]([http://dossaribook.starfree.jp/public/qiita/20190430_sample-dbaccess_light.zip])ダウンロードできます。
DB接続可能な状態で、eclipse等にインポートすることで即実行できます。
※DBFluteエンジンは除外しています。
※ダウンロードリンクは予告なく変更される可能性があります。

# 手順
## 0. Eclipseダウンロード
せっかくなのでコーディング環境、実行環境を整えるところから始めます。
Eclipseである必要性はありませんが、面倒を省くため、ここでは[Pleiades All in One](http://mergedoc.osdn.jp/)に頼ることにします。
http://mergedoc.osdn.jp/ から、WindowまたはMacのjkd付きJava Full Editionをダウンロードします。

## 1. Apache Derbyのjarダウンロード
[Mavenリポジトリ](https://mvnrepository.com/artifact/org.apache.derby/derby)からバージョン10.14系のファイルを取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299510/1e2b64e8-1174-093d-433f-0118153d6d89.png)

## 2. Spring Initializerからプロジェクト作成
https://start.spring.io/ からSpring Bootプロジェクトを作成します。
その際、Spring Bootの依存に次を追加します。

- Apache Derby
- JDBC
- MyBatis

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299510/ce3643e2-99cc-06b7-5952-96a79c26219a.png)

ダウンロードしたファイルを適当な場所に解凍します（eclipseを使う場合はワークスペース下など）。
sample-dbaccess
├─ .gitignore
├─ HELP.md
├─ mvnw
├─ mvnw.cmd
├─ pom.xml
├─.mvn
└─src

## 3. DBFlute Introダウンロード、配置
githubからjarファイルを入手します。
https://github.com/dbflute/dbflute-intro/releases

ダウンロードしたファイルを、先ほど解凍したSpringのプロジェクト直下に配置します。
sample-dbaccess
├─ .gitignore
├─ **dbflute-intro.jar**
├─ HELP.md
├─ mvnw
├─ mvnw.cmd
├─ pom.xml
├─.mvn
└─src

## 4. DBFlute Introの実行
dbflute-intro.jarファイルを実行します。
後々のために、javaのパスが通っていない場合は通しておきます。

```cmd
set PATH=%PATH%;C:\path\to\eclipse_java\11\bin
java -jar dbflute-intro.jar
```

実行すると、内部でローカルサーバが立ち上がりブラウザで開かれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299510/8db78d6b-83dd-210e-3f63-2dd4a14c3c9e.png)


## 5. DBFluteエンジン初期セットアップ
必要な事項を入力して初期セットアップを行います。
入力が完了したら画面したの「Create」ボタンをクリックして実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299510/28a1f45f-79e0-2f44-7272-9459e3eb9c26.png)

入力の際は以下の点に注意する必要があります。

- Project Name: 任意
- DBMS: Apache Derby
- JDBC Driver: 先ほどダウンロードしたApache Derbyのjarファイル
- URL: jdbc:derby:*../_derbydata*;create=true
- Schema: APP　※1
- User: APP
- Password: 任意（空白でOK）

※1「APP」はデフォルトで用意されているスキーマで、これ以外を使おうとすると別途作成しなければならないので、「APP」を指定しておきます

また、O/RマッパとしてDBFluteをサンプルで使うため、画面右の「O/R Mapper settings」をクリックして入力可能な状態にして、次のものを入力します。

- Language: Java
- DI Container: Spring Framework
- Generation Package: 任意のパッケージ（ここで指定したパッケージ下にDBFluteのソースコードが生成されます）


「Create」ボタンをクリックして実行に成功すると、次の画面が表示されます。![setupdone_dbflute_intro.png](https://qiita-image-store.s3.amazonaws.com/0/299510/bd058562-914a-ff7b-1c31-06e2b0658010.png)

プロジェクト内にファイルが増えました。
sample-dbaccess
├─ .gitignore
├─ dbflute-intro.jar
├─ **dbflute_sample_dbaccess**
├─ **mydbflute**
├─ HELP.md
├─ mvnw
├─ mvnw.cmd
├─ pom.xml
├─.mvn
└─src

- mydbflute: DBFluteのエンジンです。基本的にここを編集することはありません。
- dbflute_*xxx*: プロジェクトごとのDBFluteの設定等が入ったフォルダです。「xxx」には初期セットアップで入力した「Project Name」が入ります。

## 6. DDL作成
先ほど作成されたプロジェクト固有のDBFluteフォルダ「dbflute_*xxx*」内のreplace-schema.sqlにテーブルcreate文等のDDLを入力します。

```sql:dbflute_sample_dbaccess/playsql/replace-schema.sql
create table DEPARTMENT (
	DEPARTMENT_ID int not null,
	DEPARTMENT_NAME varchar(100) not null,
	constraint PK_DEPARTMENT primary key(DEPARTMENT_ID)
);

create table EMPLOYEE (
	EMPLOYEE_ID int not null,
	EMPLOYEE_NAME varchar(100) not null,
	DEPARTMENT_ID int not null,
	constraint PK_EMPLOYEE primary key(EMPLOYEE_ID),
	constraint FK_EMPLOYEE_1 FOREIGN KEY (DEPARTMENT_ID) references DEPARTMENT(DEPARTMENT_ID)
);
```

## 7. 登録用データ作成
今回はtsv形式（タブを区切り文字としたデータ形式）で登録用データを作成します。
データ登録の際に特殊なルールがあり、特にディレクトリ名、ファイル名に注意が必要です。
詳細は「[データ登録(TSV)](http://dbflute.seasar.org/ja/manual/function/generator/task/replaceschema/loadtsv.html)」を参照してください。

```dbflute_sample_dbaccess/playsql/data/ut/tsv/UTF-8/01-department.tsv
DEPARTMENT_ID	DEPARTMENT_NAME
1	部署1
2	部署2
3	部署3
```

```dbflute_sample_dbaccess/playsql/data/ut/tsv/UTF-8/02-employee.tsv
EMPLOYEE_ID	EMPLOYEE_NAME	DEPARTMENT_ID
11	従業員11	1
12	従業員12	1
13	従業員13	1
21	従業員21	2
22	従業員22	2
23	従業員23	2
31	従業員31	3
32	従業員32	3
33	従業員33	3

```

## 8.スキーマ作成＆データ投入
DBFlute Introの画面で、作成したプロジェクトを選択して、Replace Schemaボタンを押してスキーマ作成＆データ投入を実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299510/fbd2e298-f234-62b8-7c9a-9c3067932fd9.png)

もしくは、manage.batファイルを実行してもOKです。
javaのパスが通っていることに注意してください。

```bat
cd dbflute_sample_dbaccess
manage.bat 0
```

プロジェクト直下にderby用のデータファイルが増えました。
ここで作成されるディレクトリ名は、DBFlute Introの初期セットアップで入力した接続URLで指定しています。
sample-dbaccess
├─ .gitignore
├─ **_derbydata**
├─ dbflute-intro.jar
├─ dbflute_sample_dbaccess
├─ mydbflute
├─ HELP.md
├─ mvnw
├─ mvnw.cmd
├─ pom.xml
├─.mvn
└─src


## 9. ソースコード自動生成
DBFlute用のソースコード自動生成がDBFlute Intro画面からできなかったので、コマンドラインから実行します。
manage.batを、引数「2: regenerate」で実行します（もしくは引数なしで実行して、後から入力することもできます）。
詳細は「[Manageタスク](http://dbflute.seasar.org/ja/manual/function/generator/task/manage/index.html#subtasknumber)」を参照してください。

```bat
cd dbflute_sample_dbaccess
manage.bat 2
```

成功すると、5. DBFluteエンジン初期セットアップで入力したパッケージ下に自動生成コードが出力されます。

## 10. Springの設定追加
Spring上でO/Rマッパを使用するための設定を追加します。

まずpom.xmlを編集します。Javaのバージョンを調整して、DBFluteの依存を追加します。

```xml:pom.xml
<properties>
    <java.version>1.11</java.version>
</properties>
<!-- 中略 -->
<dependencies>
    <!-- 色々な既存の依存関係に追加 -->
    <dependency>
        <groupId>org.dbflute</groupId>
        <artifactId>dbflute-runtime</artifactId>
        <version>1.1.9</version>
    </dependency>
</dependencies>
```

```src/main/resources/application.yml
# 共通のDB接続情報
spring:
  datasource:
    url: jdbc:derby:_derbydata
    username: APP
    driverClassName: org.apache.derby.jdbc.EmbeddedDriver
# DBFluteのログ設定。これを設定しておくとDBFluteにより発行されるSQLがログ出力される
logging:
  level:
    org.dbflute: DEBUG

# MyBatis用設定. lower_case のカラム名を camelCase のプロパティにマッピングする
mybatis:
  configuration:
    mapUnderscoreToCamelCase: true
```

### 注意1. ユーザ名について
derbyの接続文字列は`jdbc:ファイルパス`となります。
ここで、ファイルパスは、javaを実行する際の基底ディレクトからの相対パス（eclipse上から実行する場合、プロジェクト直下）、もしくは絶対パスを指定します。

### 注意2. eclipse上でエラーになる場合
プロジェクトを選択して、「Maven > プロジェクトの更新」を行ってみてください。

## 11. DBFluteお試し実行
次のファイルを追加して、お手軽にコマンドラインでSpringを起動します。

```SampleDbaccessDBFluteApplication.java
@SpringBootApplication
public class SampleDbaccessDBFluteApplication {

    @Autowired
    EmployeeBhv employeeBhv;

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(SampleDbaccessDBFluteApplication.class, args)) {
            SampleDbaccessDBFluteApplication app = ctx.getBean(SampleDbaccessDBFluteApplication.class);
            app.run(args);
        }
    }

    private void run(String... args) {
        System.out.println("処理開始");
        employeeBhv.selectList(cb -> {
            cb.setupSelect_Department();
            cb.query().setDepartmentId_Equal(2);
        }).forEach(employee -> {
            System.out.println(
                String.format(
                    "employeeName: %s, departmentName: %s",
                    employee.getEmployeeName(),
                    employee.getDepartment().get().getDepartmentName()));
        });
        //アプリの処理
        System.out.println("処理終了");
    }
}
```

## 12. MyBatisお試し実行

```SampleDbaccessMyBatisApplication.java
@SpringBootApplication
public class SampleDbaccessMyBatisApplication {

    @Autowired
    EmployeeMapper employeeMapper;

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(SampleDbaccessMyBatisApplication.class, args)) {
            SampleDbaccessMyBatisApplication app = ctx.getBean(SampleDbaccessMyBatisApplication.class);
            app.run(args);
        }
    }

    private void run(String... args) {
        System.out.println("処理開始");
        employeeMapper.findEmployeeList(2).forEach(employee -> {
            if (employee != null) {
                System.out.println(
                    String.format(
                        "employeeName: %s, departmentName: %s",
                        employee.getEmployeeName(),
                        employee.getDepartmentName()));
            } else {
                System.out.println("employee is null");
            }
        });
        //アプリの処理
        System.out.println("処理終了");
    }
}
```

```EmployeeMapper.java
@Mapper
public interface EmployeeMapper {
    @Select({ "select emp.EMPLOYEE_NAME as EMPLOYEENAME, dept.DEPARTMENT_NAME",
        "from EMPLOYEE emp",
        "inner join DEPARTMENT dept on dept.DEPARTMENT_ID = emp.DEPARTMENT_ID",
        "where emp.DEPARTMENT_ID = #{departmentId}", })
    List<Employee> findEmployeeList(int departmentId);
}
```

```Employee.java
public class Employee {
    private String employeeName;
    private String departmentName;
    
    // getter, setter
}
```

## 13. Spring JDBCお試し実行

```SampleDbaccessJDBCApplication.java
@SpringBootApplication
public class SampleDbaccessJDBCApplication {

    @Autowired
    JdbcTemplate jdbcTemplate;

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(SampleDbaccessJDBCApplication.class, args)) {
            SampleDbaccessJDBCApplication app = ctx.getBean(SampleDbaccessJDBCApplication.class);
            app.run(args);
        }
    }

    private void run(String... args) {
        System.out.println("処理開始");
        findEmployeeList(2).forEach(map -> {
            System.out.println(
                String.format(
                    "employeeName: %s, departmentName: %s",
                    map.get("EMPLOYEE_NAME"),
                    map.get("DEPARTMENT_NAME")));
        });
        System.out.println("処理終了");
    }

    private List<Map<String, Object>> findEmployeeList(int departmentId) {
        return jdbcTemplate.queryForList(String.join("", new String[] {
            "select emp.EMPLOYEE_NAME, dept.DEPARTMENT_NAME",
            " from EMPLOYEE emp",
            " inner join DEPARTMENT dept on dept.DEPARTMENT_ID = emp.DEPARTMENT_ID",
            " where emp.DEPARTMENT_ID = ?"
        }), departmentId);
    }
}
```

## おまけ DB管理支援ツールとしてのDBFlute
テーブル追加変更がある場合、replace-schema.sqlおよび登録データを修正し、manage.batを実行すれば、既存のテーブル削除＆再生成してくれて、開発環境でマイグレーションが可能です。
また、スキーマ差分比較ツールなど、DBFluteはマイグレーションツールとしても非常に優秀な機能を備えています（参考：「[20160521 大規模映像配信サービスの Java8による全面リニューアルの裏側](https://www.slideshare.net/SuguruAkiho/20160521-jjug-cccunext)」。
O/Rマッパとして導入できなくても、DB管理支援ツールとしての導入をぜひ検討してみてください。

# おわりに
eclipseへのインポート等により、[即時実行可能な形で配布](http://dossaribook.starfree.jp/public/qiita/20190430_sample-dbaccess_light.zip)しています。
ソースコード＋Derbyデータベース合わせて287KBしかありません（圧縮後）。
ぜひ、O/Rマッパのサンプルを即時実行して体験してみてください。