web-dev-qa-db-ja.com

内部結合を使用してHibernateを使用して複数のオブジェクトタイプを返す

Hibernateでのクエリに問題があるようです。 2つのテーブルで内部結合を実行しています。

SELECT * FROM product p INNER JOIN warehouse w ON p.wid = w.id

製品表:

id | name | wid | price | stock .....

倉庫テーブル:

id | name | city | lat | long .....

結合結果:

id | name | wid | price | stock | id | name | city | lat | long .....

クエリを実行すると。

Session.createSQLQuery(this.query)
        .addEntity("p", Product.class)
        .addEntity("w", Warehouse.class).list();

したがって、すべての結果に対して、Product objectWarehouse objectを含むオブジェクトを取得します。

これは予想されます。問題は、hibernateが製品のIDと名前をウェアハウスオブジェクトのIDと名前のプロパティに割り当てることです。ウェアハウスプロジェクトの作成に関しては、結合結果の最初の2列がオーバーライディングしているように見えます。 Productオブジェクトには、常に正しいデータが含まれています。

この問題を回避する方法を見つけて、正しいウェアハウスデータを表すid列とname列を見つけるための提案をいただければ幸いです。

前もって感謝します。

18
Dan

{}形式を使用して、列名の重複に関する問題を回避します。

SELECT {p.*}, {w.*} FROM product p INNER JOIN warehouse w ON p.wid = w.id

Hibernateリファレンスドキュメント 、セクション18.1.4から。複数のエンティティを返す:

これまで、結果セットの列名は、マッピングドキュメントで指定された列名と同じであると想定されていました。同じ列名が複数のテーブルに表示される可能性があるため、これは複数のテーブルを結合するSQLクエリでは問題になる可能性があります。

次のクエリでは、列エイリアスの挿入が必要です(ほとんどの場合失敗します)。

sess.createSQLQuery("SELECT c.*, m.*  FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
 .addEntity("cat", Cat.class)
 .addEntity("mother", Cat.class)

このクエリは、行ごとに2つのCatインスタンス(猫とその母親)を返すことを目的としていました。ただし、名前の競合があるため、クエリは失敗します。インスタンスは同じ列名にマップされます。また、一部のデータベースでは、返される列エイリアスは「c.ID」、「c.NAME」などの形式である可能性が高く、マッピングで指定された列(「ID」および「NAME」)と等しくありません。 。

次のフォームは、列名の重複に対して脆弱ではありません。

sess.createSQLQuery("SELECT {cat.*}, {mother.*}  FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
 .addEntity("cat", Cat.class)
 .addEntity("mother", Cat.class)

このクエリは次を指定しました:

sQLクエリ文字列。Hibernateが列エイリアスを挿入するためのプレースホルダーを使用して、クエリによって返されるエンティティを{cat.*}および{mother.*}上記で使用されている表記は、「すべてのプロパティ」の省略形です。

9
Matthew Farwell

参照

エンティティが同じクラスに属していない場合のサンプルを次に示します。

public static void main(String[] args) {
        Session sess = NewHibernateUtil.getSessionFactory().openSession();
        SQLQuery q = null;
        String query = "select a.*, u.* from user u, account a where a.iduser=u.iduser";
        q = sess.createSQLQuery(query);
        q.addEntity(User.class);
        q.addEntity(Account.class);
        List lst = q.list();
        System.out.println("" + lst.size());

        for (int i = 0; i < lst.size(); i++) {
            System.out.println(((Object[]) lst.get(i))[0]);     //account bean, actually this is in reverse order - so this is user bean
            System.out.println(((Object[]) lst.get(i))[1]);     //user bean         & this account bean
        }
        sess.close();
    }
6
coding_idiot

Coding_idiotが言ったように、クエリ結果のエンティティは異なるクラスからのものであるため、おそらくわからない場合は、要素内のすべてのオブジェクトにアクセスできます。

  1. クエリ結果を取得するために_List<Object>_を作成します(例:List<Object> objs = (List<Object>)query.getResultList();
  2. Forを使用してこの配列を反復処理します(例:for (Object obj : objs){...}
  3. _List<Object>_のすべての要素には_Object[]_があるため、すべての要素をこのクラスにキャストします(例:Object[] o = (Object[]) obj;
  4. インデックス番号を介した要素へのアクセス(例:_o[4]_)

コードサンプル:

_    Query query = JPA.em().createNativeQuery("SELECT * FROM product p
    INNER JOIN warehouse w ON p.wid = w.id");

    /* I suppose that it return fields sorted by entities and field in
    database:
    *
    * 0: product.id | 1: product.name | 2: product.wid | 3: product.price | 4: product.stock | n-1: product.N-1Field
    * n: warehouse.id | n+1: name | n+2: warehouse.city | n+3: warehouse.lat | n+4: warehouse.long | m-1: warehouse.M-1Field
    *
    * Join result: id | name | wid | price | stock | ... | id | name | city | lat | long | ...
    */

    List<Object> objs = (List<Object>)query.getResultList();

    for (Object obj : objs) {
        Object[] o = (Object[]) obj;
        String productId =  String.valueOf(o[0]);
        String productName =  String.valueOf(o[1]);
        String productWid =  String.valueOf(o[2]);

        ... }
_
6
EnriMR