2016-09-07 5 views
11

InheritanceType.JOINEDで動作するネイティブクエリを取得するのには苦労しています。ハイバーネイトdocumentationから私は見つけました:ネイティブクエリにInheritanceType.JOINEDのエンティティを追加します。

13.1.6。

継承の一部としてマップされたエンティティをクエリするネイティブSQLクエリには、ベースクラスとすべてのサブクラスのすべてのプロパティが含まれている必要があります。

使用して、次の2つのエンティティ:

@Data 
@Entity 
@Table(name = "my_super") 
@EqualsAndHashCode(of = {"id"}) 
@Inheritance(strategy = InheritanceType.JOINED) 
public abstract class MySuper { 

    @Id 
    @Column(name = "id") 
    @GeneratedValue(strategy = GenerationType.SEQUENCE) 
    private long id; 

} 

そして:

@Data 
@Entity 
@Table(name = "my_sub_a") 
@EqualsAndHashCode(callSuper = true) 
public class MySubA extends MySuper { 

    @Column(name = "x") 
    private int x; 

} 

私が使用してネイティブクエリを作成しよう:

Object actual = session 
    .createNativeQuery("SELECT {s.*} FROM my_super {s} LEFT JOIN my_sub_a {a} USING (id)") 
    .addEntity("s", MySuper.class) 
    .getSingleResult(); 

それはクエリに変換:

SELECT s.id as id1_1_0_, s_1_.x as x1_0_0_, case when s_1_.id is not null then 1 when s.id is not null then 0 end as clazz_0_ FROM my_super s LEFT JOIN my_sub_a a USING (id) 

そしてで失敗します。だから私たちはそれは同様my_sub_ax列を必要とするかもしれないことを考え出す、その仕事をして別名インジェクションを観察

javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not prepare statement 
Caused by: org.hibernate.exception.SQLGrammarException: could not prepare statement 
Caused by: org.h2.jdbc.JdbcSQLException: Column "S_1_.X" not found; SQL statement: 

。しかし、my_sub_aのエイリアスを知ることはできません。このエイリアスも適切に接続されるように、コードをどのように変更する必要がありますか?

マイコードは、https://gist.github.com/JWGmeligMeyling/51e8a305f3c268eda473511e202f76e8で入手できます。

(私はこのクエリは簡単にだけでなくJPQLまたはHQLで表現することができることを認識しています、とさえEntityManagerSession APIのを使用して達成することができる。私はしかし私ことを、より複雑なクエリでこれを使用したくありませんこの質問に必要なすべての詳細に簡略化されています)。

+0

おそらくA' 'と' '{A}を交換する、または別の' .addEntity( "A" を追加する必要があり、次のいずれかここで

は、私はそれが少しを助けるん願って、テストされています.class) '、それはエイリアスを混乱させているからです。しかし、これはちょうど試しです。 –

+0

私が引用したドキュメントから得られるものは、選択肢に 'SELECT {s。*}、{a。*} [...]'というプロパティを含める必要があるということです。また、おそらく抽象クラスをインスタンス化できないため、スーパークラスなしで '.addEntity(" a "、MySubA.class)'を試してください。 – drakyoko

+0

Hibernateは '.addEntity(" a "、MySubA.class)'を使用するなら、それはそうではありません。Hibernateは、同じエンティティに属するプロパティを調べるのではなく、2つの異なるエンティティをフェッチする必要があると仮定します。私が照会で参照していない '{a}'を使用しているのは、実際はコピー・ペースト・エラーです。 –

答えて

2

問題は、使用されているデータベースの方言、つまり「エキゾチックな」クエリ部分に関連しているようです。あなたが記述した振る舞いは、あなたの提供されたクエリーで複製可能ですが、使用したい方言に応じて、小さなtweeksを使ってエラーなしで実行しています。

あなたの例では、H2データベースを使用していますが、それはあなたの生産方言ではないと思いますか? PostgresSQLデータベース(バージョン9.5)でも試してみました。

オリジンクエリでは、H2とPostgreSQLでの動作は同じです。しかし、中括弧をカラムとエイリアス(一部の場合はODBC escape sequencesのように見える)から削除し、USING clauseを明示的なON a.id = s.idの条件に変更すると、クエリは例外なく実行可能になります。

動作を確認するHibernate SessionまたはEntityManagerのいずれかを使用して異なるクエリでいくつかのテストを作成しました。リンクされたサンプルコードを見てから、Hibernate Session interfaceEntityManager methods like createNativeQueryの混在した使い方で混乱してしまったからです。疑いがある場合は、私は両方の試みをしました。 私は同じエンティティを使用していましたが、便宜上、例のように、Spring Boot environmentのように同じ設定とテストコードを使用しました。Spring Boot Profilesを使用したデータベースを切り替えるには、両方のデータベースの設定がある場合は、@ActiveProfiles("postgres")の部分をアクティブ化/アンコメントするだけです。 MySubA、

import static org.assertj.core.api.Assertions.assertThat; 

import java.util.List; 

import javax.persistence.EntityManager; 
import javax.persistence.PersistenceContext; 

import org.hibernate.Session; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.boot.test.context.SpringBootTest; 
import org.springframework.test.context.ActiveProfiles; 
import org.springframework.test.context.junit4.SpringRunner; 
import org.springframework.transaction.annotation.Transactional; 

@RunWith(SpringRunner.class) 
@SpringBootTest 
@Transactional 
//@ActiveProfiles("postgres") // activate for PostgreSQL tests 
public class InheritanceDemoApplicationTests { 

    private static final String QUERY = "SELECT {s.*} FROM my_super {s} LEFT JOIN my_sub_a {a} USING (id)"; 
    private static final String QUERY_WITHOUT_ODBC_ESCAPES = "SELECT s.* FROM my_super s LEFT JOIN my_sub_a a USING (id)"; 
    private static final String QUERY_WITHOUT_USING_KEYWORD = "SELECT {s.*} FROM my_super {s} LEFT JOIN my_sub_a {a} ON a.id = s.id"; 
    private static final String QUERY_WITHOUT_ODBC_ESCAPES_AND_WITHOUT_USING_KEYWORD = "SELECT s.* FROM my_super s LEFT JOIN my_sub_a a ON a.id = s.id"; 

    @PersistenceContext 
    private EntityManager entityManager; 

    @Test 
    public void sessionQuery() { 
     validateQueryViaSession(QUERY); 
    } 

    @Test 
    public void entityManagerQuery() { 
     validateQueryViaEntityManager(QUERY); 
    } 

    @Test // works for PostgreSQL 
    public void sessionQueryWithoutOdbc() { 
     validateQueryViaSession(QUERY_WITHOUT_ODBC_ESCAPES); 
    } 

    @Test // works for PostgreSQL 
    public void entityManagerQueryWithoutOdbc() { 
     validateQueryViaEntityManager(QUERY_WITHOUT_ODBC_ESCAPES); 
    } 

    @Test 
    public void sessionQueryWithoutUsing() { 
     validateQueryViaSession(QUERY_WITHOUT_USING_KEYWORD); 
    } 

    @Test // works for H2 
    public void entityManagerQueryWithoutUsing() { 
     validateQueryViaEntityManager(QUERY_WITHOUT_USING_KEYWORD); 
    } 

    @Test // works for H2 & PostgreSQL 
    public void sessionQueryWithoutOdbcAndWithoutUsing() { 
     validateQueryViaSession(QUERY_WITHOUT_ODBC_ESCAPES_AND_WITHOUT_USING_KEYWORD); 
    } 

    @Test // works for H2 & PostgreSQL 
    public void entityManagerQueryWithoutOdbcAndWithoutUsing() { 
     validateQueryViaEntityManager(QUERY_WITHOUT_ODBC_ESCAPES_AND_WITHOUT_USING_KEYWORD); 
    } 

    @SuppressWarnings("rawtypes") 
    private void validateQueryViaSession(final String queryString) { 
     final MySubA match = persistMySubA(); 
     List result = entityManager.unwrap(Session.class).createSQLQuery(queryString).addEntity("s", MySuper.class) 
       .list(); 
     assertThat(result.iterator().next()).isEqualToComparingFieldByField(match); 
    } 

    @SuppressWarnings("rawtypes") 
    private void validateQueryViaEntityManager(final String queryString) { 
     final MySubA match = persistMySubA(); 
     List result = entityManager.createNativeQuery(queryString, MySuper.class).getResultList(); 
     assertThat(result.iterator().next()).isEqualToComparingFieldByField(match); 
    } 

    private MySubA persistMySubA() { 
     final MySubA mySubA = new MySubA(); 
     mySubA.setX(1); 
     entityManager.persist(mySubA); 
     entityManager.flush(); 
     return mySubA; 
    } 

} 
+0

面白いキャッチ!中括弧はHibernateの例を起源としていましたが、私はhibernateがフィールドを検出するために必要と思っていましたが、明らかに状況を悪化させました。今私はそれも考えると、Hibernateはuniononの構築に気づいていません(私は投影以外の何かを使うと思っていませんが、クエリ全体を処理しているようです)。とにかく、これを調査してくれてありがとう! –

+0

ようこそ。 :)エンティティの作成を満たすには、すべてのフィールドを投影の一部にする必要があります。中括弧はここでは必要ありません。専用のコンストラクタを提供し、 '新しいものを選択...'を利用することもできます。https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/queryhql.html –

関連する問題