Gradle を使った Querydsl SQL のコード生成
前々回 と 前回 に続き、今回は Querydsl SQL のコード生成を Gradle で実施してみました。
ソースは http://github.com/fits/try_samples/tree/master/blog/20150413/
なお、Querydsl 4.0 からパッケージ名等が変更になるようなのでご注意ください。(github の master ブランチでは com.querydsl
となっています)
Gradle を使ったコード生成
Querydsl SQL の場合は Querydsl JPA・MongoDB と違って DB からメタデータを取得してコードを生成します。
querydsl-sql-codegen
モジュールを使用し、アノテーションプロセッサではなく Ant 用のタスクや API を使ってコードを生成します。
(a) Ant 用のタスクを使用
まずは Ant 用のタスククラス com.mysema.query.sql.ant.AntMetaDataExporter
を Gradle から使ってコード生成してみます。
基本的に、DB への接続が発生するため JDBC ドライバーが必要となります。 また、コード生成のログを出力するには slf4j を使います。(slf4j を使わなくても支障はありません)
今回は MySQL を使いました。
build1.gradle
apply plugin: 'java' // DB 接続 URL def qdslDbUrl = 'jdbc:mysql://localhost:3306/sample1?user=root' // コード生成先ディレクトリ def qdslDestDir = 'src/main/qdsl-generated' // 生成するコードの所属パッケージ def qdslDestPackage = 'sample.model' repositories { jcenter() } configurations { querydsl } dependencies { // コード生成用の依存モジュール定義 querydsl 'com.mysema.querydsl:querydsl-sql-codegen:3.6.3' querydsl 'mysql:mysql-connector-java:5.1.35' querydsl 'org.slf4j:slf4j-simple:1.7.12' compile 'com.mysema.querydsl:querydsl-sql:3.6.3' } task generate << { // Ant 用のタスク定義 ant.taskdef( name: 'export', classname: 'com.mysema.query.sql.ant.AntMetaDataExporter', classpath: configurations.querydsl.asPath ) // コード生成の実行 ant.export( jdbcDriverClass: 'com.mysql.jdbc.Driver', dbUrl: qdslDbUrl, targetSourceFolder: qdslDestDir, targetPackage: qdslDestPackage ) } compileJava { dependsOn generate sourceSets.main.java.srcDir qdslDestDir } clean { delete qdslDestDir }
ビルド結果は以下の通りです。
事前に MySQL を起動し、テーブル等を作成しておきます。
ビルド例
> gradle -b build1.gradle build :generate [ant:export] [main] INFO com.mysema.query.sql.codegen.MetaDataExporter - Exported product successfully [ant:export] [main] INFO com.mysema.query.sql.codegen.MetaDataExporter - Exported variation successfully :compileJava :processResources UP-TO-DATE :classes :jar :assemble :compileTestJava UP-TO-DATE :processTestResources UP-TO-DATE :testClasses UP-TO-DATE :test UP-TO-DATE :check UP-TO-DATE :build BUILD SUCCESSFUL
generate タスクの実行時に以下のようなソースコードが生成されました。
- src/main/qdsl-generated/sample/model/QProduct.java
- src/main/qdsl-generated/sample/model/QVariation.java
ちなみに、今回は以下のようなテーブルを用意しています。
DDL 例
CREATE TABLE `product` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `variation` ( `product_id` int(11) NOT NULL, `size` varchar(10) NOT NULL, `color` varchar(10) NOT NULL, PRIMARY KEY (`product_id`,`size`,`color`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
(b) API を使用
次は、コード生成の API (com.mysema.query.sql.codegen.MetaDataExporter
クラス)を Gradle から直接使います。
MetaDataExporter
クラスへ出力パッケージや出力ディレクトリなどを設定した後、export
メソッドへ DatabaseMetaData
を渡せばコードを生成します。
なお、今回は使っていませんが、com.mysema.query.sql.Configuration
で特定のカラムの型 (コード生成の Java の型) を変更するような事も可能です。
build2.gradle
apply plugin: 'java' def qdslDbUrl = 'jdbc:mysql://localhost:3306/sample1?user=root' def qdslDestDir = 'src/main/qdsl-generated' def qdslDestPackage = 'sample.model' buildscript { repositories { jcenter() } dependencies { // コード生成用の依存モジュール定義 classpath 'com.mysema.querydsl:querydsl-sql-codegen:3.6.3' classpath 'mysql:mysql-connector-java:5.1.35' } } repositories { jcenter() } dependencies { compile 'com.mysema.querydsl:querydsl-sql:3.6.3' } task generate << { // DB 接続 def con = new com.mysql.jdbc.Driver().connect(qdslDbUrl, null) def exporter = new com.mysema.query.sql.codegen.MetaDataExporter() exporter.targetFolder = new File(qdslDestDir) exporter.packageName = qdslDestPackage // コード生成の実行 exporter.export(con.metaData) con.close() } compileJava { dependsOn generate sourceSets.main.java.srcDir qdslDestDir } clean { delete qdslDestDir }
ビルド結果は以下の通りです。
ビルド例
> gradle -b build2.gradle build :generate :compileJava :processResources UP-TO-DATE :classes :jar :assemble :compileTestJava UP-TO-DATE :processTestResources UP-TO-DATE :testClasses UP-TO-DATE :test UP-TO-DATE :check UP-TO-DATE :build BUILD SUCCESSFUL
成果物は (a) のケースと同じです。
サンプルアプリケーション
最後に、簡単なサンプルアプリケーションを実装し実行してみます。
ビルド定義
(b) のビルド定義をベースに以下のようなビルド定義を用意しました。
build.gradle
apply plugin: 'application' def enc = 'UTF-8' tasks.withType(AbstractCompile)*.options*.encoding = enc def qdslDbUrl = 'jdbc:mysql://localhost:3306/sample1?user=root' def qdslDestDir = 'src/main/qdsl-generated' def qdslDestPackage = 'sample.model' // 実行クラス mainClassName = 'sample.App' buildscript { repositories { jcenter() } dependencies { classpath 'com.mysema.querydsl:querydsl-sql-codegen:3.6.3' classpath 'mysql:mysql-connector-java:5.1.35' } } repositories { jcenter() } dependencies { compile 'com.mysema.querydsl:querydsl-sql:3.6.3' // 実行用の依存モジュール定義 runtime 'mysql:mysql-connector-java:5.1.35' runtime 'org.slf4j:slf4j-nop:1.7.12' } task generate << { def con = new com.mysql.jdbc.Driver().connect(qdslDbUrl, null) def exporter = new com.mysema.query.sql.codegen.MetaDataExporter() exporter.targetFolder = new File(qdslDestDir) exporter.packageName = qdslDestPackage exporter.export(con.metaData) con.close() } compileJava { dependsOn generate sourceSets.main.java.srcDir qdslDestDir } clean { delete qdslDestDir }
アプリケーションクラス
product・variation テーブルへそれぞれデータを insert して select するだけの単純な処理を実装しました。
insert は set
メソッドを使う方法と columns
・values
メソッドを使う方法を用いてみました。 (他にも Bean を populate
する方法があります)
src/main/sample/App.java
package sample; import com.mysema.query.sql.Configuration; import com.mysema.query.sql.MySQLTemplates; import com.mysema.query.sql.SQLQuery; import com.mysema.query.sql.dml.SQLInsertClause; import sample.model.QProduct; import sample.model.QVariation; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class App { public static void main(String... args) throws SQLException { String dbUrl = "jdbc:mysql://localhost:3306/sample1?user=root"; Configuration conf = new Configuration(new MySQLTemplates()); QProduct p = QProduct.product; QVariation v = QVariation.variation; try (Connection con = DriverManager.getConnection(dbUrl)) { con.setAutoCommit(false); // product テーブルへレコードを insert Integer id = new SQLInsertClause(con, conf, p) .set(p.name, "test" + System.currentTimeMillis()) .executeWithKey(p.id); // variation テーブルへレコードを insert new SQLInsertClause(con, conf, v) .columns(v.productId, v.size, v.color) .values(id, "L", "white") .execute(); // variation テーブルへレコードを insert new SQLInsertClause(con, conf, v) .columns(v.productId, v.size, v.color) .values(id, "S", "blue") .execute(); con.commit(); // product と variation を join して select new SQLQuery(con, conf) .from(p) .join(v).on(v.productId.eq(p.id)) .where(p.name.startsWith("test")) .list(p.id, p.name, v.color, v.size) .forEach(System.out::println); } } }
実行
実行結果は以下の通りです。
実行結果
> gradle run ・・・ :run [1, test1428773665876, white, L] [1, test1428773665876, blue, S] BUILD SUCCESSFUL