LINE Developersサイトを支えるDAO自動生成ツール

はじめに

こんにちは、LINE Developersサイト、サーバーサイドを担当しているSongran Liuです。この記事はLINE Advent Calendar 2017の3日目の記事です。皆さんご存知だと思いますが、LINE Developersサイトは今年9月にリニューアルされました。

LINE Developersサイト

今回のリニューアルでは、いろいろな課題やチャレンジもありました。今日はリニューアルに関わるチャレンジの1つ、今も活躍しているDAO自動生成ツールの設計について、紹介したいと思います。

背景および課題

今回のリニューアルでは、下記の2つの目的がありました。

  • 従来のシステムの機能を踏襲しながら、さらにユーザーに使いやすい機能を追加します。
  • いくつかのサブシステムを管理しやすくするために、1つのシステムに統合します。

いくつかのサブシステムと新規データベースがあるので、複数のデータベースと100件以上のテーブルを管理することになります。永続化フレームワークにはMyBatisを選定しましたが、すぐに問題が発覚しました。

  • それぞれのテーブルに対して1個ずつ、Javaコードのエンティティ、DAOインターフェイス、マッピング用のタイプハンドラー、テストケース、およびXMLのマッパーファイルを書くのは、コストが大きすぎます。
  • サブシステムのテーブルのスキーマが他のシステムに修正されると、当システムもすぐに影響を受けて、停止してしまうリスクがあります。

上記の2点の理由で、DAO(Data Access Object)周りをシステムで自動生成することに決定しました。

対応

要件

以下のとおり、明確に要件を決めていきました。

  • 複数のデータベースに一度に接続します。
  • エンティティクラス、DAOインターフェイス、DAOインターフェイスのテストケース、mapper.xml、タイプハンドラーは、成果物として自動生成して保存します。
  • 手動で書いた箇所もあるので、その部分のコードが上書きされないように自動生成します。
  • プロジェクトのビルド時にDAOを自動生成します。

これらの要件を詳しく見ていきましょう。

複数のデータベースに一度に接続します。データベースの接続に最低限必要なのは、url、username、password、および使われそうな識別子だと思われます。この4つの情報を入力して、org.springframework.jdbc.datasource.DriverManagerDataSourceを使って、複数のデータベースに接続できます。そして、java.sql.ResultSetを使って、テーブルのメタ情報も取得できます。そのメタ情報を利用して、テーブルのスキーマの情報をJavaに処理しやすいコードに変換すると、自動生成に使えると思います。

自動生成にはいくつかのやり方があると思いますが、今回は成果物のファイルの種類が多いですし、Spring Bootにテンプレートエンジンも付いています。将来的に他のプロジェクトで使われる時に、他の形式やフォーマットにも簡単に書き換えられるので、それぞれ専用のFreeMarkerテンプレートで書き出す方針で実装するといいと思いました。ですので、今回はSpring BootフレームワークとFreeMarkerテンプレートエンジンを採用しました。

ユーザーが書いたコードの上書きを防ぐため、Generation Gapというデザインパターンで実装することにしました。

プロジェクトのビルド時に、自動生成ツールの成果物を置くためのフォルダーをプロジェクト内に用意して、ビルドが実行される時にそのフォルダーに成果物を入れます。その後、成果物で実際のファイルを上書きするようにすると、上記の要件を一応満たすことになります。

設計

要件からフローチャートを書いてみます。複雑な部分は特にないので、フローチャートもとても単純に、下記のようになります。

フローチャートから考えると、システムには主に3つの役割があります。この3つの役割からシステムのアーキテクチャー設計に入るとやりやすいです。

  • 成果物を削除する役割。
  • データベースとテーブルの情報を読み込んでJavaコードに変換する役割。
  • テンプレートによりファイルを生成する役割。

この3つの役割から、システムのアーキテクチャーを設計してみます。

Rulesモジュールは手動で書く自動生成ルールです。データを読み込む時や自動生成するや成果物を削除する時などに、参照して実行する情報です。例えば、どのデータベースのどのテーブルを対象外にするか、スキーマ情報のどの型をJava言語のどの型に変換するかなどを判断する場合に使われる情報です。
Cleaner、Generator、DBReaderのそれぞれが、Rulesの情報によってBeanを設定する必要があるので、Rulesはどこからでも参照できる場所に配置するといいです。

ちなみに、Artifactsは成果物の置き場で、Table Meta Dataは一時的にデータを保存する場所です。

実際にデータを読み込んでファイルを自動生成するモジュールは、下記の3つです。

  • DBReader
    • 複数のデータベースに接続し、テーブル情報を取得します。
    • 読み込んだテーブル情報をRulesにある情報によって、設定します。
    • 設定したデータをTable Meta Dataストレージに保存します。
  • Generator
    • Table Meta Dataストレージからデータを取得して、Javaで処理しやすいJavaクラスに変換します。
    • 自動生成の共通処理、例えばデータの準備、テンプレートファイルのIO処理などをAbstract Generatorにまとめます。
    • エンティティ、MyBatis用のマッパー、DAOインターフェイス、DAOインターフェイスのテストケース、MyBatisTypeHandlerを、それぞれに専用のクラスを用意して、Abstract Generatorを継承して作ります。これらは同じ情報と流れで処理します。将来、他のタイプのファイルを生成する必要があれば、また別のGeneratorクラスを作るといいと思います。
  • Cleaner
    • 自動生成する前に、Rulesに基づいて、Artifacts内の前回の成果物を削除します。

このようなアーキテクチャーに沿って実装すると、完成できると思います。後は、実際の要件によって、Rulesの書き方も変わりますので、Rulesの方を簡単に変えられるように設計するといいでしょう。

成果物

実際に実行してみます。

自動生成用のテンプレート

実際に生成された成果物

これで自動生成できるようになりました。プロダクト起動時に自動生成ツールを実行して、成果物をoutputフォルダーから実際のプロダクトフォルダーに移すシェルスクリプトを書けば、要件を満たせます。

最後に

この仕組みは、DAOの自動生成だけでなく、いろいろな場面でも使えると思います。

以上、LINE Developersサイトで活躍しているDAO自動生成ツールの設計について紹介しました。

明日の記事はJunさんによる「今更話す、Messaging APIのNode.js版SDK開発」です。お楽しみに!

Related Post