Hibernate Query.list () 'i List <Type>' a çevirmenin "uygun" yolu nedir?


84

Hazırda bekletme konusunda acemiyim ve belirli bir filtreyle eşleşen nesnelerin bir listesini döndürmek için basit bir yöntem yazıyorum. List<Foo>doğal bir dönüş türü gibi görünüyordu.

Ne yaparsam yapayım, çirkin birini kullanmadığım sürece derleyiciyi mutlu edemiyorum @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();
    }
}

Bundan kurtulmak isterimSuppressWarnings . Ama yaparsam uyarıyı alırım

Warning: Unchecked cast from List to List<Foo>

(Görmezden gelebilirim, ancak ilk etapta anlamamak istiyorum) ve .list()dönüş türüne uymak için jeneriği kaldırırsam , uyarıyı alıyorum

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

Ben fark org.hibernate.mapping yapar bir beyan List; ama tamamen farklı bir tür - ham tür olarak a Querydöndürür java.util.List. Yeni bir Hazırda Bekletme'nin (4.0.x) parametreleştirilmiş türleri uygulamamasını garip buluyorum, bu yüzden yanlış bir şey yaptığımdan şüpheleniyorum.

Bir nesne listesine Cast Hibernate sonucuna çok benziyor , ancak burada "zor" hatalarım yok (sistem Foo türünü biliyor ve bir SQLQuery değil, düz bir Sorgu kullanıyorum). Yani neşe yok.

Ben de baktım hazırda Sınıf Cast İstisna o vaat görünüyordu beri ama sonra ben do fark değil aslında herhangi olsun Exception... benim sorunum sadece bir uyarı - bir kodlama stili, eğer olacak.

Jboss.org'daki belgeler, Hibernate kılavuzları ve birkaç öğretici, konuyu bu kadar ayrıntılı olarak ele almıyor gibi görünüyor (veya doğru yerlerde aramadım mı?). Ayrıntıya girdiklerinde, anında döküm kullanıyorlar - ve bu resmi jboss.org sitesinde olmayan eğitimlerde, bu yüzden biraz temkinliyim.

Kod, derlenmiş bir kere, hiçbir ile çalışır belirgin sorun ... ben ... henüz biliyoruz ki; ve sonuçlar beklenenlerdir.

Yani: bunu doğru yapıyor muyum? Bariz bir şeyi mi kaçırıyorum? Bunu Yapmanın "resmi" veya "önerilen" bir yolu var mı ?

Yanıtlar:


101

Kısa cevap @SuppressWarnings, gitmenin doğru yoludur.

Uzun cevap, hazırda bir çiğ döndüren Listgelen Query.listyöntemle, bkz burada . Bu, Hibernate ile ilgili bir hata veya çözülebilecek bir şey değildir , sorgu tarafından döndürülen tür derleme sırasında bilinmemektedir .

Bu nedenle yazdığın zaman

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

Sen bir güvensiz döküm yapıyoruz Listiçin List<MyObject>- bu kaçınılması mümkün değildir.

Olarak güvenle döküm yürütebilecekleri bir yolu yoktur List olabilir bir şey içerirler.

Hatayı ortadan kaldırmanın tek yolu daha da çirkin olmaktır.

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

4
Daha iyi bir cevabın gelmesini umarak, sadece cevabınızı yükseltecektim. - Bunun yerine bu sorunu hem Bruce Eckel (Java Düşünme) ve Robert Sedgewick'e tarafından "çirkin döküm" olarak anılacaktır öğrendim Sedgewick'e. Ayrıca stackoverflow.com/questions/509076/… buldum . İç çekmek.
LSerni

6
Kullanım tarzınızı beğendimfinal
Pavel

9
Derin uyku adamlar tip bir argüman eklerseniz Class<?>içinde list(), sorunun çözülebilir. Böyle çirkin bir API kullanmak utanç verici.
Bin Wang

@BinWang sonra güvensiz oyuncu başka bir yerde olur, sorunu çözmez - sadece onu hareket ettirir. Tabii HQL API etkili bir onaylanmaz söylemek yıllarda artık. JPA, Criteria Query API adlı bir tür güvenli sorgu API'sine sahiptir .
Örümcek Boris

2
@PeteyPabPro Ham türlerden kaçınılması gerektiğini kabul etmeme rağmen, bir sorgunun sonuçlarının bir List<Object>. Sonuçlar beklenen türe dönüştürülmeli ve sorgunun doğru sonuçları döndürdüğünden emin olmak için birim testleri eklenmelidir. Sorgularla ilgili hataların " kodun ilerleyen bölümlerinde " ortaya çıkması kabul edilemez . Örneğiniz, 21. yüzyılda aforoz olması gereken kodlama uygulamalarına karşı bir argüman. A sahip olmanın asla kabul edilemez olduğunu söyleyebilirim List<Object>.
Örümcek Boris

26

Çözüm, bunun yerine TypedQuery kullanmaktır. EntityManager'dan bir sorgu oluştururken bunun yerine şu şekilde çağırın:

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

Bu aynı zamanda adlandırılmış sorgular, yerel adlandırılmış sorgular vb. İçin de aynı şekilde çalışır. Karşılık gelen yöntemler, vanilya sorgusunu döndürecek olanlarla aynı adlara sahiptir. Dönüş türünü bildiğiniz her zaman Sorgu yerine bunu kullanın.


1
Bir yan not olarak, bu, satır içi oluşturduğunuz tamamen yerel sorgular için çalışmaz. Döndürülen sorgu her zaman bir sorgu olup, yazılı bir sorgu değildir :(
Taugenichts

Şimdi, hata mesajı gitti ve ortaya çıkan liste Nesne nesnesi yerine gerçek nesneleri kullanıyor .. harika yanıt!
Johannes

Bu, hibernate 5.0'da piyasaya sürülmüş gibi görünüyor. 4.3.11'de görmüyorum ama aşağıdaki makale 5.3'e atıfta bulunuyor. wiki.openbravo.com/wiki/… Ayrıca, ona atıfta bulunan bir Ocak 2014 yığın aşımı gönderisini görüyorum: stackoverflow.com/a/21354639/854342 .
Curtis Yallop

Hibernate-jpa-2.0-api'nin bir parçasıdır. Ben şu anda hazırda bekletme 4.3'te kullandığımdan, bunu 4.3 hazırda bekletme modunda kullanabilirsiniz.
Taugenichts

6

Derleyici uyarısını aşağıdaki gibi geçici çözümlerle önleyebilirsiniz:

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

Ancak bu kodla ilgili bazı sorunlar var:

  • gereksiz ArrayList oluşturdu
  • sorgudan döndürülen tüm öğeler üzerinde gereksiz döngü
  • daha uzun kod.

Ve fark sadece kozmetik, bu yüzden bu tür geçici çözümleri kullanmak - bence - anlamsız.

Bu uyarılarla yaşamalısınız ya da onları bastırmalısınız.


1
Anlamsızlığa katılıyorum. Benim tahılıma aykırı olsa bile bastıracağım. Hepinize teşekkürler ve +1.
LSerni

2
> Bu uyarılarla yaşamalı veya onları bastırmalısınız. Yanlış olan uyarıları bastırmak her zaman daha iyidir ya da bastırılmamış yanlış uyarılardan oluşan bir spam içinde doğru uyarıyı kaçırabilirsiniz
SpongeBobFan

6

Sorunuzu cevaplamak için, bunu yapmanın "uygun bir yolu" yok. Şimdi sadece sizi rahatsız eden bir uyarı ise, çoğalmasını önlemenin en iyi yolu Query.list()yöntemi bir DAO'ya sarmaktır:

public class MyDAO {

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

Bu şekilde @SuppressWarnings("unchecked")yalnızca bir kez kullanabilirsiniz .


Stack Overflow'a hoş geldiniz ! Her neyse, turu
Sнаđошƒаӽ

3

Bunun benim için çalışmasının tek yolu bir Yineleyici kullanmaktı.

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);
    }

Bulduğum diğer yöntemlerle problemler yaşadım


Hangi "döküm sorunları"? Listeyi her zaman doğrudan yayınladım, yukarıdakiler nasıl daha kısa veya daha güvenli?
Giovanni Botta

Direkt olarak yayınlayamıyorum. Object to Destination
Popa Andrei

Hibernate'in Destinstionsizin için bir tane oluşturabileceğini biliyorsunuz değil mi? select newSözdizimini kullanarak . Bu kesinlikle doğru yaklaşım değil.
Örümcek Boris

Ben de aynı deneyimi yaşadım. Sorgum birbirine bağlı olmayan birden çok tablodan farklı alanlar döndürdüğünden. Yani benim için çalışmanın tek yolu bu oldu. Teşekkürler :)
Chintan Patel

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

Evet, bu temelde Boris'in önerdiği "çirkinlik" ile döngü içinde bir rol oynar.
LSerni

2

Bunun gibi bir ResultTransformer kullanırsınız:

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();
}

1
Bunu şimdi test edemem ama ... bu neyi değiştirecek? qhala bir Queryve bu nedenle q.list()hala ham bir java.util.Listtür. Oyuncu kadrosu daha sonra hala kontrol edilmemiştir; nesne türünün dahili olarak değiştirilmesi hiçbir
işe

Evet, yayın hala kontrol edilmemiştir, ancak alanlarınızın uygun şekilde adlandırılmasıyla, bir resultTransformer ayarlamak, Nesneleri istediğiniz POJO olarak dönüştürme işini yapar. Bu Bkz stackoverflow yazı ve bu okuma yerli sorguları kullanarak üzerinde hazırda referans doc
lakreqta

from foo where activeolduğu değil bir yerli sorgusu. Bu nedenle, varsayılan eşleme yeterli olacağından sonuç transformatörüne gerek yoktur. Soru, POJO alanlarının çevrimiyle ilgili değil, sonuç nesnesinin çevrimiyle ilgilidir. Bir sonuç transformatörü burada yardımcı olmaz.
Tobias Liefke

0

Doğru yol Hazırda Bekletme Transformatörlerini kullanmaktır:

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);

Nesnenin [] yinelenmesi gereksizdir ve bazı performans kesintilerine neden olabilir. Transofrmer kullanımıyla ilgili ayrıntılı bilgileri burada bulabilirsiniz: HQL ve SQL için Transformers

Daha da basit bir çözüm arıyorsanız, kullanıma hazır harita transformatörünü kullanabilirsiniz:

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");

Soru, sonuç dönüşümü ile ilgili değildi. QuerySonuçların dökümü ile ilgiliydi - bu, örneğinizde hala gerekli. Ve sizin örneğinizin orijinal ile hiçbir ilgisi yok from foo where active.
Tobias Liefke

0

Sadece Transformers kullanıyorum Benim için işe yaramadı Tip cast istisnası alıyordum.

sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class)) işe yaramadı çünkü dönüş listesi öğesinde sabit MYEngityName liste öğesi türü değil, Nesne Dizisi alıyordum.

Aşağıdaki değişiklikleri yaptığımda işe yaradı sqlQuery.addScalar(-)Her seçili sütunu ve türünü eklediğimde ve belirli String türü sütunu için türünü eşleştirmek zorunda kalmayız. sevmekaddScalar("langCode");

Ve sadece Sorguda yapamayacağımız NextEnity ile MYEngityName'e katıldım select *, geri dönüş listesinde Object dizisi verecek.

Kod örneğinin altında:

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();

Birine yardımı olabilir. bu şekilde benim için çalış.


-1

Burada en iyi çözümü buldum, bu sorunun anahtarı addEntity yöntemi

public static void testSimpleSQL() {
    final Session session = sessionFactory.openSession();
    SQLQuery q = session.createSQLQuery("select * from ENTITY");
    q.addEntity(Entity.class);
    List<Entity> entities = q.list();
    for (Entity entity : entities) {
        System.out.println(entity);
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.