ProgramingTip

Hibernate Query.list ()를 List로 캐스팅하는“적절한”방법은 무엇입니까?

bestdevel 2020. 10. 12. 07:55
반응형

Hibernate Query.list ()를 List로 캐스팅하는“적절한”방법은 무엇입니까??


저는 Hibernate의 다음과 같은 특정 필터와 일치하는 개체 목록을 반환하는 간단한 방법을 작성하고 있습니다. List<Foo>자연스러운 유형 반환처럼 보였습니다.

내가 무엇을하든 추악한 .NET을 사용하지 않는 한 컴파일러를 행복하게 만들 수 없습니다 @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.mapping 하지 를 선언 List; 그러나 그것은 완전히 다른 유형입니다- 원시 유형으로 Query반환 java.util.List됩니다. 최근 Hibernate (4.0.x)가 매개 변수화 된 유형을 구현하지 않는 것이 이상하다는 것을 알기 때문에 대신 내가 뭔가 잘못하고 있다는 생각합니다.

Cast Hibernate 결과 와 매우 유사 하지만 여기에는 "하드"오류가 없습니다 (시스템은 Foo 유형을 알고있는 SQLQuery가 아니라는 쿼리를 사용하고 있습니다). 그래서 기쁨이 없습니다.

또한 나는 유망 해 보였기 때문에 최대 절전 모드 클래스 캐스트 예외 을 보았지만 실제로 아무것도 얻지 못한다것을 깨달았습니다 Exception... 내 문제는 경고의 문제입니다 - 당신이 원한다면 코딩 스타일.

jboss.org에 대한 문서, Hibernate 매뉴얼 및 검색 튜토리얼이 그 주제그렇게 자세하게 다루지 않는 것 (또는 올바른 위치에서 발생하지 않습니까?). 세부 사항을 입력 할 때, 그들이 즉석 캐스팅을 사용합니다. 그리고 이것은 공식 jboss.org 사이트에 없었던 튜토리얼에 대한 것 약간 조심합니다.

일단 실행 된 코드는 명백한 문제 없이 실행됩니다 ... 내가 아는 ... 아직; 결과는 예상되는 것입니다.

그래서 :이 일을 제대로하고 있습니까? 나는 명백한 것을 놓치고 있습니까? "공식"또는 "권장" 방법이 있습니까?


짧은 대답 올바른 @SuppressWarnings방법입니다.

긴 대답, Hibernate는 메서드 List에서 원시 반환합니다 . 여기를Query.list 참조 하십시오 . 이것은 Hibernate의 버그 나 수있는 것이 아닙니다 . 에 의해 반환 쿼리 된 유형 은 컴파일 타임에 알려지지 않습니다 .

따라서 당신이 쓸 때

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

에서 List안전하지 않은 캐스트를 하고 List<MyObject>있습니다. 피할 수 없습니다.

무엇이든 포함 List 할 수 있으므로 캐스트를 수행 할 수있는 방법은 없습니다 .

오류를 없애는 유일한 방법은

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

해결 방법은 대신 TypedQuery를 사용하는 것입니다. EntityManager에서 쿼리를 만들 때 대신 다음과 같이 호출합니다.

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

이것은 또한 명명 된 쿼리, 동일하게 작동합니다. 해당 메소드는 바닐라 쿼리를 반환하는 것과 동일한 이름을 갖습니다. 반환 유형을 알 때마다 쿼리 대신 사용하십시오.


다음과 같은 해결 방법으로 컴파일러 경고를 피할 수 있습니다.

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

그러나이 코드에는 몇 가지 문제가 있습니다.

  • 불필요한 ArrayList 생성
  • 쿼리에서 반환 된 모든 요소에 대한 불필요한 루프
  • 더 긴 코드.

그리고 그 차이는 외형 일 뿐이라고 발표하는 방법을 사용하는 것을 의미합니다.

당신은 단지 경고와 함께 살거나해야합니다.


나를 위해 일하는 유일한 방법은 반복적으로 사용하는 것입니다.

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

내가 많은 방법으로 캐스트 문제가.


귀하의 질문에 답하기위한 "올바른 방법"은 없습니다. 이제 당신을 괴롭히는 경고 일 뿐이라면 경쟁을 피하는 가장 좋은 방법은 방법을 Query.list()DAO로 래핑하는 것입니다.

public class MyDAO {

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

이렇게하면 @SuppressWarnings("unchecked")한 번만 사용할 수 있습니다 .


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

다음과 같은 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();
}

적절한 방법은 Hibernate Transformer를 사용하는 것입니다.

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

단지 Transformers를 사용하는 것 입니다. 유형 캐스트 ​​예외가 발생했습니다.

sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class)) 목록 요소의 고정 MYEngityName 유형이 아닌 반환 목록 요소에서 객체 배열을 얻었 기 때문에 작동하지 않았습니다.

sqlQuery.addScalar(-)선택한 각 열과 해당 유형을 추가하고 특정 문자열 유형 열에 대해 해당 유형을 매핑 할 필요가 없을 때 다음과 같이 변경하면 저에게 효과적이었습니다. 처럼addScalar("langCode");

그리고 MYEngityName을 NextEnity와 조인 select *했습니다. Query에서 반환 목록에 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();

도움이 될 수 있습니다. 이런 식으로 나를 위해 일합니다.


여기서 가장 좋은 해결책을 찾았습니다. 이 문제의 핵심은 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);
    }
}

참고 URL : https://stackoverflow.com/questions/15913150/what-is-the-proper-way-to-cast-hibernate-query-list-to-listtype

반응형