Сначала немного контекста - я реализую API, в котором запросы содержат поля, которые интересуют клиента, в основном параметры для непосредственного выбора. Поскольку общий размер данных достаточно велик, если его не ограничивать таким образом, это делает очевидным выигрыш в производительности, когда речь идет о сериализации и передаче данных, а также ограничении объема данных, извлекаемых из базы данных. Поэтому я пытаюсь передать их для выбора при выполнении запроса к базе данных.
Проблема, с которой я сталкиваюсь, заключается в том, что при выполнении запроса с помощью выбора Hibernate ожидает существование определенного конструктора, который соответствует параметрам выбора. Например, если я укажу, что меня интересуют только столбцы id
(long) и model
(String) в моем классе BasicInfo
, то Hibternate попытается найти и вызвать конструктор BasicInfo(int, String)
. На это указывает сообщение об ошибке:
org.hibernate.hql.internal.ast.QuerySyntaxException: Невозможно найти соответствующий конструктор в классе [my.package.BasicInfo]. Ожидаемые аргументы: long, java.lang.String [выберите новый my.package.BasicInfo (агентAlias0.id, генерируетсяAlias0.model) из my.package.BasicInfo asагентAlias0]
Это невариант для меня. Клиент может пожелать запросить, скажем, model
(String) и serial
(также String) в одном запросе в этом порядке, тогда как другой клиент может пожелать запросить те же поля в обратном порядке. Поскольку я смогу создать только одну комбинацию конструктора BasicInfo(String, String)
, один из запросов будет получать данные в обратном порядке. Даже без учета этого факта мне пришлось бы создавать все возможные перестановки конструкторов всех возможных полей, которые могут быть запрошены, что - с учетом количества полей, существующих в этом классе - делает его очень неэффективным, если не невозможным, делом.
Мне нужен способ заставить Hibernate использовать конструктор класса по умолчанию, даже если указан параметр запроса select, и использовать установщики для установки запрошенных полей. Если нет, то, возможно, другой способ, который позволил бы мне не извлекать полные строки данных из базы данных при каждом запросе (и затем ограничивать их в коде приложения перед отправкой обратно клиенту).
IМы также пытались зарегистрировать пользовательский перехватчик Hibernate и переопределяли его метод instantiate
следующим образом:
public class CustomInterceptor extends EmptyInterceptor {
@Override
public Object instantiate(String entityName, EntityMode entityMode, Serializable id) {
if(entityName.equals(BasicInfo.class.getName()))
return new BasicInfo();
return null;
}
}
, надеясь, что это изменит стратегию создания объекта. В этом случае, когда я не указываю параметры выбора, этот метод вызывается правильно. Однако, когда заданы параметры выбора, он не вызывается вообще, и Hibternate пытается напрямую вызвать конструктор с конкретными аргументами.
Ниже приведены соответствующие части проекта:
Настройка кода выбора параметров (несущественные части кода удалены для ясности):
CriteriaQuery<T> query = cb.createQuery(responseClass);
Root<T> from = query.from(responseClass);
// Handle select
CriteriaQuery<T> select = null;
if(request.getSelect() != null) {
List<Selection<?>> selectionList = new ArrayList<Selection<?>>();
for(String s : request.getSelect()) {
selectionList.add(from.get(s));
}
select = query.multiselect(selectionList);
} else {
// select all fields
select = query.select(from);
}
TypedQuery<T> typedQuery = em.createQuery(select);
List<T> itemsList = typedQuery.getResultList();
И applicaion.yml
:
spring:
datasource:
platform: h2
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true