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


174

Я використовую JPA у своєму проекті.

Я прийшов до запиту, в якому мені потрібно зробити операцію приєднання на п'яти таблицях. Тому я створив власний запит, який повертає п'ять полів.

Тепер я хочу перетворити об'єкт результату в клас POJO Java, який містить ті самі п'ять рядків.

Чи є спосіб у 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.

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


1
Дякую за відповідь. Тут ми зіставляємо наш результат з сутністю з класом сутності tha 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. до основного ключа - все-таки ви повинні мати @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

Я один з тих, хто знаходить додавання всіх цих @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
Чи можливо для конструкторарезультату використовувати клас, який має вкладений клас?
chrismarx

5
Якщо використовувати JPA 2.1 з @SqlResultSetMappingцим, можливо, варто відзначити, що для Jediкласу буде потрібен конструктор all-arg, а @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());
    }

Це воно.


Приємна відповідь, але я думаю, ви пропустили дужки після першої анотації @ColumnResult.
mwatzer

У коді є помилки, але їх легко виправити. Наприклад: "resultSetMapping =" повинен бути "resultSetMapping ="
Zbyszek

3
Я бачу ще одну гірку правду: NamedNativeQuery & SqlResultSetMapping має бути в класі
@Entity

10

Якщо ви використовуєте Spring-jpa, це доповнення до відповідей та цього питання. Будь ласка, виправте це, якщо є недоліки. В основному я використовував три методи, щоб досягти "відображення результату Object[]на подзо", виходячи з того, яку практичну потребу я задовольняю:

  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якості прикладу в відповідь Едвіна Dalorzo в.

Перші два методи закликають багато середніх обробників, як-от індивідуальні перетворювачі. Наприклад, AntiStealingвизначає a secretKey, перш ніж воно зберігається, вставляється перетворювач для його шифрування. Це призведе до того, що перші два способи повернуть перетворену назад, secretKeyщо не є тим, що я хочу. У той час як метод 3 подолав би перетворювач, і повернутий secretKeyбув би таким же, як він зберігається (зашифрований).


Спосіб 1 насправді не потребує весни і працює з чистою сплячкою.
Мартін Висний

@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.


зауважте, що 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
рідний запит та проекції не йдуть разом.
Кевін Рейв

1
Я не міг зробити так, щоб картографічне поле працювало цілком правильно - більшість значень продовжували повертатися як null
аянг

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

Я впевнений, що ви можете знайти те саме для інших баз даних.

Також в якості мапи є результати власних запитів 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, визначені у Strings, тому я не міг просто використовувати 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

Дивіться приклад нижче для використання 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 db http://dev.mysql.com/doc/employee/en/employees-installation.html

стійкість.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>

Співробітник.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, чому ваш цикл для кожного циклу перебирається над типом Object? Якщо ви пишете свій цикл для кожного, як for(Employee emp : list)тоді, ви виявите, що ваша відповідь неправильна, а вміст вашого списку не є працівниками, і що попередження, яке ви придушили, мало на меті попередити вас про цю потенційну помилку.
Едвін Далорцо

@SuppressWarnings ("не відмічено") використовується для придушення попередження для " List<Employee> list = (List<Employee>) query.getResultList();Зміна for (Object emp : list)на for (Employee emp : list)краще", але помилок, якщо вони зберігаються, Object empоскільки список є екземпляром List<Employee>. Я змінив код у проекті git, але не тут, щоб ваш коментар стосувався початкової публікації
Jonathan L

проблема полягає в тому, що ваш запит не повертає список працюючих, а масив об'єктів. Ваше придушене попередження приховує це. У той момент, коли ви спробуєте перетворити будь-яку з них на службовця, ви отримаєте помилку, виняткову категорію.
Едвін Далорцо

Подивіться на Query query = em.createNativeQuery("select * ...", Employee.class);та persistent.xml, власний запит повертає список співробітників. Я щойно перевірив і запустив проект без випуску. Якщо ви налаштуєте mysql зразки співробітників db локально, ви також зможете запустити проект
Jonathan L

О, я бачу, що ти маєш на увазі зараз. Але в цьому випадку ваша відповідь не задовольняє питання, оскільки мова йшла про використання звичайного 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

Використання сплячого режиму:

@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, які мають ті ж поля, що і стовпці з набором результатів.
Гунян Шах
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.