プロダクトレビュー
話題のO/Rマッピングツール「Cayenne」を使う


   Cayenneを実際に使ってみる

 作成したアプリケーションを使い、実際にクエリなどを実行しながらCayenneの使い勝手を見ていきましょう。

全件検索クエリの実行

 Cayenneによって生成されたエンティティ・クラスを使用して、クエリ(SQLのSELECT文)を実行するにはDataContextクラスのクエリ発行用メソッドを使用します。用途に応じたいくつかのメソッドが用意されていますが、ここではシンプルなperformQuery()メソッドを使ってみます。

 実際のサンプル・コードを見ていきましょう。最初にDataContextオブジェクトを取得します。ファクトリ・メソッドcreateDataContext()を利用します。cayenne.xmlを読み込んでDataContextオブジェクトの初期化を行います。

DataContext ctx = DataContext.createDataContext();

 次にorg.objectstyle.cayenne.query.SelectQueryオブジェクトを生成します。SelectQueryオブジェクトはその名からも分かるとおりSQLのSELECTクエリを表現するオブジェクトです。最も単純な全件検索を行うためのSelectQueryの生成は、次のようにコンストラクタに対してDataObjectをjava.lang.Class型として指定するか、マッピング・ファイルで定義されている名前を指定します。

SelectQuery query = new SelectQuery(Employee.class); // Class型で指定する場合
SelectQuery query = new SelectQuery("Employee"); // マッピング名で指定する場合

 クエリの実行はDataContextオブジェクトのperformQuery()メソッドを実行します。performQuery()メソッドはクエリの検索結果となるDataObjectが格納されたListオブジェクトを返します。

List list = ctx.performQuery(query);

 List型でDataObjectが取得できれば、Iteratorを使ってループ処理の中でgetXXX()メソッドを用いて各レコードのデータにアクセスすることができます。

Iterator i = list.iterator();
while(i.hasNext()) {
  Employee emp = (Employee)i.next();
  System.out.println(emp.getName()); // Employee表のName列のデータを表示
}

実行結果
John
Elvin
McCoy
Richard
Eric

 CayenneModelerで自動生成されたエンティティ・クラスには主キーに対するsetXXX()、getXXX() は作成されていません。Employeeクラスをエディタで開いて、次のようなgetId()メソッドを追加することで主キーの値を取得することができます。

public class Employee extends _Employee {
  public Integer getId() {
    return (getObjectId() != null && !getObjectId().isTemporary())
    ? (Integer)getObjectId().getIdSnapshot().get(ID_PK_COLUMN)
    : null;
  }
}

 Cayenneには自動的に主キーの値を生成する機能もありますが、今回は直接キー値を入力するので次のようにsetIdメソッドも追加しておきます(ObjectIdのimportを記述し忘れないようにしてください)。

public void setId(int id) {
  setObjectId(new ObjectId(Employee.class, "ID", id));
}

主キーによる検索

 全件検索の次は、主キーによって識別されるユニークなレコードを検索する例を試してみましょう。この場合はDataContextオブジェクトのregisteredObject()メソッドを使用します。先述したようにCayenneでは、主キーはObjectIdクラスのオブジェクトとして表現されます。registeredObject()メソッドはこのObjectIdを引数に取り、そのObjectIdによって識別される一意なエンティティ・オブジェクトを返します。

 ObjectIdのコンストラクタにはエンティティ・クラスと主キーフィールド、主キーの値を渡します。以下のコード例ではEmployeeのIDが1であるObjectIdを用いてregisteredObject()メソッドを呼び出しています。

Employee emp =
(Employee)ctx.registeredObject(new ObjectId(Employee.class, "ID", 1));
System.out.println(emp.getName());

注:主キーが文字列型の場合には、ObjectId生成時にString型でキー値を渡します。

実行結果
John

 該当するレコードが存在し、エンティティ・オブジェクトが取得できれば、後はgetXXX()やsetXXX()メソッドを使ってデータ・アクセスを行うことができます。該当レコードがない場合、getXXX()メソッドの結果にはnullが返ります。

WHERE句による条件検索

 次にWHERE句による条件検索を試してみましょう。条件検索はorg.objectstyle.cayenne.exp.Expressionオブジェクトを引数に取るSelectQuery()メソッドを使用します。ExpressionオブジェクトはSQLのWHERE句の条件を表すために用意されており、fromString()メソッドを用いてSQLのWHERE句を直接記述することができます。
例えば名前の先頭が「E」であるレコードだけを取り出すには次のようにします。

Expression exp = Expression.fromString("name like 'E%'"); // Expressionの作成
SelectQuery query = new SelectQuery(Employee.class, exp);

List list = ctx.performQuery(query);
Iterator i = list.iterator();
while(i.hasNext()) {
  Employee emp = (Employee)i.next();
  System.out.println(emp.getId() + ":" + emp.getName());
}

実行結果
2:Elvin
5:Eric

 CayenneではExpressionオブジェクトを生成するためのヘルパー・クラスであるExpressionFactoryが用意されています。このクラスに用意されているメソッドを用いることで、よりオブジェクト指向的な感覚で条件式を指定することができます。例えば、以下のコードでは「dptCode = 'A01' AND name LIKE 'J%'」というSQLのWHERE句を実現しています。ここで使用しているsetQualifier()メソッドはクエリ・オブジェクトにExpressionをセットするためのメソッドであり、すでにExpressionが設定されたクエリにAND式やOR式を加えるためのandQualifier()やorQualifier()というメソッドも用意されています。

Expression exp1 = ExpressionFactory.matchExp("dptCode", "A01");
Expression exp2 = ExpressionFactory.likeExp("name", "J%");

SelectQuery query = new SelectQuery(Employee.class);
query.setQualifier(exp1);
query.andQualifier(exp2); // AND式を実現

 ExpressionFactoryでほかにどのようなメソッドが用意されているかは、APIドキュメントを参照してください。

リレーション関係にあるデータの取得

 CayenneModelerを使ってリレーションの設定を行ったので、_Employeeクラスには、関連付いているDepartmentオブジェクトを返すgetEmpToDptRel()というメソッドが作成されています。そのため、以下のようにコーディングすることで、Employeeオブジェクトが参照するDepartmentオブジェクトのデータにアクセスすることができます。

// 全件検索のクエリを発行、Iteratorオブジェクトを取得済み
while(i.hasNext()) {
  Employee emp = (Employee)i.next();
  System.out.println(emp.getId() + ":" + emp.getName() + ":" +
  emp.getEmpToDptRel().getDptName());
}

実行結果
1:John:Development
2:Elvin:Development
3:McCoy:Accounting
4:Richard:Research
5:Eric:Sales
6:Alice:Sales

ORDER BY句による並べ替え

 ORDER BY句による並べ替えを実現するにはSelectQueryオブジェクトのaddOrdering()メソッドを使用します。引数に並べ替え対象となるフィールド名と、昇順であればtrue、降順であればfalseを渡します。以下のコード例ではEmployeeテーブルのName列を昇順に並べ替えています。

SelectQuery query = new SelectQuery("Employee");
query.addOrdering("Name", true);

実行結果
Elvin
Eric
John
McCoy
Richard

新規レコードの挿入

 ここまではSQLのSELECT文の機能を実現してきましたが、ここからは更新系のDML文(INSERT、UPDATE、DELETE)をどのように実現するのか紹介していきます。

 新規レコードを挿入(SQLのINSERT文)するには、まずDataContextオブジェクトのcreateAndRegisterNewObject()メソッドを呼び出してメモリ上に新しいエンティティ・オブジェクトを生成します。この新しく生成したエンティティ・オブジェクトに対してsetXXX()メソッドを使用してフィールドの値をセットしていきます。新規レコードに必要な値をセットし終えたら、DataContextオブジェクトのcommitChanges()メソッドを呼び出すことによってエンティティ・オブジェクトの内容がDBに反映されます。

 以下のコード例ではID列が「6」、Name列が「Alice」、DptCode列が「B01」の新しいレコードをテーブルに挿入しています。

Employee newEmp = (Employee)ctx.createAndRegisterNewObject("Employee");

newEmp.setId(6);
newEmp.setName("Alice");
newEmp.setDptCode("B01");
ctx.commitChanges();

 更新系の操作を、もしロールバックする場合にはDataContextオブジェクトのrollbackChanges()メソッドを呼び出します。

データの更新

 既存のレコードのデータを更新する(SQLのUPDATE)場合には、取り出したエンティティ・オブジェクトのsetXXX()メソッドを使用して値を変更すればOKです。以下の例では、IDが1番のJohnのレコードを取り出し、名前を「Johnny」に、所属部署を「B01」に変更しています。変更後はcommitChanges()メソッドを呼び出して更新をDBに反映しておきます。

Employee emp =
(Employee)ctx.registeredObject(new ObjectId(Employee.class, "ID", 1));
emp.setName("Johnny");
Department dept =
(Department)ctx.registeredObject(new ObjectId(Department.class, "Code", "B01"));
emp. setEmpToDptRel(dept);
ctx.commitChanges();

注:既存レコードの外部キーフィールドを更新する際には参照オブジェクト(上記例ではDepartmentオブジェクト)をリレーション・メソッドの引数に渡す必要があります。

レコードの削除

 最後にレコードの削除(SQLのDELETE文)です。レコードを削除するには、DataContextクラスに用意されているdeleteObject()メソッドを使用します。引数には削除の対象となるレコードに対応したエンティティ・オブジェクトを渡してやります。deleteObject()メソッドを実行した後にcommitChanges()メソッドを呼び出すことを忘れないようにしてください。

ctx.deleteObject(emp);
ctx.commitChanges();

発行されるSQL文の確認

 ここまでの簡単なサンプル・プログラムで理解できたと思いますが、O/Rマッピング・フレームワークを利用するアプリケーション開発者はSQL文を意識することなく、よりオブジェクト指向的なプログラミングによってRDBを扱うことができます。しかしO/Rマッピング・フレームワークの内部処理ではRDBに対してJDBCを通じてSQL文を発行しています。場合によっては実際に発行されているSQL文を確認したいこともあるでしょう。Cayenneで実際に発行されるクエリのSQL文を確認するにはQueryインターフェイスで定義されているsetLoggingLevel()メソッドを使用します。このメソッドの引数にはLog4jのLevelオブジェクト(WARN)を渡します。

query.setLoggingLevel(org.apache.log4j.Level.WARN);

 更新系のSQL文(INSERT、UPDATE、DELETE)の確認はcommitChanges()メソッドを呼び出す際に、上記同様Level.WARNを引数として渡してやってください。

ctx.commitChanges(org.apache.log4j.Level.WARN);

 こうすることで、標準出力のコンソールにCayenneがデータベースに対して発行したSQL文が表示されるようになります。


 最近話題のO/Rマッピング・フレームワークCayenneの仕組みと簡単な使い方をざっと紹介してきましたが、いかがでしたか? この記事では詳細な説明をすることはできませんでしたが、Cayenneを使ってみる取っ掛かりにはなったと思います。ぜひこれを機会にアプリケーションでのCayenneの活用を試してみてください。また、ほかのO/Rマッピング・フレームワークも試しながら、相違点や使いやすさなどを比べてみるのも面白いでしょう。ソースコードも公開されているので、オリジナルのO/Rマッピング・フレームワークを作成する際のヒントにすることもできるでしょう。 

3/3  

 INDEX

話題のO/Rマッピングツール「Cayenne」を使う
  Page1
Cayenneの構造と仕組み 
  Page2
サンプルアプリケーションを作成する
  Page3
Cayenneを実際に使ってみる




Java Agile フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Java Agile 記事ランキング

本日 月間