Hibernate 入門記 SQL クエリー

今日は「Chapter 13. Native SQL Queries」です.
HQL ではなく,SQL による問い合せ.これも以前に学習したことがありますね.おぉ,これまた 08/11 の「セッションその5 問い合せその他」でしたか.ふーん,ちゃんとお試しまでやってるじゃないですか.意外とやるな>昔の俺
そんなわけで (どんなわけで?),この章は終〜了っ!!
なーんちって.前に学習したときに見た「9.3.6. Queries in native SQL」は文章が 2 行しかなかったのですが,今度のははるかに説明が多いので,一粒で二度おいしくいってみましょう.


そんなわけで (どんなわけで?),まずは「13.1. Creating a SQL based Query」.
SQL を使うには,すでに学習済みの

  • Query

を使います.これ自体は,HQL を使う場合と同じものですね.
こいつを入手するには,Session

    • Query createSQLQuery(String sql, String returnAlias, Class returnClass)
    • Query createSQLQuery(String sql, StringZ] returnAliases, ClassZ] returnClasses)

を使います.
2番目の引数は,永続オブジェクトにマッピングする項目のエイリアス,3番目の引数は永続クラスです.
早速お試し.前回までと同じ問い合せクラスの匿名内部クラスを次のように修正.

                            return session.createSQLQuery(
                                "select {magazine.*} from MAGAZINE",
                                "magazine", Magazine.class).list();

その結果.

CanCam(Shogakukan):[Yuri Ebihara(24), Asami Usuda(19), Yu Yamada(20)]
Oggi(Shogakukan):[Satoko Koizumi(23)]
JJ(Kobunsha):[]
ViVi(Kodansha):[Jun Hasegawa(18)]
Ray(Shufunotomo):[Karina null(20)]

その時のSQL (最初の SQL のみ)

Hibernate: select 
               magazine.id as id0_, magazine.name as name0_, magazine.publisher as publisher0_ 
           from 
               MAGAZINE

この SQL 中で使った {magazine.*} という表記は,magazine というエイリアスで参照している永続クラス,つまり Magazine の「全てのプロパティ」を意味するそうです.Hibernate はこれを見つけると,マッピングファイルに従って全てのプロパティを取得できるように置き換えてくれるのですね.
実行された SQL を見ると,SELECT 句の各項目は magazine. で修飾されています.元の SQL ではエイリアスは指定しなかったのですが,たまたまエイリアスとテーブルが大文字・小文字の違いを除いて同じだったためにうまく動いたようです.
エイリアスを例えば foo とかにすると,見事に例外が吹っ飛んできます.SQL として見たときに必要なさそうでも,常にエイリアスを付けるようにした方がよさげですね.


続いて「13.2. Alias and property references」.
先の SQL 中で使った {magazine.*} を使わない場合は,自分で明示的に SELECT 句を書いてあげなければならないようです.
さっそくやってみましょう.

                            return session
                                    .createSQLQuery(
                                        "select magazine.ID {magazine.id}, "
                                            + "magazine.NAME {magazine.name}, "
                                            + "magazine.PUBLISHER {magazine.publisher} "
                                            + "from MAGAZINE magazine",
                                        "magazine", Magazine.class).list();

その結果.

CanCam(Shogakukan):[Yuri Ebihara(24), Asami Usuda(19), Yu Yamada(20)]
Oggi(Shogakukan):[Satoko Koizumi(23)]
JJ(Kobunsha):[]
ViVi(Kodansha):[Jun Hasegawa(18)]
Ray(Shufunotomo):[Karina null(20)]

ふむ.ちょっと大変ですね.
ちなみに,SELECT 句には永続クラスの全てのプロパティを並べないといけないようです.
試しに出版社を外してみましょう.

                            return session
                                    .createSQLQuery(
                                        "select magazine.ID {magazine.id}, "
                                            + "magazine.NAME {magazine.name} "
                                            + "from MAGAZINE magazine",
                                        "magazine", Magazine.class).list();

その結果.

java.sql.SQLException: Column not found: publisher0_

お見事... HibernateResultSet から getString("publisher0_") しようとして,でもそんなの SELECT 句にないので HSQLDB が例外を投げた,と.


ところで,ここまでのお試しは雑誌でやってきたのですが,モデルでもやってみたいですよね.

                            return session
                                    .createSQLQuery(
                                        "select {model.*} from MODEL model",
                                        "model", Model.class).list();

その結果.

Sayo Aizawa(26)
Yuri Ebihara(24)
Asami Usuda(19)
Yu Yamada(20)
Satoko Koizumi(23)
Jun Hasegawa(18)
Karina null(20)

ふむ.普通に出来ますね.ちょっと意外.
なぜに意外かというと,モデルの名前はコンポーネント (Name クラス) を使っているからです.
では,{model.*} ではなく,個別にプロパティを指定してみましょう.
コンポーネントの指定の仕方が分からなかったので,とりあえずこんな感じで.

                            return session
                                    .createSQLQuery(
                                        "select model.ID {model.id}, "
                                            + "model.FIRST_NAME {model.name.firstName}, "
                                            + "model.LAST_NAME {model.name.lastName}, "
                                            + "model.age {model.age} "
                                            + "from MODEL model", "model",
                                        Model.class).list();

{model.name.firstName} なんていう指定がうまくいくのでしょうか?
その結果.

java.sql.SQLException: Unknown JDBC escape sequence: {

ぐはぁっ.(;_;)
ドキュメントには書いてなさそうですが,SQLLoader というクラスのソースを見ると,例外のメッセージとして

SQL queries only support properties mapped to a single column.

という文言が見つかりました.一つのプロパティは一つのカラムにしかマップ出来ない,ということですね.コンポーネントは一つのプロパティを複数のカラムにマップするものですから,SELECT 句で明示的に指定することは出来ないようです.{model.*} 記法を使うしかないということですね.


次は「13.3. Named SQL queries」.
ぐはぁっ.(ToT)
08/11 の「セッションその5 問い合せその他」では,

SQL を使う場合,問い合せ文字列の外部化は直接サポートされていないようです.残念!!!!

と書いたのですが,ちゃんとできるみたいです.しくしくしく.
外部化した SQL を使うには,HQL と同様,Session

    • Query getNamedQuery(String queryName)

を使うのですね.getNamedSQLQuery() みたいなメソッドが見あたらないので SQL は外部化できないと思いこんでしまったようです.心より恥じる.
そうかぁ,ソース上は HQL でも SQL でも,同じにできるんですね.それはいいかも♪
そんなことができるのは,HQL と SQL ではマッピングファイルへの記述が異なるためです.
HQL を外部化する場合には,<query> 要素を使いましたが,SQL を外部化するには

  • <sql-query> 要素

を使います.この要素は,<hibernate-mapping> 要素の子として,<query> 要素の後に,<hibernate-mapping> 要素の最後の子として記述します.
こいつは,一つだけ属性を持っています.

name
問い合せの名前を指定します.必須です.

<sql-query> 要素の内容には,二種類の要素と SQL 文字列を記述することができます.
要素の一つは

  • <return> 要素

です.こいつは次の属性を持っています.

alias
エイリアスを指定します.createSQLQuery() の第 2 引数に相当します.
class
永続クラスを指定します.createSQLQuery() の第 3 引数に相当します.

もう一つの要素は

  • <synchronize> 要素

ですが,こちらの使い道は分かりませんでした.残念!!!!
ともあれ (JW),使ってみましょう.
まず,雑誌のマッピングファイルに <sql-query> 要素を追加します.

    <sql-query name="cancam">
        <return alias="mag" class="study.Magazine"/>
        select {mag.*} from MAGAZINE mag where mag.NAME = 'CanCam'
    </sql-query>

そして問い合せクラスの匿名内部クラスの中を次のように修正.

                            return session.getNamedQuery("cancam").list();

その実行結果.

CanCam(Shogakukan):[Yuri Ebihara(24), Asami Usuda(19), Yu Yamada(20)]


ということで,さくっと 13 章も終了.
そんなわけで (どんなわけで?)... そうです,ついに次回 (来週) より,「パフォーマンスの中心で,愛をさけぶ」略して パフォチュー です (違).
DB にアクセスする回数を『はんぶんにしてくださぁぁぁぁぁい!!!!!』©眞鍋かをり
と,パフォチュってみる日がついにやってくるのです.
ふふっ,パフォチューには楽しみがたくさんあるんですよ.
最初は
『…おそっ……』©眞鍋かをり
で始まって,
『最速でお願いします』©眞鍋かをり
とか言ってみたり,
…………まだ遅いんだよ… ©眞鍋かをり
とかって文句たれてみたり.
そしてパフォチュった結果,DB アクセスは一瞬となり...
『もうおわったんですか!?』©眞鍋かをり
完璧だ!