DBFluteをできるだけ簡単に説明してみる

DBFluteとは何か」と思って調べてみたら「DBFluteの紹介の時点で長すぎっ!!」って思った人向け。 いつも説明に困るのでまとめておく。

DBFluteとは、

  • データベースアクセスを行うO/Rマッパー。(DBFlute Runtime)
  • データベース周りのかゆいところに手が届くツール類。(DBFlute Client)

です。

DBFluteの特徴

O/Rマッパーとして

  1. データベースのメタ情報を元にコードを自動生成し、SQL発行をタイプセーフに行うことができる。
  2. JDBC APIにしか依存しない。JPAは使わない。独自のトランザクション管理もない。
  3. データベースのテーブル構造のまま、Javaから操作できるようにするだけ。
    • O/Rマッパーとは言えないのかも。その分シンプルかつ強力。
    • マッピング設定などは不要で、テーブルがそのままクラスになり、カラムがフィールドとなる。
    • 外部キーから関連の多重度を推測し、適切なフィールドを追加してくれる(1:Nならば1側にListを、N側にTを)。
  4. データベースのテーブル構造に対応するSQL生成コードが生成される。逆に言うと、構造上正しくないSQLが発行できない。
    • 例)Table AにTable Bへの外部キーがあれば、Table AにTable BをJOINするためのメソッド、Table Bに関連するTable Aをフェッチするためのメソッドが生成される、など

データベース周りのツールとして

データベースに関する様々なツールタスクと呼ばれる)がある。

  1. O/Rマッピング用コード生成ツールjdbc, generate, sql2entity, doc, manage regenerate
    • 既存のデータベースにJDBCで接続し、メタ情報を元にコードを生成する。
    • 異常なまでに細かい沢山のカスタマイズ項目があるが、慣れるまでは無視して問題ない。
  2. データベーススキーマを作成する(ReplaceSchema
    • DDLを実行し、Excel/CSV/TSVからデータをINSERTする。
    • DDL実行前に、既存のスキーマに存在するテーブルは自動で全てDROPしてくれる。
    • root権限で動かすDDLを別に指定できるので、MySQL等がインストールしてあってrootでアクセスできれば何でもできる。
  3. その他多数

DBFluteの主要クラス

DBFluteを使ってDBアクセスするコードを書くにあたり、以下の3つを押さえておくこと。

  • Entity
    • レコードの値を保持するクラス。テーブルと1:1で対応する。
    • SELECT結果の戻り値として、またINSERT/UPDATEのパラメータを詰めるために使用する。
  • ConditionBean (CB)
    • クエリの組み立てに使用するクラス。これもテーブルと1:1で対応する。
  • Behavior (Bhv)

利用例

DBFluteを使ってのCRUDは以下のようになる。DBを操作するためのメソッドが生成されており、文字列でSQLを組み立てる箇所が一切ないことに注目。

public class ProductRepositoryTest {
  @Inject
  private ProductBhv productBhv;

  @Test
  public void selectRow() {
    ProductCB cb = productBhv.newMyConditionBean();
    cb.query().setProductCode_Equal("XXXXX");
    Product product = productBhv.selectEntity(cb);
    assertThat(product.getProductCode(), is("XXXXX"));
  }

  @Test
  public void selectRows() {
    ProductCB cb = productBhv.newMyConditionBean();
    cb.query().setProductName_LikePrefix("iPad");
    List<Product> products = productBhv.selectList(cb);
    assertThat(products.stream().map(Product::getProductName).collect(toList()), hasItems("iPad", "iPad Mini", "iPad Air"));
  }

  @Test
  public void selectJoin() {
    ProductCB cb = val.newMyConditionBean();
    cb.query().setProductCode_Equal("XXXXX");
    cb.setupSelect_Manufacture();
    Product product = productBhv.selectEntity(cb);
    assertThat(product.getManufacture().getName(), is("Foxconn"));
  }

  @Test
  public void insert() {
    Product product = new Product();
    product.setProductCode("ZZZZZ");
    product.setProductName("iPad Nano");
    productBhv.insert(product);

    ProductCB cb = new ProductCB();
    cb.query().setProductName_LikePrefix("iPad");
    assertThat(productBhv.selectCount(cb), is(4));
  }

  @Test
  public void update() {
    Product product = new Product();
    product.setProductCode("ZZZZZ");
    product.setProductName("iPad Micro");
    productBhv.update(product);

    assertThat(productBhv.selectByPKValue("ZZZZZ").getProductName(), is("iPad Micro"));
  }

  @Test
  public void delete() {
    Product product = new Product();
    product.setProductCode("ZZZZZ");
    product.setProductName("iPad Micro");
    productBhv.delete(product);

    assertThat(productBhv.selectByPKValue("ZZZZZ"), is(nullValue()));
  }
}

ここで使った以外にも、ConditionBeanでは様々なSQLが発行できる。詳しく知るにはConditionBeanの機能を参照のこと。

困ったときは

結局かなり長くなってしまった。。。