Как я могу избежать создания лишних сущностей?

#java #hibernate #jpa

#java #спящий режим #jpa

Вопрос:

В моем текущем проекте мне нужно выполнить несколько собственных запросов, которые выбирают некоторые поля из таблиц, объединенных в запросе, например:

 SELECT t1.col1, t2.col5
FROM t1
JOIN t2 ON t2.id = t1.t2_id
  

Я попытался сохранить их в классе, подобном

 class Result {
  String t1_col1;
  String t2_col5;
}
  

использование

 Query q = entityManager.createNativeQuery( "THE SQL SELECT" , Result.class );
  

Теперь JPA жалуется («uknown entity: result»), что класс ‘result’ не является сущностью, которая, вероятно, требуется для сопоставления столбцов с объектом.
Я также попытался повторить @Column объявления в классе результатов.

Мой вопрос в том, как я могу объявить это без необходимости создавать объекты, представленные в виде таблиц в моей БД?

Ответ №1:

Увы, я не вижу способа сделать это в JPA. Однако вы можете сделать это с Query помощью объекта hibernate. для его получения используйте:

 org.hibernate.Query query = q.unwrap(org.hibernate.Query.class);
  

А затем установите преобразователь результатов. Смотрите здесь:

 query.setResultTransformer(Transformers.aliasToBean(Result.class));
  

Ответ №2:

Если вы используете JPA / Hibernate для выполнения SQL-запросов, то вы используете неправильный инструмент. Hibernate — это ORM, и вы должны сопоставлять таблицы с объектами. В этом весь смысл JPA. Если вы просто хотите выполнять SQL-запросы, используйте JDBC (и, например, JdbcTemplate от Spring)

Как только table1 и table2 будут сопоставлены с объектами (назовем эти объекты T1 и T2), вам больше не понадобятся эти SQL-запросы, потому что JPQL может выбирать только некоторые поля объектов. Ваш запрос может выглядеть следующим образом (в зависимости от связи между t1 и t2):

 select t1.col1, t2.col5 from T1 t1 join t1.t2 t2
  

И вам просто нужно будет выполнить итерацию по результату (список объектов [] ), чтобы построить свои результаты (которые являются DTO, а не отображенным объектом) :

 List<Object[]> rows = (List<Object[]>) query.list();
List<Result> listOfResults = new ArrayList<Result>(rows.size);
for (Object[] row : rows) {
    listOfResults.add(new Result((String) row[0], (String) row[1]));
}
  

Комментарии:

1. Согласен. Просто чтобы добавить одну маленькую (но довольно приятную) особенность: вы можете использовать оператор NEW в запросе JPQL, например: ВЫБЕРИТЕ NEW com . Result(t1.col1, t2.col5) FROM T1 t1 join t1.t2 t2 Если у вас есть конструктор Result(String, String), он будет вызван для создания вашего нового результирующего объекта, который не обязательно должен быть самим объектом.

2. Да, я знаю это, но нахожу это ужасным. Он не допускает рефакторинга и сохраняет только две или три тривиальные строки кода. Кроме того, это усложняет отладку.

3. что вы подразумеваете под «не допускает рефакторинга»? Если параметры или их типы изменяются, то ни использование, ни неиспользование НОВОЙ конструкции вас не спасут. Я думаю, что это вопрос компромисса, как и в случае с Criteria API — он гораздо более удобен для рефакторинга, но лично мне его сложнее читать.

4. Если я добавлю аргумент в свой конструктор, приведенный выше код перестанет компилироваться, и я буду знать, что мне нужно исправить запрос. С помощью new в запросе JPQL у меня просто будет исключение во время выполнения. Если я поменяю порядок аргументов в конструкторе, моя IDE переключит все вызовы. Но не в том случае, если новое находится в JPQL. И если мой запрос вернет какой-то неправильный тип, у меня будет чистое исключение ClassCastException вместо какого-то непонятного исключения «не удается найти конструктор». И я смогу проверить результат запроса, чтобы увидеть, что не так.

5. Это было не то, о чем спрашивал вопрос. Он запрашивал решение для собственного запроса.

Ответ №3:

Я могу запустить этот запрос (с небольшим изменением) в DataNucleus JPA, и он работает нормально, как и должно быть в соответствии со спецификацией JPA.

 SELECT t1.col1 AS t1_col1, t2.col5 AS t2_col5 FROM t1 JOIN t2 ON t2.id = t1.t2_id
  

т.е. сделать так, чтобы возвращаемые столбцы совпадали с именами полей в классе результатов. В спецификации JPA не говорится, что результирующий класс должен быть классом сущностей; он просто говорит «класс результирующего экземпляра (ов)».

Ответ №4:

Вам может сойти с рук определение представления, которое возвращает объединенные столбцы, необходимые из его запросов, и использовать имя представления для вашего класса-держателя данных.