Jestem nowicjuszem w Hibernate i piszę prostą metodę zwracania listy obiektów pasujących do określonego filtra. List<Foo>
wydawało się naturalnym typem powrotu.
Cokolwiek robię, nie mogę uszczęśliwić kompilatora, chyba że użyję brzydkiego @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();
}
}
Chciałbym się tego pozbyćSuppressWarnings
. Ale jeśli to zrobię, otrzymam ostrzeżenie
Warning: Unchecked cast from List to List<Foo>
(Mogę to zignorować, ale w pierwszej kolejności nie chciałbym tego dostać), a jeśli usunę rodzaj ogólny, aby dostosować się do .list()
zwracanego typu, otrzymuję ostrzeżenie
Warning: List is a raw type. References to generic type List<E>
should be parameterized.
Zauważyłem, że org.hibernate.mapping
ma zadeklarować List
; ale jest to zupełnie inny typ - Query
zwraca a java.util.List
, jako typ surowy. Wydaje mi się dziwne, że niedawny Hibernate (4.0.x) nie zaimplementował sparametryzowanych typów, więc podejrzewam, że to ja zamiast tego robię coś złego.
Wygląda to bardzo podobnie do wyniku Cast Hibernate na liście obiektów , ale tutaj nie mam „twardych” błędów (system zna typ Foo i nie używam zapytania SQLQuery, ale prostego zapytania). Więc nie ma radości.
Przyjrzałem się również Wyjątkowi rzutowania klasy Hibernate, ponieważ wyglądał obiecująco, ale potem zdałem sobie sprawę, że tak naprawdę nie dostaję żadnego Exception
... moim problemem jest tylko ostrzeżenie - styl kodowania, jeśli wolisz.
Dokumentacja na jboss.org, podręczniki Hibernate i kilka samouczków nie wydają się opisywać tego tematu w tak szczegółowy sposób (czy nie szukałem w odpowiednich miejscach?). Kiedy wchodzą w szczegóły, używają castingu w locie - i to w samouczkach, których nie było na oficjalnej stronie jboss.org, więc jestem trochę ostrożny.
Po skompilowaniu kod działa bez widocznego problemu… o którym wiem… jeszcze; a rezultaty są oczekiwane.
Więc: czy robię to dobrze? Czy brakuje mi czegoś oczywistego? Czy istnieje „oficjalny” lub „zalecany” sposób na zrobienie tego ?
final
Class<?>
wlist()
, problem może zostać rozwiązany. Szkoda używać tak brzydkiego API.List<Object>
. Wyniki powinny być rzutowane na oczekiwany typ i należy dodać testy jednostkowe, aby upewnić się, że zapytanie zwróci prawidłowe wyniki. Niedopuszczalne jest występowanie błędów w zapytaniach pojawiających się „w dalszej części kodu ”. Twój przykład jest argumentem przeciwko praktykom kodowania, które powinny być przekleństwem w XXI wieku. To, chciałbym zaproponować, nie do przyjęcia, aby miećList<Object>
.Rozdzielczość polega na użyciu zamiast tego TypedQuery. Podczas tworzenia zapytania z EntityManager zamiast tego wywołaj je w ten sposób:
TypedQuery<[YourClass]> query = entityManager.createQuery("[your sql]", [YourClass].class); List<[YourClass]> list = query.getResultList(); //no type warning
Działa to tak samo w przypadku nazwanych zapytań, natywnych nazwanych zapytań itp. Odpowiednie metody mają takie same nazwy, jak te, które zwracają zapytanie proste. Po prostu użyj tego zamiast zapytania zawsze, gdy znasz typ zwracanej wartości.
źródło
Możesz uniknąć ostrzeżenia kompilatora, stosując takie obejścia:
List<?> resultRaw = query.list(); List<MyObj> result = new ArrayList<MyObj>(resultRaw.size()); for (Object o : resultRaw) { result.add((MyObj) o); }
Ale są pewne problemy z tym kodem:
A różnica jest tylko kosmetyczna, więc stosowanie takich obejść jest - moim zdaniem - bezcelowe.
Musisz żyć z tymi ostrzeżeniami lub je stłumić.
źródło
Odpowiadając na twoje pytanie, nie ma „właściwego sposobu”, aby to zrobić. Jeśli przeszkadza Ci tylko ostrzeżenie, najlepszym sposobem uniknięcia jego rozprzestrzeniania się jest zawinięcie
Query.list()
metody w DAO:public class MyDAO { @SuppressWarnings("unchecked") public static <T> List<T> list(Query q){ return q.list(); } }
W ten sposób możesz użyć
@SuppressWarnings("unchecked")
tylko raz.źródło
Jedynym sposobem, w jaki pracowałem dla mnie, był Iterator.
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); }
W przypadku innych metod, które znalazłem, miałem problemy z rzutami
źródło
Destinstion
dla Ciebie, prawda? Korzystanie zeselect new
składni. To z pewnością nie jest właściwe podejście.List<Person> list = new ArrayList<Person>(); Criteria criteria = this.getSessionFactory().getCurrentSession().createCriteria(Person.class); for (final Object o : criteria.list()) { list.add((Person) o); }
źródło
Używasz ResultTransformer w ten sposób:
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(); }
źródło
q
jest nadalQuery
i dlategoq.list()
nadal jest surowymjava.util.List
typem. Obsada jest wtedy nadal niesprawdzona; zmiana typu obiektu wewnętrznie nic niefrom foo where active
to nie native zapytania. Nie ma więc potrzeby stosowania transformatora wynikowego, ponieważ wystarczy domyślne mapowanie. Nie chodzi o rzutowanie pól POJO, ale o rzutowanie obiektu wynikowego. Transformator wynikowy nie pomógłby tutaj.Prawidłowym sposobem jest użycie transformatorów hibernacji:
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);
Iterowanie przez obiekt [] jest zbędne i może mieć pewien spadek wydajności. Szczegółowe informacje na temat wykorzystania transofrmerów znajdziesz tutaj: Transformers for HQL i SQL
Jeśli szukasz jeszcze prostszego rozwiązania, możesz skorzystać z prostego transformatora mapy:
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");
źródło
Query
wyników - co jest nadal potrzebne w twoim przykładzie. Twój przykład nie ma nic wspólnego z oryginałemfrom foo where active
.Tylko używanie Transformers To nie zadziałało. Otrzymałem wyjątek rzutowania typu.
sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class))
nie zadziałało, ponieważ otrzymywałem Array of Object w elemencie listy zwrotów, a nie ustalony typ MYEngityName elementu listy.Udało mi się to, gdy wprowadzam następujące zmiany. Po dodaniu
sqlQuery.addScalar(-)
każdej wybranej kolumny i jej typu oraz dla określonej kolumny typu String nie musimy mapować jej typu. lubićaddScalar("langCode");
I połączyłem MYEngityName z NextEnity, którego nie możemy tylko
select *
w zapytaniu poda tablicę Object na liście zwrotów.Poniżej przykładowy kod:
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();
To może komuś pomóc. w ten sposób działa dla mnie.
źródło
Tutaj znalazłem najlepsze rozwiązanie , kluczem do tego problemu jest metoda addEntity
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); } }
źródło