14.4. DML スタイルの操作
すでに議論したように、自動的かつ透過的なオブジェクト/リレーショナルマッピングは、オブジェクト状態の管理であると考えられます。このオブジェクトの状態は、メモリ内で利用できます。そのため (SQLの
データ操作言語
(DML) 文: INSERT
、 UPDATE
、 DELETE
を使って)データベース内のデータを直接操作しても、インメモリの状態には影響を与えません。しかし Hibernate は、バルク SQL スタイルの DML 文実行に対応するメソッドを用意しています。これは Hibernate クエリ言語 (15章HQL: Hibernate クエリ言語) を通して実行されます。
UPDATE
と DELETE
文の疑似構文は: ( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?
です。注意すべき点がいくつかあります:
注意点:
- from 節において、 FROM キーワードはオプションです。
- from 節ではエンティティ名1つだけが可能ですが、別名を付けることができます。エンティティ名に別名が与えられると、どのようなプロパティ参照も、その別名を使って修飾しなければなりません。エンティティ名に別名が与えられなければ、どのようなプロパティ参照も修飾してはなりません。
- 暗黙的であれ明示的であれ 「結合構文の形式」をバルク HQL クエリ内で指定することはできません。サブクエリは where 節で使うことができます。サブクエリそのものは、結合を含められます。
- where 節はオプションです。
例として、HQL の
UPDATE
を実行するには、Query.executeUpdate()
メソッドを使ってください。このメソッドはおなじみの JDBC PreparedStatement.executeUpdate()
から名付けられました:
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName"; // or String hqlUpdate = "update Customer set name = :newName where name = :oldName"; int updatedEntities = session.createQuery( hqlUpdate ) .setString( "newName", newName ) .setString( "oldName", oldName ) .executeUpdate(); tx.commit(); session.close();
EJB3 仕様を受け継いでおり、HQL の
UPDATE
文は、デフォルトでは、作用するエンティティの 「Version(オプション)」 バージョンや 「Timestamp(オプション)」 タイムスタンプのプロパティの値には影響しません。しかし versioned update
を使って、 version
や timestamp
プロパティの値を強制的にリセットさせることができます。これは UPDATE
キーワードの後に VERSIONED
キーワードを追加することで行えます。
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName"; int updatedEntities = session.createQuery( hqlVersionedUpdate ) .setString( "newName", newName ) .setString( "oldName", oldName ) .executeUpdate(); tx.commit(); session.close();
カスタムバージョン型(
org.hibernate.usertype.UserVersionType
)は update versioned
文と一緒に使えません。
HQL の
DELETE
を実行するには、同じ Query.executeUpdate()
メソッドを使ってください:
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); String hqlDelete = "delete Customer c where c.name = :oldName"; // or String hqlDelete = "delete Customer where name = :oldName"; int deletedEntities = session.createQuery( hqlDelete ) .setString( "oldName", oldName ) .executeUpdate(); tx.commit(); session.close();
Query.executeUpdate()
メソッドが返す int
の値は、この操作が影響を及ぼしたエンティティの数です。これが影響するデータベース内の行数と、相互に関係する場合と、しない場合があります。HQL バルク操作は、結果として、実際の SQL 文が複数実行されることがあります(例:joined-subclass)。返される数は、その文によって影響を受けた実際のエンティティの数を示します。joined-subclass の例に戻ると、サブクラスの一つに対する削除は、そのサブクラスがマッピングされたテーブルだけではなく、「ルート」テーブルと継承階層をさらに下った joined-subclass のテーブルの削除になります。
INSERT
文の疑似構文は: INSERT INTO エンティティ名プロパティリスト select 文
です。注意すべき点がいくつかあります:
- INSERT INTO ... SELECT ... の形式だけがサポートされています。 INSERT INTO ... VALUES ... の形式はサポートされていません。properties_list は、SQL の
INSERT
文におけるカラムの仕様
に類似しています。継承のマッピングに含まれるエンティティに対して、クラスレベルで直接定義されたプロパティだけが、プロパティリストに使えます。スーパークラスのプロパティは認められず、サブクラスのプロパティは効果がありません。言い換えるとINSERT
文は、本質的にポリモーフィックではありません。 - select_statementは有効なHQL select クエリになりえますが、注意としては、返り値の型が insert 文の期待する型とマッチしていなければなりません。現在このチェックをデータベースへ任せるのではなく、クエリのコンパイル時にチェックします。このことは、 equal とは違い、Hibernate の
Type
間の equivalent に関する問題を引き起こすことに注意してください。これはorg.hibernate.type.DateType
として定義されたプロパティと、org.hibernate.type.TimestampType
として定義されたプロパティの間のミスマッチの問題を引き起こします。データベースがそれらを区別できなくても、変換することができても、この問題は発生します。 - id プロパティに対して、insert 文には二つの選択肢があります。properties_listで明示的に id プロパティを指定するか (この場合、対応する select 式から値が取られます)、プロパティリストからそれを除外するかのいずれかです (この場合、生成される値が使われます)。後者の選択肢は、データベース内を操作する id ジェネレータを使うときのみ、利用可能です。この選択肢を採る場合、「インメモリ」型のジェネレータを使うと、構文解析時に例外が発生します。この議論では、インデータベース型ジェネレータは
org.hibernate.id.SequenceGenerator
(とそのサブクラス) と、org.hibernate.id.PostInsertIdentifierGenerator
の実装であると考えています。ここで最も注意すべき例外は、org.hibernate.id.TableHiLoGenerator
です。値を取得する選択可能な方法がないため、このジェネレータを使うことはできません。 version
やtimestamp
としてマッピングされるプロパティに対して、insert 文には二つの選択肢があります。properties_listで明示的にプロパティを指定するか(この場合、対応する select 式から値が取られます)、プロパティリストから除外するか(この場合、org.hibernate.type.VersionType
で定義されたシード値
が使われます)のいずれかです。
以下は、HQL の
INSERT
文の実行例です:
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ..."; int createdEntities = session.createQuery( hqlInsert ) .executeUpdate(); tx.commit(); session.close();