iciqlで実現するSQL DSLとJDBCリファクタリング

#iciqlとは？
　
　iciqlはJavaのO/R Mappingで，特徴は軽量なSQL DSL(domain-specific language: ドメイン固有言語)です。とても可読性が高い文法が魅力的なので，紹介します。
　
　
##iciql SQL DSLの例

　iciql SQL DSLの例を見てみましょう。

```java:IcliqlSampleMain.java
Senryu t = new Senryu();
List<Senryu> dbData =
        db
                .from(t)
                .where(t.category).oneOf("09", "15")
                .and(t.sql).like("%SELECT%")
                .and(t.count).lessThan(1)
                .orderByDesc(t.updatedate)
                .orderBy(t.idSenryu)
                .select();
```
　
　次のSQLが発行されます。

```sql:IcliqlSampleMain.sql
SELECT *
FROM   senryu
WHERE  category IN( '09', '15' )
   AND sql LIKE '%SELECT%'
   AND count < 1
ORDER  BY updatedate DESC,
          id_senryu 
```
　
　
##Fluent Interface

　iciqlの文法が懐かしい開発者も多いと思います。これは，2005年にEric EvansとMartin Fowlerが提唱した「流れるようなインタフェース」(fluent interface)が源流で，2007年にSeasarのS2JDBに採用されたり，Ruby on RailsのActive Record，最近ではScalaのSlickなど，Web開発者には馴染みがある文法のためです。
　
　
##SQL DSLのメリット
　SQL DSLのメリットは，**タイプセーフ(type-safe)**の一言に尽きます。
　
　テーブルが変更された場合に，JavaとSQLとの境界が文字列のORMだと，**実行時エラーまで気づかない**ことがあります。SQL DSLでタイプセーフに記述すると，**コンパイルエラーで気づく**ことができます。


　
　
##iciqlのメリット　
　iciqlのメリットは，**jarファイルひとつで動作する**ことです。

 >0. jarファイルひとつで軽量動作（依存なし）
 >0. 設定ファイル不要
 >0. OSS（活発ではない）
 >0. タイプセーフ＆流れるようなインタフェース
 >0. DBからモデルを生成するツールもある（公式ページの解説は古い）
　

　流れるようなインタフェースでタイプセーフのJava ORMは，S2JDBCだとSeasar依存，jOOQは有料など，それぞれ偏りがあります。iciqlはバランスが良いです。
　
　
#SQL DSL x Java 8 ラムダ式
　テーブルモデルのSQL DSLとラムダ式を組み合わせは，コードの相性が良く，可読性が向上します。

　Java JDBC SQL周りのリファクタリングに興味を持たれた方は，GitHubに掲載した[iciql x Java 8 ラムダ式のソースコード](https://github.com/k--kato/iciql/blob/master/IciqlSample/src/com/iciql/sample/IcliqlSampleMain.java)も是非ご参照ください。
 
```java:IcliqlSampleMain.java
public static void main(String[] args) {

    Db db = null;

    try {
        /*==================================================
         * Instantiating a Db
         *==================================================*/
        String url = "jdbc:postgresql://localhost:5432/sqlsenryu";
        String user = "sqlsenryu";
        String password = "sqlsenryu";
        db = Db.open(url, user, password);

        /*==================================================
         * SQL DSL > Select Statements
         *==================================================*/
        Senryu t = new Senryu();
        List<Senryu> dbData =
                db
                        .from(t)
                        .where(t.category)
                        .oneOf(
                                CommonConst.Category.SQLite,
                                CommonConst.Category.PostgreSQL)
                        .and(t.sql).like("%SELECT%")
                        .and(t.count).lessThan(1)
                        .orderByDesc(t.updatedate)
                        .orderBy(t.idSenryu)
                        .select();

        /*==================================================
         * Lambda Expression
         *==================================================*/
        dbData
                .stream()
                .filter(
                        x -> x.category
                                .equals(CommonConst.Category.PostgreSQL))
                .forEach(y -> {
                    print(y);
                    update(y);
                });

        dbData
                .stream()
                .filter(x -> x.category.equals(CommonConst.Category.SQLite))
                .forEach(y -> {
                    print(y);
                });

        /*==================================================
         * SQL DSL > Update Statements
         *==================================================*/
        db.updateAll(dbData);

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (db != null) db.close();
    }
}

private static void update(Senryu senryu) {
    senryu.updatedate = new Timestamp(System.currentTimeMillis());
    senryu.count++;
}

private static void print(Senryu senryu) {
    System.out.println(senryu.toString());
}
```


#参考資料

 * iciql, [http://iciql.com/](http://iciql.com/)
 * GitHub, iciql, [https://github.com/gitblit/iciql](https://github.com/gitblit/iciql)
 * Seasar2, 「タイプセーフAPI」, [http://s2container.seasar.org/2.4/ja/s2jdbc_typesafe.html](http://s2container.seasar.org/2.4/ja/s2jdbc_typesafe.html)
 * jOOQ, [http://www.jooq.org/](http://www.jooq.org/)
 * Slick, [http://slick.typesafe.com/](http://slick.typesafe.com/)
 * Martin Fowler's Bliki,「流れるようなインタフェース」, [http://capsctrl.que.jp/kdmsnr/wiki/bliki/?FluentInterface](http://capsctrl.que.jp/kdmsnr/wiki/bliki/?FluentInterface)

#付録

 * iciqlの開発者は，ひとつのwarファイルデプロイでGitサーバを手軽に構築できる"gitblit"を開発したJames Moger氏です。2011年に公開され，最新リリースは2014-11-10のVer.1.5.0です。
　
 * iciqlの読みは「イシキュエル」説。 イシキュエル -> シークェル -> SQLを有力視しています。
　
 * iciqlのテーブルモデルはアノテーションを使用してテーブルとカラムの属性を定義します。設定ファイルレスが良いです。

```sql:Senryu.java
@IQTable(
        create = false,
        name = "senryu",
        memoryTable = false,
        annotationsOnly = true,
        inheritColumns = false)
public class Senryu {

    @IQColumn(
            primaryKey = true,
            name = "id_senryu",
            trim = false,
            scale = 0,
            length = 0,
            nullable = false,
            defaultValue = "",
            autoIncrement = false)
    public long idSenryu;

    @IQColumn(
            primaryKey = false,
            name = "category",
            trim = false,
            scale = 0,
            length = 2,
            nullable = false,
            defaultValue = "",
            autoIncrement = false)
    public String category;

    public String toString() {
        return String.format(
                "%d %s %s",
                idSenryu,
                category);
    }
}
```
　‡SQL DSL以前の世界は・・・

```sql:PreparedStatementSampleMain.java
StringBuffer  sql = new StringBuffer();
sql.append("SELECT * ");
sql.append("FROM   senryu ");
sql.append("WHERE  category IN( ?, ? ) ");
sql.append("   AND sql LIKE ? ");
sql.append("   AND count < ? ");
sql.append("ORDER  BY updatedate DESC, ");
sql.append("          id_senryu");

PreparedStatement pstmt = conn.prepareStatement(sql);

pstmt.setString(1, "09");
pstmt.setString(2, "15");
pstmt.setString(3, "%SELECT%");
pstmt.setInt(4, 1);
```