JPA : 기본 쿼리 결과 세트를 POJO 클래스 콜렉션으로 변환하는 방법


174

프로젝트에서 JPA를 사용하고 있습니다.

5 개의 테이블에서 조인 작업을 수행 해야하는 쿼리에 왔습니다. 그래서 5 개의 필드를 반환하는 기본 쿼리를 만들었습니다.

이제 결과 객체를 동일한 5 개의 문자열이 포함 된 Java POJO 클래스로 변환하려고합니다.

JPA에서 결과를 POJO 객체 목록으로 직접 캐스트하는 방법이 있습니까 ??

나는 다음 해결책에왔다.

@NamedNativeQueries({  
    @NamedNativeQuery(  
        name = "nativeSQL",  
        query = "SELECT * FROM Actors",  
        resultClass = db.Actor.class),  
    @NamedNativeQuery(  
        name = "nativeSQL2",  
        query = "SELECT COUNT(*) FROM Actors",  
        resultClass = XXXXX) // <--------------- problem  
})  

이제 여기 resultClass에서 실제 JPA 엔터티 인 클래스를 제공해야합니까? 또는 동일한 열 이름을 포함하는 JAVA POJO 클래스로 변환 할 수 있습니까?


이 답변을 확인하십시오. 완전한 답변이 있습니다 : stackoverflow.com/a/50365522/3073945
Md. Sajedul Karim

그는 JPA를 사용하고,하지 봄

답변:


103

JPA는 SqlResultSetMapping네이티브 쿼리의 모든 반환 내용을 엔터티에 매핑 할 수있는또는 커스텀 클래스.

EDIT JPA 1.0은 비 엔티티 클래스로의 매핑을 허용하지 않습니다. JPA 2.1에서만 ConstructorResult 가 추가되어 Java 클래스의 리턴 값을 맵핑합니다.

또한 OP를 계산하는 데 문제가 있으면 단일 결과 집합 매핑을 정의하기에 충분해야합니다 ColumnResult


1
답장을 보내 주셔서 감사합니다. 여기서는 "@EntityResult"및 "@FieldResult"주석이있는 Java 엔티티 클래스가있는 엔티티와 결과를 맵핑합니다. 괜찮아. 그러나 여기에 더 명확성이 필요합니다. 결과와 매핑하는 클래스는 JPA 엔터티 클래스 여야합니까? 또는 필요한 모든 변수를 결과 세트의 열로 포함하는 엔티티 구매가 아닌 간단한 POJO 클래스를 사용할 수 있습니다.
Gunjan Shah

1
@ GunjanShah : 알 수있는 가장 좋은 방법은 시도해 보는 것입니다 :) 또한 엔티티는 주석과 함께 같은 포조입니다. 당신이 그것을 유지하려고하지 않는 한, 그것은 pojo를 유지합니다.
Denis Tulskiy

2
이것을 시도했을 때 클래스가 알려진 엔티티가 아니라는 오류가 발생했습니다. 기본 쿼리를 사용하는 대신 stackoverflow.com/questions/5024533/… 이 방법 을 사용했습니다.
FGreg

2
@ EdwinDalorzo : jpa 1.0에 맞습니다. jpa 2.1 에서는 생성자에 설정된 모든 필드와 함께 pojo를 사용할 수 있도록 ConstructorResult매개 변수 중 하나로 추가 했습니다 SqlResultSetMapping. 답변을 업데이트하겠습니다.
Denis Tulskiy

4
또 다른 쓴 진실을 봅니다. ConstructorResult는 POJO에 매핑 될 수 있습니다. 그러나 ConstructorResult 자체는 Entity 클래스에 있어야하므로 피할 수없는 Entity입니다. 따라서 더 큰 어려운 사실은 상관하지 않습니다 기본 키로-여전히 엔터티에 @Id가 있어야합니다 ... 건조한가?
Arnab Dutta

210

나는 이것에 대한 몇 가지 해결책을 발견했다.

매핑 된 엔터티 사용 (JPA 2.0)

JPA 2.0을 사용하면 기본 조회를 POJO에 맵핑 할 수 없으며 엔티티로만 수행 할 수 있습니다.

예를 들어 :

Query query = em.createNativeQuery("SELECT name,age FROM jedi_table", Jedi.class);
@SuppressWarnings("unchecked")
List<Jedi> items = (List<Jedi>) query.getResultList();

그러나이 경우, Jedi는 맵핑 된 엔티티 클래스 여야합니다.

여기서 확인되지 않은 경고를 피하기위한 대안은 명명 된 기본 쿼리를 사용하는 것입니다. 따라서 엔티티에서 기본 쿼리를 선언하면

@NamedNativeQuery(
 name="jedisQry", 
 query = "SELECT name,age FROM jedis_table", 
 resultClass = Jedi.class)

그런 다음 간단히 할 수 있습니다.

TypedQuery<Jedi> query = em.createNamedQuery("jedisQry", Jedi.class);
List<Jedi> items = query.getResultList();

이것은 더 안전하지만 여전히 매핑 된 엔터티를 사용하도록 제한되어 있습니다.

수동 매핑

내가 JPA 2.1이 도착하기 전에 약간의 실험을 한 솔루션은 약간의 리플렉션을 사용하여 POJO 생성자에 대한 매핑을 수행하고있었습니다.

public static <T> T map(Class<T> type, Object[] tuple){
   List<Class<?>> tupleTypes = new ArrayList<>();
   for(Object field : tuple){
      tupleTypes.add(field.getClass());
   }
   try {
      Constructor<T> ctor = type.getConstructor(tupleTypes.toArray(new Class<?>[tuple.length]));
      return ctor.newInstance(tuple);
   } catch (Exception e) {
      throw new RuntimeException(e);
   }
}

이 메소드는 기본적으로 튜플 배열 (네이티브 쿼리에 의해 리턴 됨)을 가져와 동일한 수의 필드와 동일한 유형의 생성자를 찾아 제공된 POJO 클래스에 맵핑합니다.

그런 다음 다음과 같은 편리한 방법을 사용할 수 있습니다.

public static <T> List<T> map(Class<T> type, List<Object[]> records){
   List<T> result = new LinkedList<>();
   for(Object[] record : records){
      result.add(map(type, record));
   }
   return result;
}

public static <T> List<T> getResultList(Query query, Class<T> type){
  @SuppressWarnings("unchecked")
  List<Object[]> records = query.getResultList();
  return map(type, records);
}

그리고 우리는이 기술을 다음과 같이 간단하게 사용할 수 있습니다 :

Query query = em.createNativeQuery("SELECT name,age FROM jedis_table");
List<Jedi> jedis = getResultList(query, Jedi.class);

@SqlResultSetMapping을 사용한 JPA 2.1

JPA 2.1이 출시되면 @SqlResultSetMapping 주석을 사용하여 문제를 해결할 수 있습니다.

엔티티 어딘가에 결과 세트 맵핑을 선언해야합니다.

@SqlResultSetMapping(name="JediResult", classes = {
    @ConstructorResult(targetClass = Jedi.class, 
    columns = {@ColumnResult(name="name"), @ColumnResult(name="age")})
})

그리고 우리는 단순히 :

Query query = em.createNativeQuery("SELECT name,age FROM jedis_table", "JediResult");
@SuppressWarnings("unchecked")
List<Jedi> samples = query.getResultList();

물론이 경우 Jedi매핑 된 엔터티 일 필요는 없습니다. 일반적인 POJO 일 수 있습니다.

XML 매핑 사용

나는 @SqlResultSetMapping엔티티 에이 모든 것을 침범하는 것을 발견 한 사람들 중 하나이며 특히 엔티티 내의 명명 된 쿼리의 정의를 싫어하므로 META-INF/orm.xml파일 에서이 모든 것을 수행 합니다.

<named-native-query name="GetAllJedi" result-set-mapping="JediMapping">
    <query>SELECT name,age FROM jedi_table</query>
</named-native-query>

<sql-result-set-mapping name="JediMapping">
        <constructor-result target-class="org.answer.model.Jedi">
            <column name="name" class="java.lang.String"/>
            <column name="age" class="java.lang.Integer"/>
        </constructor-result>
    </sql-result-set-mapping>

그리고 그것들은 내가 아는 모든 솔루션입니다. 마지막 두 가지는 JPA 2.1을 사용할 수있는 이상적인 방법입니다.


1
참고 사항 : JPA2.1 종속성과 함께 JPA 2.0 접근 방식을 사용했는데 실패했습니다. 아마 이것은 하향 호환이되지 않을 것입니다 ...
membersound September

1
"엔터티의 어딘가에"는 무엇을 의미합니까? 내 Pojo는 JPA가 아닙니다. POJO에서 @SqlResultSetMapping을 선언 할 수 없습니까? JPA 2.1 솔루션에 관심이 있습니다. 좀 더 정확하십시오.
Alboz

3
@Alboz @SqlResultSetMappingJPA가 메타 데이터를 읽는 것이므로 엔티티에 배치해야합니다. JPA가 POJO를 검사 할 것으로 기대할 수 없습니다. 매핑을 배치하는 엔터티는 관련이 없으며 아마도 POJO 결과와 더 관련이있는 엔터티입니다. 또는 완전히 관련이없는 엔터티와의 연결을 피하기 위해 매핑을 XML로 표현할 수 있습니다.
Edwin Dalorzo

1
생성자 결과에 중첩 클래스가있는 클래스를 사용할 수 있습니까?
chrismarx

5
JPA 2.1을 사용 @SqlResultSetMapping하면 Jedi클래스에 모든 인수 생성자 @ColumnResult가 필요하며 주석에는 type암시 적이 지 않을 수있는 변환에 속성이 추가되어야 할 수도 있습니다 ( type = ZonedDateTime.class일부 열에 추가해야 함 ).
Glenn

11

예, JPA 2.1을 사용하면 쉽습니다. 매우 유용한 주석이 있습니다. 그들은 당신의 인생을 단순화합니다.

먼저 고유 쿼리를 선언 한 다음 결과 세트 맵핑 (데이터베이스가 POJO에 리턴 한 데이터의 맵핑을 정의 함)을 맵핑하십시오. 참조 할 POJO 클래스를 작성하십시오 (간단하게 여기에 포함되지 않음). 마지막으로 : DAO에서 메소드를 작성하여 (예를 들어) 조회를 호출하십시오. 이것은 dropwizard (1.0.0) 앱에서 저에게 효과적이었습니다.

먼저 엔티티 클래스에서 기본 쿼리를 선언하십시오.

@NamedNativeQuery (
name = "domain.io.MyClass.myQuery",
query = "Select a.colA, a.colB from Table a",
resultSetMapping = "mappinMyNativeQuery")   // must be the same name as in the SqlResultSetMapping declaration

아래에 결과 집합 매핑 선언을 추가 할 수 있습니다.

@SqlResultSetMapping(
name = "mapppinNativeQuery",  // same as resultSetMapping above in NativeQuery
   classes = {
      @ConstructorResult( 
          targetClass = domain.io.MyMapping.class,
          columns = {
               @ColumnResult( name = "colA", type = Long.class),  
               @ColumnResult( name = "colB", type = String.class)
          }
      )
   } 
)

나중에 DAO에서 쿼리를 다음과 같이 참조 할 수 있습니다.

public List<domain.io.MyMapping> findAll() {
        return (namedQuery("domain.io.MyClass.myQuery").list());
    }

그게 다야.


좋은 대답이지만 첫 번째 @ColumnResult 주석 후 괄호를 놓친 것 같습니다.
mwatzer

코드에 실수가 있지만 쉽게 수정할 수 있습니다. 예 : "resulSetMapping ="은 "resultSetMapping ="
Zbyszek

3
또 다른 쓴 진실을 봅니다. NamedNativeQuery & SqlResultSetMapping은 @Entity 클래스에
있어야합니다

10

를 사용 Spring-jpa하면 답변과이 질문에 대한 보충 자료입니다. 결함이 있으면이를 수정하십시오. 나는 Object[]실질적인 요구를 충족시키기 위해 "포조에 매핑 결과"를 달성하기 위해 주로 세 가지 방법을 사용했다 .

  1. 내장 된 JPA이면 충분합니다.
  2. JPA 내장 메소드로는 충분하지 않지만 사용자 정의 sqlEntity충분합니다.
  3. 전자 2는 실패했으며을 사용해야합니다 nativeQuery. 예제는 다음과 같습니다. 예상되는 포조 :

    public class Antistealingdto {
    
        private String secretKey;
    
        private Integer successRate;
    
        // GETTERs AND SETTERs
    
        public Antistealingdto(String secretKey, Integer successRate) {
            this.secretKey = secretKey;
            this.successRate = successRate;
        }
    }

방법 1 : pojo를 인터페이스로 변경하십시오.

public interface Antistealingdto {
    String getSecretKey();
    Integer getSuccessRate();
}

그리고 저장소 :

interface AntiStealingRepository extends CrudRepository<Antistealing, Long> {
    Antistealingdto findById(Long id);
}

방법 2 : 리포지토리 :

@Query("select new AntistealingDTO(secretKey, successRate) from Antistealing where ....")
Antistealing whatevernamehere(conditions);

참고 : POJO 생성자의 매개 변수 순서는 POJO 정의와 sql에서 동일해야합니다.

방법 3 : 사용 @SqlResultSetMapping하고 @NamedNativeQuery있는 Entity에드윈 Dalorzo의 대답의 예로서.

처음 두 가지 방법은 사용자 정의 변환기와 같은 많은 중간 처리기를 호출합니다. 예를 들어, AntiStealing정의 secretKey가 유지되기 전에 컨버터이를 암호화하는 삽입된다. 이로 인해 처음 2 개의 메소드가 변환 된 결과를 반환합니다 secretKey. 방법 3은 변환기를 극복하고 리턴 secretKey된 것은 저장된 것과 동일합니다 (암호화 된 것).


방법 1은 실제로 Spring을 필요로하지 않으며 순수한 Hibernate와 함께 작동합니다.
Martin Vysny 2016 년

@MartinVysny 예, M1은 JPQL입니다. JPQL을 구현하는 모든 프로젝트는이를 지원해야합니다. 이런 식으로 M2도 널리 지원됩니까?
Tiina

8

비 엔티티 (Beans / POJO)에 결과를 지정하기 위해 랩핑 해제 프로 시저를 수행 할 수 있습니다. 절차는 다음과 같습니다.

List<JobDTO> dtoList = entityManager.createNativeQuery(sql)
        .setParameter("userId", userId)
        .unwrap(org.hibernate.Query.class).setResultTransformer(Transformers.aliasToBean(JobDTO.class)).list();

사용법은 JPA-Hibernate 구현에 사용됩니다.


JobDTO기본 생성자 가 있어야합니다. 또는 구현에 따라 자체 변압기를 구현할 수도 있습니다 AliasToBeanResultTransformer.
Lu55

4

먼저 다음 주석을 선언하십시오.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NativeQueryResultEntity {
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NativeQueryResultColumn {
    int index();
}

그런 다음 POJO에 다음과 같이 주석을 답니다.

@NativeQueryResultEntity
public class ClassX {
    @NativeQueryResultColumn(index=0)
    private String a;

    @NativeQueryResultColumn(index=1)
    private String b;
}

그런 다음 주석 프로세서를 작성하십시오.

public class NativeQueryResultsMapper {

    private static Logger log = LoggerFactory.getLogger(NativeQueryResultsMapper.class);

    public static <T> List<T> map(List<Object[]> objectArrayList, Class<T> genericType) {
        List<T> ret = new ArrayList<T>();
        List<Field> mappingFields = getNativeQueryResultColumnAnnotatedFields(genericType);
        try {
            for (Object[] objectArr : objectArrayList) {
                T t = genericType.newInstance();
                for (int i = 0; i < objectArr.length; i++) {
                    BeanUtils.setProperty(t, mappingFields.get(i).getName(), objectArr[i]);
                }
                ret.add(t);
            }
        } catch (InstantiationException ie) {
            log.debug("Cannot instantiate: ", ie);
            ret.clear();
        } catch (IllegalAccessException iae) {
            log.debug("Illegal access: ", iae);
            ret.clear();
        } catch (InvocationTargetException ite) {
            log.debug("Cannot invoke method: ", ite);
            ret.clear();
        }
        return ret;
    }

    // Get ordered list of fields
    private static <T> List<Field> getNativeQueryResultColumnAnnotatedFields(Class<T> genericType) {
        Field[] fields = genericType.getDeclaredFields();
        List<Field> orderedFields = Arrays.asList(new Field[fields.length]);
        for (int i = 0; i < fields.length; i++) {
            if (fields[i].isAnnotationPresent(NativeQueryResultColumn.class)) {
                NativeQueryResultColumn nqrc = fields[i].getAnnotation(NativeQueryResultColumn.class);
                orderedFields.set(nqrc.index(), fields[i]);
            }
        }
        return orderedFields;
    }
}

다음과 같이 위의 프레임 워크를 사용하십시오.

String sql = "select a,b from x order by a";
Query q = entityManager.createNativeQuery(sql);

List<ClassX> results = NativeQueryResultsMapper.map(q.getResultList(), ClassX.class);

어떤 패키지에 BeanUtils들어 있습니까?
Harish

4

가장 쉬운 방법은 투영법 을 사용하는 것 입니다. 쿼리 결과를 인터페이스에 직접 매핑 할 수 있으며 SqlResultSetMapping을 사용하는 것보다 구현하기가 더 쉽습니다.

예는 다음과 같습니다.

@Repository
public interface PeopleRepository extends JpaRepository<People, Long> {

    @Query(value = "SELECT p.name AS name, COUNT(dp.people_id) AS count " +
        "FROM people p INNER JOIN dream_people dp " +
        "ON p.id = dp.people_id " +
        "WHERE p.user_id = :userId " +
        "GROUP BY dp.people_id " +
        "ORDER BY p.name", nativeQuery = true)
    List<PeopleDTO> findByPeopleAndCountByUserId(@Param("userId") Long userId);

    @Query(value = "SELECT p.name AS name, COUNT(dp.people_id) AS count " +
        "FROM people p INNER JOIN dream_people dp " +
        "ON p.id = dp.people_id " +
        "WHERE p.user_id = :userId " +
        "GROUP BY dp.people_id " +
        "ORDER BY p.name", nativeQuery = true)
    Page<PeopleDTO> findByPeopleAndCountByUserId(@Param("userId") Long userId, Pageable pageable);

}



// Interface to which result is projected
public interface PeopleDTO {

    String getName();

    Long getCount();

}

투영 된 인터페이스의 필드는이 엔티티의 필드와 일치해야합니다. 그렇지 않으면 필드 매핑이 중단 될 수 있습니다.

또한 SELECT table.column표기법 을 사용하는 경우 항상 예제와 같이 엔티티의 이름과 일치하는 별명을 정의하십시오.


1
기본 쿼리와 프로젝션은 잘 어울리지 않습니다.
Kevin Rave

1
필드 매핑을 제대로 작동시킬 수 없었습니다. 대부분의 값은 계속 null로 돌아 왔습니다
ayang

4

최대 절전 모드에서는이 코드를 사용하여 기본 쿼리를 쉽게 매핑 할 수 있습니다.

private List < Map < String, Object >> getNativeQueryResultInMap() {
String mapQueryStr = "SELECT * FROM AB_SERVICE three ";
Query query = em.createNativeQuery(mapQueryStr);
NativeQueryImpl nativeQuery = (NativeQueryImpl) query;
nativeQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List < Map < String, Object >> result = query.getResultList();
for (Map map: result) {
    System.out.println("after request  ::: " + map);
}
return result;}

2

최대 절전 모드 사용 :

@Transactional(readOnly=true)
public void accessUser() {
EntityManager em = repo.getEntityManager();
    org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
    org.hibernate.SQLQuery q = (org.hibernate.SQLQuery) session.createSQLQuery("SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u").addScalar("username", StringType.INSTANCE).addScalar("name", StringType.INSTANCE).addScalar("email", StringType.INSTANCE).addScalar("passe", StringType.INSTANCE).addScalar("loginType", IntegerType.INSTANCE)
        .setResultTransformer(Transformers.aliasToBean(User2DTO.class));

    List<User2DTO> userList = q.list();
}

2

ResultSet을 사용하는 이전 스타일

@Transactional(readOnly=true)
public void accessUser() {
    EntityManager em = this.getEntityManager();
    org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
    session.doWork(new Work() {
        @Override
        public void execute(Connection con) throws SQLException {
            try (PreparedStatement stmt = con.prepareStatement(
                    "SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u")) {
                ResultSet rs = stmt.executeQuery();
                ResultSetMetaData rsmd = rs.getMetaData();
                for (int i = 1; i <= rsmd.getColumnCount(); i++) {
                    System.out.print(rsmd.getColumnName(i) + " (" + rsmd.getColumnTypeName(i) + ") / ");
                }
                System.out.println("");
                while (rs.next()) {
                    System.out.println("Found username " + rs.getString("USERNAME") + " name " + rs.getString("NAME") + " email " + rs.getString("EMAIL") + " passe " + rs.getString("PASSE") + " email " + rs.getInt("LOGIN_TYPE"));
                }
            }
        }
    });
}

1

다른 사람들이 이미 가능한 모든 솔루션을 언급 했으므로 해결 방법을 공유하고 있습니다.

내 상황에와 Postgres 9.4함께 작업하는 동안 Jackson,

//Convert it to named native query.
List<String> list = em.createNativeQuery("select cast(array_to_json(array_agg(row_to_json(a))) as text) from myschema.actors a")
                   .getResultList();

List<ActorProxy> map = new ObjectMapper().readValue(list.get(0), new TypeReference<List<ActorProxy>>() {});

다른 데이터베이스에서도 동일하게 찾을 수 있습니다.

또한 참고로 JPA 2.0 기본 쿼리 결과


1

이것이 여기에 맞는지 확실하지 않지만 비슷한 질문이 있었고 다음과 같은 간단한 해결책 / 예를 발견했습니다.

private EntityManager entityManager;
...
    final String sql = " SELECT * FROM STORE "; // select from the table STORE
    final Query sqlQuery = entityManager.createNativeQuery(sql, Store.class);

    @SuppressWarnings("unchecked")
    List<Store> results = (List<Store>) sqlQuery.getResultList();

필자의 경우 Strings에 정의 된 SQL 부분을 다른 곳에서 사용해야했기 때문에 NamedNativeQuery 만 사용할 수 없었습니다.


엔티티를 반환하자마자 공상이 아닙니다. 문제는 결과를 관리되지 않는 POJO에 매핑하려고 할 때입니다.
Olgun Kaya

1

Resultset을 사용한 구식

@Transactional(readOnly=true)
public void accessUser() {
    EntityManager em = this.getEntityManager();
    org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
    session.doWork(new Work() {
        @Override
        public void execute(Connection con) throws SQLException {
            try (PreparedStatement stmt = con.prepareStatement(
                    "SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u")) {
                ResultSet rs = stmt.executeQuery();
                ResultSetMetaData rsmd = rs.getMetaData();
                for (int i = 1; i <= rsmd.getColumnCount(); i++) {
                    System.out.print(rsmd.getColumnName(i) + " (" + rsmd.getColumnTypeName(i) + ") / ");
                }
                System.out.println("");
                while (rs.next()) {
                    System.out.println("Found username " + rs.getString("USERNAME") + " name " + rs.getString("NAME") + " email " + rs.getString("EMAIL") + " passe " + rs.getString("PASSE") + " email " + rs.getInt("LOGIN_TYPE"));
                }
            }
        }
    });
}

1

다음과 같은 방법으로 문제를 해결했습니다.

   //Add actual table name here in Query
    final String sqlQuery = "Select a.* from ACTORS a"
    // add your entity manager here 
    Query query = entityManager.createNativeQuery(sqlQuery,Actors.class);
    //List contains the mapped entity data.
    List<Actors> list = (List<Actors>) query.getResultList();

0

복잡한 SqlResultSetMapping을 사용하지 않고 원시 쿼리에서 결과를 검색하기 위해 POJO를 의사 엔티티로 사용하려면 아래 예를 참조하십시오. POJO에 두 개의 주석, 즉 @Enity와 dummy @Id가 필요합니다. @Id는 선택한 모든 필드에서 사용할 수 있으며 @Id 필드는 중복 키를 가질 수 있지만 null 값은 가질 수 없습니다.

@Enity는 실제 테이블에 맵핑되지 않으므로이 POJO를 의사 엔티티라고합니다.

환경 : eclipselink 2.5.0-RC1, jpa-2.1.0, mysql-connector-java-5.1.14

완전한 maven 프로젝트를 여기에서 다운로드 할 수 있습니다

기본 쿼리는 mysql 샘플 직원 db http://dev.mysql.com/doc/employee/en/employees-installation.html을 기반으로합니다.

persistence.xml

<?xml version="1.0" encoding="UTF-8"?><persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.1" 
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="jpa-mysql" transaction-type="RESOURCE_LOCAL">
    <class>org.moonwave.jpa.model.pojo.Employee</class>
    <properties>
        <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/employees" />
        <property name="javax.persistence.jdbc.user" value="user" />
        <property name="javax.persistence.jdbc.password" value="***" />
        <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
    </properties>
</persistence-unit>

Employee.java

package org.moonwave.jpa.model.pojo;

@Entity
public class Employee {

@Id
protected Long empNo;

protected String firstName;
protected String lastName;
protected String title;

public Long getEmpNo() {
    return empNo;
}
public void setEmpNo(Long empNo) {
    this.empNo = empNo;
}
public String getFirstName() {
    return firstName;
}
public void setFirstName(String firstName) {
    this.firstName = firstName;
}
public String getLastName() {
    return lastName;
}
public void setLastName(String lastName) {
    this.lastName = lastName;
}   
public String getTitle() {
    return title;
}
public void setTitle(String title) {
    this.title = title;
}
public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("empNo: ").append(empNo);
    sb.append(", firstName: ").append(firstName);
    sb.append(", lastName: ").append(lastName);
    sb.append(", title: ").append(title);
    return sb.toString();
}
}

EmployeeNativeQuery.java

public class EmployeeNativeQuery {
private EntityManager em;
private EntityManagerFactory emf;

public void setUp() throws Exception {
    emf=Persistence.createEntityManagerFactory("jpa-mysql");
    em=emf.createEntityManager();
}
public void tearDown()throws Exception {
    em.close();
    emf.close();
}

@SuppressWarnings("unchecked")
public void query() {
    Query query = em.createNativeQuery("select e.emp_no as empNo, e.first_name as firstName, e.last_name as lastName," + 
            "t.title from employees e join titles t on e.emp_no = t.emp_no", Employee.class);
    query.setMaxResults(30);
    List<Employee> list = (List<Employee>) query.getResultList();
    int i = 0;
    for (Object emp : list) {
        System.out.println(++i + ": " + emp.toString());
    }
}

public static void main( String[] args ) {
    EmployeeNativeQuery test = new EmployeeNativeQuery();
    try {
        test.setUp();
        test.query();
        test.tearDown();
    } catch (Exception e) {
        System.out.println(e);
    }
}
}

1
당신의 list주장에 따르면, Employee왜 for-each 루프가 타입을 반복 Object하는가? for-each 루프 for(Employee emp : list)를 작성하면 대답이 잘못되었고 목록의 내용이 직원이 아니며 억제 한 경고에이 잠재적 실수에 대해 경고 할 목적이 있음을 알게됩니다.
Edwin Dalorzo

@SuppressWarnings ( "unchecked")는 List<Employee> list = (List<Employee>) query.getResultList();Change for (Object emp : list)to for (Employee emp : list)better에 대한 경고를 억제하는 데 사용 되지만 Object emplist 가의 인스턴스이므로 유지되는 경우 오류가 없습니다 List<Employee>. 나는 git 프로젝트에서 코드를 변경했지만 귀하의 의견은 원래 게시물과 관련이 있도록 여기에 없습니다
Jonathan L

문제는 쿼리가 고용자 목록을 반환하지 않고 개체 배열을 반환한다는 것입니다. 당신의 억제 된 경고는 그것을 숨기고 있습니다. 그 중 하나를 직원으로 변환하려고하는 순간, 캐스트 예외 인 오류가 발생합니다.
Edwin Dalorzo

Query query = em.createNativeQuery("select * ...", Employee.class);그리고 persistence.xml을 보십시오 . 기본 조회는 Employee의 목록을 리턴합니다. 방금 체크 아웃하고 문제없이 프로젝트를 실행했습니다. mysql 샘플 직원 db를 로컬로 설정하면 프로젝트도 실행할 수 있어야합니다.
Jonathan L

아 지금 무슨 말인지 알 겠어 그러나이 경우 귀하의 답변은 질문을 만족시키지 못합니다. 이것은 대상 객체로 일반 POJO를 사용하는 것이었고 귀하의 답변은 Employee내가 엔티티라고 가정 하고 있기 때문 입니다. 그렇지 않습니까?
Edwin Dalorzo

0

Spring을 사용하는 경우 사용할 수 있습니다 org.springframework.jdbc.core.RowMapper

예를 들면 다음과 같습니다.

public List query(String objectType, String namedQuery)
{
  String rowMapper = objectType + "RowMapper";
  // then by reflection you can instantiate and use. The RowMapper classes need to follow the naming specific convention to follow such implementation.
} 

0

최대 절전 모드 사용 :

@Transactional(readOnly=true)
public void accessUser() {
    EntityManager em = repo.getEntityManager();
    org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
    org.hibernate.SQLQuery q = (org.hibernate.SQLQuery) session.createSQLQuery("SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u")
        .addScalar("username", StringType.INSTANCE).addScalar("name", StringType.INSTANCE)
        .addScalar("email", StringType.INSTANCE).addScalar("passe", StringType.INSTANCE)
        .addScalar("loginType", IntegerType.INSTANCE)
        .setResultTransformer(Transformers.aliasToBean(User2DTO.class));

    List<User2DTO> userList = q.list();
}

-1

SQL 쿼리를 POJO 클래스 컬렉션으로 변환하는 간단한 방법,

Query query = getCurrentSession().createSQLQuery(sqlQuery).addEntity(Actors.class);
List<Actors> list = (List<Actors>) query.list();
return list;

-1

생성자가 포함 된 DTO 만 있으면됩니다.

public class User2DTO implements Serializable {

    /** pode ser email ou id do Google comecando com G ou F para Facebook */
    private String username;

    private String password;

    private String email;

    private String name;

    private Integer loginType;

    public User2DTO(Object...fields) {
        super();
        this.username = (String) fields[0];
        this.name = (String) fields[1];
        this.email = (String) fields[2];
        this.password = (String) fields[3];
        this.loginType = (Integer) fields[4];
    }

그리고 그것을 부르십시오 :

EntityManager em = repo.getEntityManager();
        Query q = em.createNativeQuery("SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u");
        List<Object[]> objList = q.getResultList();
        List<User2DTO> ooBj = objList.stream().map(User2DTO::new).collect(Collectors.toList());

새 열을 추가하면 코드가 중단됩니다.
접시

-2

사용하십시오 DTO Design Pattern. 에서 사용되었습니다 EJB 2.0. 엔티티는 컨테이너 관리되었습니다. DTO Design Pattern이 문제를 해결하는 데 사용됩니다. 그러나 응용 프로그램 Server SideClient Side별도로 개발할 때 현재 사용 중일 수 있습니다 . 을 주석으로 전달 / 반환하지 않으려 DTO는 경우에 사용됩니다 .Server sideEntityClient Side

DTO 예 :

PersonEntity.java

@Entity
public class PersonEntity {
    @Id
    private String id;
    private String address;

    public PersonEntity(){

    }
    public PersonEntity(String id, String address) {
        this.id = id;
        this.address = address;
    }
    //getter and setter

}

PersonDTO.java

public class PersonDTO {
    private String id;
    private String address;

    public PersonDTO() {
    }
    public PersonDTO(String id, String address) {
        this.id = id;
        this.address = address;
    }

    //getter and setter 
}

DTOBuilder.java

public class DTOBuilder() {
    public static PersonDTO buildPersonDTO(PersonEntity person) {
        return new PersonDTO(person.getId(). person.getAddress());
    }
}

EntityBuilder.java <-필요가있다

public class EntityBuilder() {
    public static PersonEntity buildPersonEntity(PersonDTO person) {
        return new PersonEntity(person.getId(). person.getAddress());
    }
}

4
답변 주셔서 감사합니다. 여기에 DTO 패턴이 필요하지 않습니다. 내 요구 사항은 클라이언트에서 주석 세부 정보를 숨기지 않아야합니다. 따라서 내 앱에서 POJO를 하나 더 만들 필요가 없습니다. 내 요구 사항은 JAVA 엔터티가 아닌 결과 집합 열과 동일한 필드를 가진 간단한 POJO 클래스 인 qa pojo로 결과 집합을 캐스팅하는 것입니다.
Gunjan Shah
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.