この記事では、Apache Cayenneのオブジェクトリレーショナルマッピング(ORM)機能を使用して、Mavenを使用して構築した小さなJavaプロジェクトからMySQLデータベースを操作します。
本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。
プロジェクトの設定
MavenはJavaアプリケーションを構築するために最もよく使われているツールの一つです。このセクションでは、プロジェクトの依存関係を設定します(これはあなたのシステムが空であることを前提としています)。
最初のステップとして、Apache CayenneとMYSQLコネクタ(具体的にはJDBCドライバ)を追加するために、以下の依存関係を追加してみましょう。
<dependency>
<groupId>org.apache.cayenne</groupId>
<artifactId>cayenne-server</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.14</version>
<scope>runtime</scope>
</dependency>
モデラープラグインは pom.xml
にも定義されている必要があります。これは、現在のプロジェクトのマッピングファイルを開いたときに、コマンドラインからApache Cayenne Modelerプラグインを起動するために使われるmavenコマンドです。
<plugin>
<groupId>org.apache.cayenne.plugins</groupId>
<artifactId>cayenne-modeler-maven-plugin</artifactId>
<version>4.0.1</version>
<configuration>
<modelFile>${project.basedir}/src/main/resources/cayenne-blog.xml</modelFile>
</configuration>
</plugin>
一般的にモデラーは、データベースと実際のJavaモデルクラスとの間のマッピングを設計し、設定するための推奨される方法です。
このページからダウンロードできます。あなたの特定のOS用に作られたバージョンをダウンロードする必要があるでしょうし、代わりに、Mavenプラグインとして含まれているクロスプラットフォームバージョン(JAR)を使用することもできます。この記事を書いている時点での最新の安定版はバージョン4.1です。このバージョンはJava 1.8以降が必要です。
次のステップとして、mvn clean install
コマンドでプロジェクトをビルドし、mvn cayenne-modeler:run
コマンドでモデラーGUIを起動して、この画面を出力してみましょう。
MySQL 以外のものを使用している場合は、JDBC ドライバも変更する必要があるため、設定はデータベースに依存します。ここでは、対応するドライバと一緒に完全なリストを示します。
マッピングとデータベースの設計
例えば、2つのテーブル間の1対多の関係を示すcayenne_blogという既存のデータベースがあり、その中で以下のようなパラメータが定義されているとします。
- author(著者): id (PK) と name
- article(記事):id (PK)、タイトル、内容、author_id (FK)
では、この例のDBを参照したSQLコマンドを考えてみましょう。
CREATE TABLE `author` (
`id` int(11) NOT NULL,
`name` varchar(250) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Indexes for table `article`
--
ALTER TABLE `article`
ADD PRIMARY KEY (`id`),
ADD KEY `author_id` (`author_id`);
--
-- Indexes for table `author`
--
ALTER TABLE `author`
ADD PRIMARY KEY (`id`);
--
-- AUTO_INCREMENT for table `article`
--
ALTER TABLE `article`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `author`
--
ALTER TABLE `author`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
--
-- Constraints for table `article`
--
ALTER TABLE `article`
ADD CONSTRAINT `article_ibfk_1` FOREIGN KEY (`author_id`) REFERENCES `author` (`id`);
db.sql](https://github.com/dassiorleando/apache-cayenne/blob/master/db.sql)
ファイルをphpMyAdmin
にインポートするか、ターミナルのMYSQLサーバーから以下のコマンドを実行します: mysql < db.sql.
では、モデラープラグインの設定であるcayenne-maven-plugin
というプラグインをpom.xml
に追加してみましょう。
<plugin>
<groupId>org.apache.cayenne.plugins</groupId>
<artifactId>cayenne-maven-plugin</artifactId>
<version>4.0.1</version>
<configuration>
<map>${project.basedir}/src/main/resources/blog.map.xml</map>
<dataSource>
<driver>com.mysql.jdbc.Driver</driver>
<url>jdbc:mysql://localhost:3306/cayenne_blog</url>
<username>root</username>
<password>root</password>
</dataSource>
<dbImport>
<defaultPackage>com.dassiorleando.apachecayenne.models</defaultPackage>
</dbImport>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</plugin>
ここではORMがデータを保存する場所(<data source>
)とマッピングファイルを保存する場所(<map>
)を指定します。この設定から、データベース名はcayenne_blog
、ユーザデータベースの認証情報はroot:root
(MYSQLサーバのものに合わせて更新してください)、デフォルトパッケージはプロジェクト構造体のもの(モデルクラスを作成するパッケージ)を使用していることがわかります。
最後に、プロジェクトのコマンドラインからcbimportコマンドを使用します: mvn cayenne:cdbimport
。cdbimport
はXMLマップファイルを既存のデータベースと同期させるので、以下のようなログが得られるはずです。
INFO] +++ Connecting: SUCCESS.
[INFO] Detected and installed adapter: org.apache.cayenne.dba.mysql.MySQLAdapter
[INFO] Table: cayenne_blog.AUTO_PK_SUPPORT
[INFO] Table: cayenne_blog.article
[INFO] Table: cayenne_blog.author
[INFO] Db Relationship : toOne (article.author_id, author.id)
[INFO] Db Relationship : toMany (author.id, article.author_id)
[INFO]
[INFO] Map file does not exist. Loaded db model will be saved into '/Users/dassiorleando/projects/opensource/apache-cayenne/src/main/resources/blog.map.xml'
[INFO]
[INFO] Detected changes:
[INFO] Create Table article
[INFO] Create Table author
[INFO] Create Table AUTO_PK_SUPPORT
[INFO]
[WARNING] Can't find ObjEntity for author
[WARNING] Db Relationship (Db Relationship : toOne (article.author_id, author.id)) will have GUESSED Obj Relationship reflection.
[INFO] Migration Complete Successfully.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.165 s
[INFO] Finished at: 2019-07-22T17:40:36+01:00
[INFO] Final Memory: 10M/164M
[INFO] ------------------------------------------------------------------------
Javaクラスを生成してみましょう: mvn cayenne:cgen
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building apache-cayenne 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- cayenne-maven-plugin:4.0.1:cgen (default-cli) @ apache-cayenne ---
[INFO] Generating superclass file: /Users/dassiorleando/projects/opensource/apache-cayenne/src/main/java/com/dassiorleando/apachecayenne/model/auto/_Article.java
[INFO] Generating class file: /Users/dassiorleando/projects/opensource/apache-cayenne/src/main/java/com/dassiorleando/apachecayenne/model/Article.java
[INFO] Generating superclass file: /Users/dassiorleando/projects/opensource/apache-cayenne/src/main/java/com/dassiorleando/apachecayenne/model/auto/_Author.java
[INFO] Generating class file: /Users/dassiorleando/projects/opensource/apache-cayenne/src/main/java/com/dassiorleando/apachecayenne/model/Author.java
[INFO] Generating superclass file: /Users/dassiorleando/projects/opensource/apache-cayenne/src/main/java/com/dassiorleando/apachecayenne/model/auto/_AutoPkSupport.java
[INFO] Generating class file: /Users/dassiorleando/projects/opensource/apache-cayenne/src/main/java/com/dassiorleando/apachecayenne/model/AutoPkSupport.java
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
これで、プロジェクトの構造が変更されているのがすぐにわかるはずです。ここでは、_Article.java
と_Author.java
の話をしています(どちらもCayenneDataObject
を拡張したファイルです)。同じ設定(XML形式)がresources/blog.map.xml
ファイルに入れられていると見ることができます。
次に、mvn cayenne-modeler:run
というコマンドを入力してモデラーを起動します。New Projectをクリックし、次のページでマッピングファイルのData Domain Nam (blog)を指定します。ここでは、作成したマップファイルと同じフォルダに保存します。
次に、File > Import Datamapをクリックして、データマップへのリンクを許可するUIにアクセスします。
cayenne-blog.xml
とblog.map.xml
がリンクされたら、モデラーからモデルを更新してクラスに反映させることができます。以下のようになります。
Apache Cayenne は 3 種類の主要な鍵戦略をサポートしています。
- Cayenne-Generated: PK生成を管理します。
- データベース生成: PK はデータベースエンジンによって管理されます。
- カスタムシーケンス:ここにカスタムロジックを実装する必要があります。
次のスクリーンショットでは、著者テーブルには、データベースで管理されているオートインクリメントされた整数であるidが設定されています。
注: 次の図に示すように、当社のデータベース構成と正確に一致するDataNodeを作成してください。
Mavenプロジェクトのresources
ディレクトリに、以下の内容のcayenne-blog
という特別なXMLファイルを作成します。
<?xml version="1.0" encoding="utf-8"?>
<domain project-version="9">
<map name="blog"/>
<node name="datanode"
factory="org.apache.cayenne.configuration.server.XMLPoolingDataSourceFactory"
schema-update-strategy="org.apache.cayenne.access.dbsync.SkipSchemaUpdateStrategy">
<map-ref name="blog"/>
<data-source>
<driver value="com.mysql.jdbc.Driver"/>
<url value="jdbc:mysql://localhost:3306/cayenne_blog"/>
<connectionPool min="1" max="1"/>
<login userName="root" password="root"/>
</data-source>
</node>
</domain>
XMLPoolingDataSourceFactory
は、DataNodeDescriptor
に関連付けられたXMLリソースからJDBC接続情報をロードする役割を担う。
マッピング構造
Apache Cayenne はモデルを記述するための独自の構文を持っています。
-
DataNode(<node>)
: データベースのモデル。データベース名、ドライバ、データベースユーザの認証情報など、データベースに接続するために必要なすべての情報が含まれています。 -
DataMap(<data-map>)
:関係を持つ永続的なエンティティのコンテナです。 -
DbAttribute(<db-attribute>)
: データベーステーブルのカラムを表します。 -
DbEntity(<db-entity>)
: 単一のデータベーステーブルまたはビューのモデル。DbAttributesとリレーションシップを持つことができます。 -
ObjEntity(<obj-entity>)
: 単一の永続的なJavaクラスのモデルで、エンティティクラスのプロパティに対応するObjAttributesと、別のエンティティの型を持つプロパティであるObjRelationshipsで構成されています。 -
Embeddable(<embeddable>)
: ObjEntityのプロパティとして機能するJavaクラスのモデルですが、データベースの複数の列に対応しています。 -
procedure(<procedure>)
: データベースにストアドプロシージャを登録します。マッピングする際に使用します。
詳細は以下のガイドを参照してください。
モデルへのCRUDの適用
このセクションでは、モデル(Article
とAuthor
)にいくつかの基本的な操作を適用します。ObjectSelect
クラスはデータベースへの問い合わせに便利ないくつかの静的メソッドを持っていますが、挿入と更新のために、変更をコミットするために使用されるserver's context(ObjectContext)
を使用する必要があります。
ここでは、私たちのプロジェクトに関連するサーバーのコンテキストを取得する方法を説明します。
ServerRuntime cayenneRuntime = ServerRuntime.builder()
.addConfig("cayenne-blog.xml")
.build();
ObjectContext context = cayenneRuntime.newContext();
注: cayenne-blog.xmlファイルはプロジェクトのリソースフォルダにあります。
オブジェクトの作成
以下のクエリでオブジェクトを作成することができます。
/**
* Save an author
* @param name
*/
public void save(String name) {
// Save a single author
Author author = this.context.newObject(Author.class);
author.setName(name);
context.commitChanges();
}
オブジェクトの読み取り
以下のクエリでオブジェクトを読み込むことができます。
/**
* Find an author by its ID
* @param id the author's ID
* @return the matched author or null if not existing
*/
public Author findById(int id) {
Author author = Cayenne.objectForPK(context, Author.class, id);
return author;
}
/**
* Looking for an author by name
* @param name the name to look up with
* @return the first matched author or null if not existing
*/
public Author findByName(String name) {
Author foundAuthor = ObjectSelect.query(Author.class)
.where(Author.NAME.eq(name))
.selectOne(this.context);
return foundAuthor;
}
/**
* Find authors by name starting with(like%)
* @param partName expected name part
* @return list of authors
*/
public List<Author> findByNameLike(String partName) {
// Let's apply a case-insensitive LIKE on the Author's name column
// We get all the authors with their name starting with "partName"
List<Author> authorsLike = ObjectSelect.query(Author.class)
.where(Author.NAME.likeIgnoreCase(partName + "%"))
.select(context);
return authorsLike;
}
/**
* Find authors by name ending with
* @param partName expected name part
* @return list of authors
*/
public List<Author> findByNameEndWith(String partName) {
// All authors with names ending with "partName"
List<Author> authorsEnd = ObjectSelect.query(Author.class)
.where(Author.NAME.endsWith(partName))
.select(context);
return authorsEnd;
}
クラスのすべてのレコードを検索する
これまでに保存されたすべての著者に以下のクエリを使用してクエリを実行することができます。
public List<Author> findAll() {
// Looking for all authors
List<Author> authors = ObjectSelect
.query(Author.class)
.select(this.context);
return authors;
}
オブジェクトの更新
以下のクエリでオブジェクトを更新することができます。
/**
* Update an author
* @param id the author's ID
* @param newName the new name to set
* @return true for a successful operation and false unknown author
*/
public boolean update(int id, String newName) {
if (StringUtils.isEmpty(newName)) return false;
// Get the author to update
Author author = this.findById(id);
if (author == null) return false;
// Set its name
author.setName(newName);
context.commitChanges();
return true;
}
オブジェクトへの関係付け
ここでは、著者が書いた記事と著者をリンクさせる方法を紹介します。
/**
* Attach a fake article to the author
* @param id the author's ID
* @return true for a successful operation and false unknown author
*/
public boolean attachArticle(int id) {
// Get the author to link with
Author author = this.findById(id);
if (author == null) return false;
// Create a fake article and link it to the current author
Article article = context.newObject(Article.class);
article.setTitle("My post title");
article.setContent("The content");
article.setAuthor(author);
context.commitChanges();
// Get author's linked data (articles)
List<Article> articles = author.getArticles();
return true;
}
オブジェクトの削除
以下のクエリでオブジェクトを削除することができます。
/**
* Delete an author
* @param id author's ID
* @return true for a successful operation and false unknown author
*/
public boolean delete(int id) {
// Get the author to delete
Author author = this.findById(id);
if (author != null) {
context.deleteObjects(author);
context.commitChanges();
return true;
} else {
return false;
}
}
クラスのすべてのレコードを削除する
Apache CayenneのAPIではSQLTemplate
を使ってテーブルの全レコードを削除することができますが、ここでは基本的なSQL削除クエリをターゲットクラスと一緒に提供するだけです。
// SQL delete queries for Author and Article classes
SQLTemplate deleteArticles = new SQLTemplate(Article.class, "delete from article");
SQLTemplate deleteAuthors = new SQLTemplate(Author.class, "delete from author");
// Applying the deletion queries
context.performGenericQuery(deleteArticles);
context.performGenericQuery(deleteAuthors);
ExpressionとExpressionFactory
Apache Cayenne を使って高度なクエリを構築するには多くの可能性があります。しかし、ほとんどの場合はExpressionとExpressionFactoryクラスを使っていますが、ここではその例をいくつか紹介します。
-
likeExp
:LIKE
式を構築するために使用します。 -
likeIgnoreCaseExp
:LIKE_IGNORE_CASE
式を構築するために使用します。 -
containsExp
: 文字列内の任意の場所にマッチするパターンを持つ LIKE クエリに使用される式。 -
containsIgnoreCaseExp
:containsExp
に似ていますが、大文字小文字を区別しないアプローチを使用します。 -
startsWithExp
: パターンは文字列の先頭にマッチします。 -
startsWithIgnoreCaseExp
:startsWithExp
に似ていますが、大文字小文字を区別しないアプローチを使用します。 -
endsWithExp
: 文字列の末尾にマッチする式。 -
endsWithIgnoreCaseExp
: 大文字小文字を区別しないアプローチを使用する文字列の最後にマッチする式。 -
expTrue
:True
の式に使用します。 -
expFalse
: ブール値のFalse
の式に使用します。 -
andExp
:and
演算子を使用して2つの式を連結する際に使用します。 -
orExp
:or
演算子を使用して 2 つの式を連結する場合に使用します。
結論
このチュートリアルでは、Apache Cayenne のオブジェクトリレーショナルマッピング (ORM) 機能を MySQL データベース用に設定する方法と、基本的な CRUD 操作を一対多のリレーションシップを持つクエリの例として学習しました。この記事の完全なソースコードはGithubにあります。
アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ