3.1 はじめに

もう 1 つ話したい便利な点は、NativeQueryです。すでにご存知のとおり、NativeQuery を使用すると、ネイティブ SQL でクエリを作成できます。ただし、さらに興味深いのは、クエリ結果を取得するときにクラス マッピングを使用する必要がないことです。

むしろ例を示したいと思います。

List<Object[]> persons = session.createNativeQuery("SELECT * FROM Person").list();

この例では、クエリ結果の行に一致するクラスを渡さず、代わりに Object オブジェクトの配列を使用します。

これは、テーブル内のいくつかの列だけを選択する場合に便利です。例:


List<Object[]> persons = session.createNativeQuery("SELECT id, name FROM Person").list();
 
for(Object[] person : persons) {
    Number id = (Number) person[0];
    String name = (String) person[1];
}

これは、ResultSet オブジェクトを取得してその行からデータを読み取るときの JDBC のアプローチに似ています。

ただし、Hibernate では、これの信頼性を高めるためのさまざまな方法が提供されています。たとえば、減算する列のタイプを指定できます。例:


Query<Object[]> query = session.createNativeQuery("SELECT id, name FROM Person");
query.addScalar("id", StandardBasicTypes.LONG);
query.addScalar("name", StandardBasicTypes.STRING);
List<Object[]> persons = query.list();
 
for(Object[] person : persons) {
    Long id = (Long) person[0];
    String name = (String) person[1];
}

3.2 エンティティのマッピング

NativeQueryの結果を解析するときに Hibernate が使用するクラスを明示的に指定することもできます。これはさまざまな方法で実行できます。


Query<Person> query = session.createNativeQuery("SELECT * FROM Person")
    .addEntity(Person.class);
    .list();

そしてもちろん、あなたが知っている古き良き形式:


Query<Person> query = session.createNativeQuery("SELECT * FROM Person", Person.class).list();

1 つ目のアプローチはネイティブ Hibernate アプローチで、2 つ目は JPA アプローチです。JPA アプローチは、Hibernate が長年存在していた後に考案された標準であるため、より便利で簡潔です。そして Hibernate は進化し、古いバージョンとの互換性を維持するために古いアプローチをサポートする必要がありました。

ちなみに、Hibernate ではそのアプローチのおかげで、1 つのクラスをクエリ結果マッピングに接続するのではなく、複数のクラスを接続することができます。例:


List<Phone> results = session.createNativeQuery(
    "SELECT {ph.*}, {pr.*}" +
    "FROM Phone ph" +
    "JOIN Person pr ON ph.person_id = pr.id")
.addEntity("ph", Phone.class)
.addJoin("pr", "ph.person")
.list();
 
for (Phone. phone : results) {
           	assertNotNull( phone.getPerson().getName() );
}

NativeQueryを使用したこのアプローチを使用すると、データベースからのデータの選択を高速化できます。一部の列が必要ないことがわかっている場合は、リクエストでそれらを省略できます。

Hibernate がCacheまたはLazyLoadingメカニズムを使用したい場合でも、すべての子エンティティを一度にロードすることもできます。さらに、子エンティティにはデータベース内に多数の列がある場合があり、そのうちの一部のみを選択できます。

3.3 DTO マッピング

Hibernate では、結果のマッピングに非エンティティ クラスを使用することもできます。注釈がなく、どのテーブルにもマップされていないクラス。

例:

public class PersonSummaryDTO {
    private Number id;
    private String name;

    public Number getId() {
    	return id;
    }

    public void setId(Number id) {
    	this.id = id;
    }

    public String getName() {
    	return name;
    }

    public void setName(String name) {
    	this.name = name;
	}
}

List<PersonSummaryDTO> dtos = session.createNativeQuery(
    "SELECT p.id as \"id\", p.name as \"name\" FROM Person p")
.setResultTransformer(Transformers.aliasToBean(PersonSummaryDTO.class) )
.list();

PersonsummaryDTO クラスには注釈がないため、SQL クエリ内の列の名前は、PersonsummaryDTO クラスのフィールドの名前と正確に一致する必要があります。

これは、アプリケーションが読み取り専用モードでのみ接続されている外部データベースからデータを読み取る場合に非常に便利です。つまり、50 を超える列を持つテーブルへのアクセスが許可され、選択を高速化するためにデータが非正規化形式で保存されます。

あるいは、誰かがクラス階層を 1 つのテーブルに保存することに決め、5 年間でこのテーブルが悪魔に足を折られるほど大きくなったとします。このテーブルからいくつかの列 (ID とユーザー名) を選択し、クライアントに提供する必要があります。

理解できたと思いますが、このトピックについてさらに詳しく知りたい場合は、次のリンクで詳細を読むことができます。

ネイティブ SQL クエリ