Query query = em.createNativeQuery(STUDENT_QUERY_WITH_PARAMS);
query.setParameter("studentId", stduentId);
query.setParameter("startDate", valueOf(startDate), TemporalType.DATE);
query.setParameter("endDate", valueOf(endDate), TemporalType.DATE);
List<Object[]> resultList = query.getResultList();
List<StudentDepartmentCatDTO> result = new ArrayList<>(resultList.size());
for (Object[] row : resultList) {
result.add(new StudentDepartmentCatDTO((String)row[0], (String)row[1], (String)row[2], null,(UUID)row[4], (Integer)row[5], (Integer)row[6], (Integer)row[7], (Integer)row[8], null, null, null, null ));
}
«У меня есть SQL-запрос для извлечения определенных данных из разных таблиц путем объединения нескольких таблиц (в моем случае 13 таблиц), и я могу успешно вернуть все результаты, но когда я пытаюсь сопоставить эти результаты обратно с DTO, я получаю приведение исключения. Как обойти эти исключения?
B нельзя привести к классу java.util.UUID и класс java.math.BigDecimal не может быть приведен к классу java.lang.Integer
Поскольку все результаты поступают из разных таблиц, я не нашел способа сделать это с помощью SqlResultSetMapping, поскольку ни один из управляемых объектов не может быть напрямую сопоставлен с результатами. Есть лучший способ сделать это? а как исправить эти литые исключения? Могу ли я создать неуправляемую сущность, чтобы сопоставить с ней все результаты? Как извлечь результаты sql в мой DTO.
Вы можете возразить используйте @SqlResultSetMapping
, чтобы преобразовать каждую запись в вашем наборе результатов в DTO. Но для этого потребуется дополнительный конструктор, чтобы вам не нужно было устанавливать какие-либо параметры в null
.
Вот пример такого отображения. Как видите, вам нужно ссылаться на элементы в вашем результирующем наборе по их имени, но вы не поделились своим запросом. Итак, я не могу адаптировать пример к вашему запросу.
@SqlResultSetMapping(
name = "BookValueMapping",
classes = @ConstructorResult(
targetClass = BookValue.class,
columns = {
@ColumnResult(name = "id", type = Long.class),
@ColumnResult(name = "title"),
@ColumnResult(name = "version", type = Long.class),
@ColumnResult(name = "authorName")}))
Я использую это сопоставление с запросом, который выбирает столбцы id
, title
, version
и authorName
из базы данных. Аннотация @ConstructorResult
описывает вызов конструктора класса BookValue
. Аннотации @ColumnResult
определяют параметры конструктора и их порядок. Вы должны убедиться, что созданный экземпляр класса предоставляет конструктор, соответствующий этим параметрам.
@SqlResultSetMapping
— мощная и гибкая функция в JPA. Я подробно объясняю это здесь: https://thoughts-on-java.org/result-set-mapping-constructor-result-mappings/
Хорошо, давайте теперь посмотрим на проблему с составом классов...
Приведение не выполняется, потому что два объекта относятся к неправильному классу. row[4]
кажется byte[]
, а один из row[6/7/8]
(или все они) кажутся BigInteger.
Этот фрагмент кода должен помочь вам преобразовать byte[] в UUID:
ByteBuffer bb = ByteBuffer.wrap(bytes);
long high = bb.getLong();
long low = bb.getLong();
UUID uuid = new UUID(high, low);
Преобразование BigDecimal
в Integer
намного проще. Вам просто нужно вызвать метод intValueExact()
на вашем BigDecimal
, например. row[6].intValueExact();
.
Спасибо, Янссен, я просмотрел статью, она очень полезна в нашей текущей базе кода, мы везде использовали SQLResultMapping, но это особый случай, когда у нас нет управляемого объекта для запроса. Это странный сценарий, мы присоединяемся к 12 таблицам и 12 таблицам. Мы разделили все сопоставления наших сущностей на 3 разных службы API, поэтому не можем сопоставлять все сущности при соединениях.