web-dev-qa-db-ja.com

Hibernate Query.list()をList <Type>にキャストする「適切な」方法は何ですか?

私はHibernateの初心者であり、特定のフィルターに一致するオブジェクトのリストを返す簡単なメソッドを書いています。 List<Foo>は自然な戻り値の型のようです。

私が何をするにしても、compilerい@SuppressWarningsを使わない限り、コンパイラを幸せにすることはできません。

import Java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;

public class Foo {

    public Session acquireSession() {
        // All DB opening, connection etc. removed,
        // since the problem is in compilation, not at runtime.
        return null;
    }

    @SuppressWarnings("unchecked") /* <----- */

    public List<Foo> activeObjects() {
        Session s = acquireSession();
        Query   q = s.createQuery("from foo where active");
        return (List<Foo>) q.list();
    }
}

そのSuppressWarningsを取り除きたい。しかし、そうすると、警告が表示されます

Warning: Unchecked cast from List to List<Foo>

(私はそれを無視することができますが、そもそもそれを取得したくない)、そして、.list()戻り型に準拠するようにジェネリックを削除すると、警告が表示されます

Warning: List is a raw type. References to generic type List<E>
should be parameterized.

org.hibernate.mappingdoesList;を宣言していることに気付きました。しかし、それはまったく異なる型です-QueryJava.util.Listを生の型として返します。最近のHibernate(4.0.x)がパラメーター化された型を実装しないのは奇妙だと思うので、代わりに何か間違ったことをしているのではないかと思います。

オブジェクトのリストへの結果のキャスト に非常に似ていますが、ここでは「ハード」エラーはありません(システムはタイプFooを知っており、SQLQueryではなくストレートクエリを使用しています) 。喜びはありません。

Hibernate Class Cast Exception も有望そうに見えたので見ましたが、実際にnotを取得することに気付きましたException...私の問題は…警告のそれだけ-あなたがする場合、コーディングスタイル。

Jboss.orgのドキュメント、Hibernateのマニュアル、およびいくつかのチュートリアルでは、such detailのトピックをカバーしていないようです(または、適切な場所で検索しませんでしたか?)。彼らが詳細に入るとき、彼らはオンザフライのキャスティングを使います-そしてこれは公式のjboss.orgサイトになかったチュートリアルで、それで私は少し警戒しています。

コンパイルされたコードは、apparent問題なしで実行されます...私が知っている...まだ;結果は予想されたものです。

だから、私はこれを正しくやっていますか?明らかな何かが欠けていますか? 「公式」または「推奨」はありますか方法

78
LSerni

短い答え@SuppressWarningsが正しい方法です。

長い答え、HibernateはQuery.listメソッドから生のListを返します。 here を参照してください。これはHibernateのバグや解決可能な問題ではなく、クエリによって返されるタイプはコンパイル時にnotknownです。

したがって、あなたが書くとき

final List<MyObject> list = query.list();

ListからList<MyObject>への安全でないキャストを行っています-これは避けられません。

Listcouldには何かが含まれているため、キャストを安全に実行する方法はありません。

エラーをなくす唯一の方法は、さらにmoreいです

final List<MyObject> list = new LinkedList<>();
for(final Object o : query.list()) {
    list.add((MyObject)o);
}
94

解決策は、代わりにTypedQueryを使用することです。 EntityManagerからクエリを作成する場合、代わりに次のように呼び出します。

TypedQuery<[YourClass]> query = entityManager.createQuery("[your sql]", [YourClass].class);
List<[YourClass]> list = query.getResultList(); //no type warning

これは、名前付きクエリ、ネイティブの名前付きクエリなどでも同じように機能します。対応するメソッドの名前は、Vanillaクエリを返すものと同じです。戻り値の型がわかっている場合は、クエリの代わりにこれを使用してください。

20
Taugenichts

このような回避策を使用すると、コンパイラの警告を回避できます。

List<?> resultRaw = query.list();
List<MyObj> result = new ArrayList<MyObj>(resultRaw.size());
for (Object o : resultRaw) {
    result.add((MyObj) o);
}

ただし、このコードにはいくつかの問題があります。

  • 余分なArrayListを作成しました
  • クエリから返されたすべての要素に対する不要なループ
  • より長いコード。

そして、違いは見た目だけなので、そのような回避策を使用することは-私の意見では-無意味です。

これらの警告を受け入れるか、それらを抑制する必要があります。

6

あなたの質問に答えるために、それを行うための「適切な方法」はありません。気になるのが警告だけの場合、その拡散を避ける最善の方法はQuery.list()メソッドをDAOにラップすることです:

public class MyDAO {

    @SuppressWarnings("unchecked")
    public static <T> List<T> list(Query q){
        return q.list();
    }
}

これにより、@SuppressWarnings("unchecked")を1回だけ使用できます。

3
Pdv
List<Person> list = new ArrayList<Person>();
Criteria criteria = this.getSessionFactory().getCurrentSession().createCriteria(Person.class);
for (final Object o : criteria.list()) {
    list.add((Person) o);
}
3
user3184564

私のために働く唯一の方法は、イテレータでした。

Iterator iterator= query.list().iterator();
Destination dest;
ArrayList<Destination> destinations= new ArrayList<>();
Iterator iterator= query.list().iterator();
    while(iterator.hasNext()){
        Object[] Tuple= (Object[]) iterator.next();
        dest= new Destination();
        dest.setId((String)Tuple[0]);
        dest.setName((String)Tuple[1]);
        dest.setLat((String)Tuple[2]);
        dest.setLng((String)Tuple[3]);
        destinations.add(dest);
    }

私が見つけた他の方法では、キャストの問題がありました

3
Popa Andrei

次のようなResultTransformerを使用します。

public List<Foo> activeObjects() {
    Session s = acquireSession();
    Query   q = s.createQuery("from foo where active");
    q.setResultTransformer(Transformers.aliasToBean(Foo.class));
    return (List<Foo>) q.list();
}
2
lakreqta

Transformersを使用するだけでうまくいきませんでした。型キャスト例外が発生していました。

sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class))は、リスト要素の固定MYEngityNameタイプではなく、戻りリスト要素でオブジェクトの配列を取得していたため、機能しませんでした。

sqlQuery.addScalar(-)を選択した各列とそのタイプを追加し、特定の文字列タイプの列にタイプをマップする必要がない場合、次の変更を行うとうまくいきました。 addScalar("langCode");など

そして、MYEngityNameをNextEnityに結合しました。クエリでselect *だけを返すことはできません。戻りリストにObjectの配列が表示されます。

以下のコードサンプル:

session = ht.getSessionFactory().openSession();
                String sql = new StringBuffer("Select txnId,nft.mId,count,retryReason,langCode FROM  MYEngityName nft INNER JOIN NextEntity m on nft.mId  =  m.id where nft.txnId < ").append(lastTxnId)
                       .append(StringUtils.isNotBlank(regionalCountryOfService)? " And  m.countryOfService in ( "+ regionalCountryOfService +" )" :"")
                       .append(" order by nft.txnId desc").toString();
                SQLQuery sqlQuery = session.createSQLQuery(sql);
                sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class));
                sqlQuery.addScalar("txnId",Hibernate.LONG)
                        .addScalar("merchantId",Hibernate.INTEGER)
                        .addScalar("count",Hibernate.BYTE)
                        .addScalar("retryReason")
                        .addScalar("langCode");
                sqlQuery.setMaxResults(maxLimit);
                return sqlQuery.list();

助けになるかもしれません。このように私のために働く。

0
Laxman G

適切な方法は、Hibernateトランスフォーマーを使用することです。

public class StudentDTO {
private String studentName;
private String courseDescription;

public StudentDTO() { }  
...
} 

List resultWithAliasedBean = s.createSQLQuery(
"SELECT st.name as studentName, co.description as courseDescription " +
"FROM Enrolment e " +
"INNER JOIN Student st on e.studentId=st.studentId " +
"INNER JOIN Course co on e.courseCode=co.courseCode")
.setResultTransformer( Transformers.aliasToBean(StudentDTO.class))
.list();

StudentDTO dto =(StudentDTO) resultWithAliasedBean.get(0);

Object []を繰り返し処理することは冗長であり、パフォーマンスがいくらか低下します。トランスフォーマーの使用に関する詳細情報は、次のとおりです。 HQLおよびSQLのトランスフォーマー

さらにシンプルなソリューションを探している場合は、すぐに使用できるマップトランスフォーマーを使用できます。

List iter = s.createQuery(
"select e.student.name as studentName," +
"       e.course.description as courseDescription" +
"from   Enrolment as e")
.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP )
.iterate();

String name = (Map)(iter.next()).get("studentName");
0
ANTARA