web-dev-qa-db-ja.com

Hibernate Unionの代替

Hibernateを使用してユニオンクエリを実装するには、どのような代替手段が必要ですか?現時点では、休止状態がユニオンクエリをサポートしていないことを知っています。現在、ユニオンを作成する唯一の方法は、ビューテーブルを使用することです。

もう1つのオプションはプレーンjdbcを使用することですが、この方法では、すべての例/基準クエリの利点、およびhibernateがテーブル/列に対して実行するhibernateマッピング検証を失います。

54
Miguel Ping

VIEWを使用します。エンティティ名を使用して、同じクラスを異なるテーブル/ビューにマッピングできるため、重複はほとんどありません。そこにいても、それで問題ありません。

プレーンJDBCには別の隠れた問題があります。Hibernateセッションキャッシュを認識しないため、トランザクションの終わりまで何かがキャッシュされ、Hibernateセッションからフラッシュされない場合、JDBCクエリはそれを見つけられません。時々非常に不可解かもしれません。

29

id in (select id from ...) or id in (select id from ...)を使用できます

例えばnon-workingの代わりに

from Person p where p.name="Joe"
union
from Person p join p.children c where c.name="Joe"

できます

from Person p 
  where p.id in (select p1.id from Person p1 where p1.name="Joe") 
    or p.id in (select p2.id from Person p2 join p2.children c where c.name="Joe");

少なくともMySQLを使用すると、後でパフォーマンスの問題が発生します。代わりに、2つのクエリで貧乏人の結合を行う方が簡単な場合があります。

// use set for uniqueness
Set<Person> people = new HashSet<Person>((List<Person>) query1.list());
people.addAll((List<Person>) query2.list());
return new ArrayList<Person>(people);

多くの場合、1つの複雑なクエリよりも2つの単純なクエリを行う方が適切です。

編集:

例として、サブセレクトソリューションからの結果のMySQLクエリのEXPLAIN出力を次に示します。

mysql> explain 
  select p.* from PERSON p 
    where p.id in (select p1.id from PERSON p1 where p1.name = "Joe") 
      or p.id in (select p2.id from PERSON p2 
        join CHILDREN c on p2.id = c.parent where c.name="Joe") \G
*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: a
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 247554
        Extra: Using where
*************************** 2. row ***************************
           id: 3
  select_type: DEPENDENT SUBQUERY
        table: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
        Extra: Impossible WHERE noticed after reading const tables
*************************** 3. row ***************************
           id: 2
  select_type: DEPENDENT SUBQUERY
        table: a1
         type: unique_subquery
possible_keys: PRIMARY,name,sortname
          key: PRIMARY
      key_len: 4
          ref: func
         rows: 1
        Extra: Using where
3 rows in set (0.00 sec)

最も重要なことは、1。行はインデックスを使用せず、20万行以上と見なします。悪い!両方のサブクエリがミリ秒単位である場合、このクエリの実行には0.7秒かかりました。

66
sfussenegger

ウラジミールに同意しなければなりません。私もHQLでUNIONを使用することを検討しましたが、その方法を見つけることができませんでした。奇妙なことは、(Hibernate FAQで)UNIONがサポートされていないこと、UNIONに関するバグレポートが「修正済み」とマークされていること、ステートメントがUNIONで切り捨てられると言っている人々のニュースグループ、およびそれが機能していることを報告している人々のその他のニュースグループを見つけることができることでしたうまく... 1日いじってから、HQLをプレーンなSQLに移植することになりましたが、データベースのビューでそれを行うのは良い選択肢です。私の場合、クエリの一部は動的に生成されたため、代わりにコードでSQLを作成する必要がありました。

6
CodingWithSpike

HQLのユニオンを使用した1つの重要なシナリオ(多くの苦労をした)の解決策があります。

例:動作しない代わりに:-

select i , j from A a  , (select i , j from B union select i , j from C) d where a.i = d.i 

OR

select i , j from A a  JOIN (select i , j from B union select i , j from C) d on a.i = d.i 

Hibernate HQLでできます->

Query q1 =session.createQuery(select i , j from A a JOIN B b on a.i = b.i)
List l1 = q1.list();

Query q2 = session.createQuery(select i , j from A a JOIN C b on a.i = b.i)
List l2 = q2.list();

次に、両方のリストを追加できます->

l1.addAll(l2);
4
Vijay Gupta

ビューはより良いアプローチですが、hqlは通常ListまたはSet ...を返すため、list_1.addAll(list_2)を実行できます。組合と比較して完全にひどいですが、動作するはずです。

3
Patrick

おそらく、もっと簡単な問題を解決する必要がありました。私の「インスタンス」はJPAで、JPAプロバイダーとしてHibernateを使用していました。

3つの選択(2つ目の場合は2つ)を複数の選択に分割し、返されたコレクションを結合して、「すべてを結合」を効果的に置き換えました。

2
Walt

私もこの苦痛を経験してきました-クエリが動的に生成された場合(たとえば、Hibernate Criteria)、それを行う実用的な方法を見つけることができませんでした。

私にとって朗報は、Oracleデータベースで「または」を使用する場合、パフォーマンスの問題を解決するためだけにユニオンを調査していたことです。

パトリックが投稿したソリューション(セットを使用してプログラムで結果を組み合わせる)はwhileい(特に、結果のページングも行いたいため)で十分でした。

0
Daniel Alexiuc

ここに特別なケースがありますが、独自の回避策を作成するように促すかもしれません。ここでの目標は、レコードが特定の基準を満たす2つの異なるテーブルからのレコードの総数をカウントすることです。この手法は、複数のテーブル/ソースからデータを集約する必要がある場合に有効だと思います。

特別な中間クラスのセットアップがあるため、名前付きクエリを呼び出すコードは短くて便利ですが、通常は名前付きクエリと組み合わせて使用​​する任意のメソッドを使用してクエリを実行できます。

QueryParms parms=new QueryParms();
parms.put("PROCDATE",PROCDATE);

Long pixelAll = ((SourceCount)Fetch.row("PIXEL_ALL",parms,logger)).getCOUNT();

ここでわかるように、名前付きクエリはユニオンステートメントのようにひどく見え始めます。

@Entity
@NamedQueries({
        @NamedQuery(
            name  ="PIXEL_ALL",
            query = "" +
                    "  SELECT new SourceCount(" +
                    "     (select count(a) from PIXEL_LOG_CURR1 a " +
                    "       where to_char(a.TIMESTAMP, 'YYYYMMDD') = :PROCDATE " +
                    "     )," +
                    "     (select count(b) from PIXEL_LOG_CURR2 b" +
                    "       where to_char(b.TIMESTAMP, 'YYYYMMDD') = :PROCDATE " +
                    "     )" +
                    ") from Dual1" +
                    ""
    )
})

public class SourceCount {
    @Id
    private Long   COUNT;

    public SourceCount(Long COUNT1, Long COUNT2) {
        this.COUNT = COUNT1+COUNT2;
    }

    public Long getCOUNT() {
        return COUNT;
    }

    public void setCOUNT(Long COUNT) {
        this.COUNT = COUNT;
    }
}

ここでの魔法の一部は、ダミーテーブルを作成し、そこに1つのレコードを挿入することです。私の場合、データベースがOracleであるため、dual1と名付けましたが、ダミーテーブルと呼ばれるものは重要ではないと思います。

@Entity
@Table(name="DUAL1")
public class Dual1 {
    @Id
    Long ID;
}

ダミーレコードを挿入することを忘れないでください。

SQL> insert into dual1 values (1);
0
wryan



パトリックが言ったように、それぞれから[〜#〜] list [〜#〜] sを追加[〜#〜] select [〜#〜]良い考えですが、NION ALLのように機能することを覚えておいてください。この副作用を回避するには、オブジェクトが既に最終コレクションに追加されているかどうかを制御するだけです。いいえの場合、追加します。
他に気にする必要があるのは、各[〜#〜] join [〜#〜]がある場合です[〜#〜] select [〜 #〜]、結果はオブジェクト配列のリストになります(List<Objetc[]>)そのため、必要なオブジェクトのみを保持するために繰り返し処理する必要があります。

うまくいくことを願っています。

0