JPA: Как преобразовать собственный набор результатов запроса в коллекцию классов POJO

174

Я использую JPA в своем проекте.

Я пришел к запросу, в котором мне нужно сделать операцию соединения на пяти таблицах. Поэтому я создал собственный запрос, который возвращает пять полей.

Теперь я хочу преобразовать полученный объект в 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 который позволяет отображать любые результаты из вашего собственного запроса в сущностьили пользовательский класс,

РЕДАКТИРОВАТЬ JPA 1.0 не разрешает сопоставление с не-сущностными классами. Только в JPA 2.1 ConstructorResult был добавлен к возвращаемым значениям карты java-класса.

Кроме того, для проблемы OP с получением количества должно быть достаточно, чтобы определить отображение набора результатов с одним ColumnResult

Денис Тульский
источник
1
Спасибо за ответ. Здесь мы сопоставляем наш результат с сущностью с помощью класса сущности Java с аннотациями «@EntityResult» и «@FieldResult». Это хорошо. Но здесь мне нужно больше ясности. Требуется ли, чтобы класс, который мы отображаем с результатом, был классом сущности JPA? ИЛИ мы можем использовать простой класс POJO, который не является покупкой сущности, который имеет все необходимые переменные в качестве столбцов в наборе результатов.
Гунджан Шах
1
@GunjanShah: лучший способ узнать это попробовать :) также, сущность - это то же самое pojo, только с некоторыми аннотациями. до тех пор, пока вы не будете пытаться упорствовать, он останется пижо.
Денис Тульский
2
Когда я попробовал это, я получил ошибку, что класс не был известным объектом. В итоге я использовал этот подход stackoverflow.com/questions/5024533/… вместо того, чтобы пытаться использовать собственный запрос.
FGreg
2
@ EdwinDalorzo: это верно для jpa 1.0. в jpa 2.1 они добавлены ConstructorResultкак один из параметров, SqlResultSetMappingкоторый позволяет использовать pojo со всеми полями, установленными в конструкторе. Я обновлю ответ.
Денис Тульский
4
Я вижу еще одну горькую правду: ConstructorResult может отображаться в POJO. НО ConstructorResult должен быть в классе Entity, чтобы Entity вы не могли избежать ... и, следовательно, более сложный факт: вам нужен какой-то результат с безразличным к первичному ключу - все равно у вас должен быть @Id в Entity ... смешно, верно?
Арнаб Датта
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);

JPA 2.1 с @SqlResultSetMapping

С появлением 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 Mapping

Я один из тех, кто находит добавление всех этих @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
Sidenote: Я только что использовал подход JPA 2.0 с зависимостью JPA2.1, и он потерпел неудачу. Так что, вероятно, это не совместимо
сверху
1
что вы подразумеваете под "где-то в сущности"? Мой Pojo не является организацией JPA, не могу ли я объявить @SqlResultSetMapping в моем POJO? Я заинтересован в решениях JPA 2.1. Пожалуйста, будьте немного точнее.
Альбоз
3
@Alboz @SqlResultSetMappingДолжен быть помещен в сущность, потому что именно из этого JPA будет читать метаданные. Вы не можете ожидать, что JPA будет проверять ваши POJO. Объект, в который вы помещаете отображение, не имеет значения, возможно, тот, который больше связан с вашими результатами POJO. В качестве альтернативы отображение может быть выражено в XML, чтобы избежать связи с совершенно не связанной сущностью.
Эдвин Далорсо
1
Возможно ли для конструктора результата использовать класс, который имеет вложенный класс?
Крисмаркс
5
Если вы используете JPA 2.1 вместе с @SqlResultSetMappingним, то, возможно, стоит отметить, что Jediклассу потребуется конструктор, содержащий все аргументы, и @ColumnResultаннотации может потребоваться typeдобавить атрибут к преобразованиям, который может быть неявным (мне нужно было добавить type = ZonedDateTime.classнекоторые столбцы).
Гленн
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());
    }

Вот и все.

GiulioDS
источник
Хороший ответ, но я думаю, что вы пропустили скобки после первой аннотации @ColumnResult.
mwatzer
В коде есть ошибки, но их легко исправить. Например: "resulSetMapping =" должно быть "resultSetMapping ="
Zbyszek
3
Я вижу еще одну горькую правду: NamedNativeQuery & SqlResultSetMapping должен быть в классе @Entity
Арнаб Датта
10

Если вы используете Spring-jpa, это дополнение к ответам и этот вопрос. Пожалуйста, исправьте это, если есть какие-либо недостатки. В основном я использовал три метода для достижения «результата сопоставления Object[]с pojo», основываясь на том, что я на практике понимаю:

  1. JPA встроенный метод достаточно.
  2. Встроенного метода JPA недостаточно, но достаточно настроить sqlего Entity.
  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качестве примера в ответе Эдвина Далорсо.

Первые два метода вызвали бы многие промежуточные обработчики, такие как настраиваемые преобразователи. Например, AntiStealingопределяет secretKey, прежде чем он будет сохранен, вставляется конвертер для его шифрования. Это приведет к тому, что первые 2 метода вернут конвертированный обратно, secretKeyчто не то, что я хочу. Хотя способ 3 будет преодолевать конвертер, и возвращаемый secretKeyбудет таким же, как он хранится (зашифрованный).

Тийна
источник
Метод 1 на самом деле не требует Spring и работает с чистым Hibernate.
Мартин Высний
@MartinVysny да, M1 - это JPQL. любые проекты, реализующие JPQL, должны его поддерживать. Таким образом, может быть, M2 также широко поддерживается?
Тийна
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.

zawhtut
источник
обратите внимание, что 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);
riship89
источник
В какой упаковке BeanUtils?
Хариш
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нотацию, всегда определяйте псевдонимы, совпадающие с именами из объекта, как показано в примере.

Thanthu
источник
1
нативный запрос и прогнозы не сочетаются друг с другом.
Кевин Рэйв
1
Я не мог заставить отображение поля работать правильно - большинство значений продолжали возвращаться как нулевые
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

Использование Hibernate:

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

Я уверен, что вы можете найти то же самое для других баз данных.

Также FYI, 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();

В моем случае мне пришлось использовать части SQL, определенные в строках где-то еще, поэтому я не мог просто использовать NamedNativeQuery.

Андреас Л.
источник
как только мы возвращаемся. ничего особенного. проблема в том, когда вы пытаетесь сопоставить результат с неуправляемым POJO.
Ольгун Кая
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

Ниже приведен пример использования POJO в качестве псевдо-сущности для получения результата из собственного запроса без использования сложного SqlResultSetMapping. Просто нужно две аннотации, пустой @Enity и фиктивный @Id в вашем POJO. @Id может использоваться в любом поле по вашему выбору, поле @Id может иметь дублирующиеся ключи, но не нулевые значения.

Поскольку @Enity не отображается ни на одну физическую таблицу, этот POJO называется псевдо-сущностью.

Среда: eclipselink 2.5.0-RC1, jpa-2.1.0, mysql-connector-java-5.1.14

Вы можете скачать полный проект Maven здесь

Собственный запрос основан на примере сотрудников MySQL. Ddev 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)то обнаружите, что ваш ответ неправильный, и содержимое вашего списка не является сотрудниками, и что это предупреждение, которое вы подавили, имело целью предупредить вас об этой потенциальной ошибке.
Эдвин Далорсо
@SuppressWarnings ("unchecked") используется для подавления предупреждения List<Employee> list = (List<Employee>) query.getResultList();об улучшении Change for (Object emp : list)to for (Employee emp : list), но без ошибок, если его оставить, Object empпоскольку list является экземпляром List<Employee>. Я изменил код в git проекте, но не здесь, чтобы ваш комментарий был актуален для оригинального сообщения
Джонатан Л
проблема в том, что ваш запрос не возвращает список сотрудников, а представляет собой массив объектов. Ваше скрытое предупреждение скрывает это. В тот момент, когда вы пытаетесь преобразовать любого из них в сотрудника, вы получите ошибку, исключение приведения.
Эдвин Далорсо
Посмотрите Query query = em.createNativeQuery("select * ...", Employee.class);и persistence.xml, нативный запрос возвращает список Employee. Я только что проверил и запустил проект без проблемы. Если вы настроите пример базы данных mysql для сотрудников локально, вы также сможете запустить проект
Джонатан Л
О, я понимаю, что ты имеешь в виду сейчас. Но в этом случае ваш ответ не удовлетворяет этому вопросу, потому что речь шла об использовании обычного POJO в качестве целевого объекта, а ваш ответ использует, как Employeeя полагаю, сущность. Не так ли?
Эдвин Далорсо
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

Использование Hibernate:

@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 Sideи Client Sideотдельно. DTOиспользуется, когда Server sideне хочет проходить / возвращаться Entityс аннотацией к Client 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 в моем приложении. Мое требование - привести результирующий набор к qa pojo, который не является сущностью JAVA, а простым классом POJO, который имеет те же поля, что и столбцы набора результатов.
Гунджан Шах