Как использовать объект в качестве фильтра в собственном запросе JPA?JAVA

Программисты JAVA общаются здесь
Anonymous
Как использовать объект в качестве фильтра в собственном запросе JPA?

Сообщение Anonymous »

У меня есть собственный запрос, который принимает для фильтрации 4 (необязательных) параметра. 4 — это слишком много для читаемости, поэтому я решил заменить его объектом. При этом я столкнулся с двумя отдельными проблемами.
Во-первых, я просто попробовал сделать POJO в качестве фильтра:

Код: Выделить всё

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
public class QuestionCategoryFilter {
private String name;
private Boolean active;
private Integer orderNumber;
private String answerTypeName;
}
но потом я получил следующую ошибку:

Код: Выделить всё

org.springframework.orm.jpa.JpaSystemException: A problem occurred in the SQL executor : No JDBC mapping could be inferred for parameter - org.hibernate.sql.exec.internal.JdbcParameterImpl@6c173735
Поэтому позже я наткнулся на решение, буквально просто нажав CTRL + щелкнув классы спящего режима, и мой поиск привел к org.hibernate.type.QueryParameterJavaObjectType.
Расширение этого класса действительно решило проблему, мой запрос мог быть выполнен нормально, но позже, добавляя Swagger-UI в проект, я столкнулся с проблемой моего простого объекта с 4 полями, который на самом деле был гигантский:

Код: Выделить всё

{
"jdbcType": {
"integer": true,
"float": true,
"number": true,
"binary": true,
"castType": "STRING",
"ddlTypeCode": 0,
"duration": true,
"nationalized": true,
"string": true,
"interval": true,
"friendlyName": "string",
"lobOrLong": true,
"jdbcTypeCode": 0,
"temporal": true,
"stringLike": true,
"lob": true,
"defaultSqlTypeCode": 0,
"decimal": true
},
"jdbcValueBinder": {},
"jdbcValueExtractor": {},
"jdbcLiteralFormatter": {},
"name": "string",
"active": true,
"orderNumber": 0,
"answerTypeName": "string",
"mutable": true,
"associationType": true,
"componentType": true,
"collectionType": true,
"anyType": true,
"entityType": true,
"castType": "STRING",
"registrationKeys": [
"string"
],
"javaTypeDescriptor": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
},
"valueConverter": {
"relationalJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
},
"domainJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
}
},
"mappedType": {
"mappedJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
}
},
"jdbcTypeCount": 0,
"jdbcMapping": {
"jdbcJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength":  0,
"temporalType": true
},
"jdbcType": {
"integer": true,
"float": true,
"number": true,
"binary": true,
"castType": "STRING",
"ddlTypeCode": 0,
"duration": true,
"nationalized": true,
"string": true,
"interval": true,
"friendlyName": "string",
"lobOrLong": true,
"jdbcTypeCode": 0,
"temporal": true,
"stringLike": true,
"lob": true,
"defaultSqlTypeCode": 0,
"decimal": true
},
"jdbcValueBinder": {},
"castType": "STRING",
"valueConverter": {
"relationalJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
},
"domainJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
}
},
"jdbcTypeCount": 0,
"mappedJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
},
"javaTypeDescriptor": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
},
"jdbcLiteralFormatter": {},
"singleJdbcMapping": "string",
"jdbcValueExtractor": {}
},
"mappedJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
},
"expressibleJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
},
"singleJdbcMapping": {
"jdbcJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
},
"jdbcType": {
"integer": true,
"float": true,
"number": true,
"binary": true,
"castType": "STRING",
"ddlTypeCode": 0,
"duration": true,
"nationalized": true,
"string": true,
"interval": true,
"friendlyName": "string",
"lobOrLong":  true,
"jdbcTypeCode": 0,
"temporal": true,
"stringLike": true,
"lob": true,
"defaultSqlTypeCode": 0,
"decimal": true
},
"jdbcValueBinder": {},
"castType": "STRING",
"valueConverter": {
"relationalJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
},
"domainJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
}
},
"jdbcTypeCount": 0,
"mappedJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
},
"javaTypeDescriptor": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
},
"jdbcLiteralFormatter": {},
"singleJdbcMapping": "string",
"jdbcValueExtractor": {}
},
"persistenceType": "ENTITY",
"sqmType": {
"sqmType": "string",
"tupleLength": 0,
"typeName": "string",
"expressibleJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
},
"relationalJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
}
},
"tupleLength": 0,
"typeName": "string",
"relationalJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType": true
},
"jdbcJavaType": {
"javaType": {
"typeName": "string"
},
"typeName": "string",
"defaultValue": {},
"comparator": {},
"mutabilityPlan": {
"mutable": true
},
"longSqlLength": 0,
"temporalType":  true
}
}
И я не могу добавить @JsonIgnore или подобные способы, чтобы не подвергать их чванству, поскольку все, кроме четырех, происходят из классов, которыми я не владею.
Итак, каково обычное решение моей проблемы?
Должен ли я отказаться от использования объекта в запросе и развернуть объект при его вызове?

Должен ли я создать отдельный объект фильтра для API, который я затем преобразую в JPA-совместимый объект фильтра?

Или есть ли более простой способ расширения JavaType вместо org.hibernate.type.QueryParameterJavaObjectType?
Edit1: Я не знаю, почему я никогда не публиковал свой исходный код, поэтому вот соответствующие части:

Код: Выделить всё

    @Transactional(readOnly = true)
@Query(nativeQuery = true, value = """
SELECT qc.id, qc.name, qc.active, qc.order_number as orderNumber, qc.answer_type_id as answerTypeId,
at.name as answerTypeName, at.max_value as 'maxValue',
q.id as questionId, q.question, q.active AS questionActive, q.order_number AS questionOrderNumber,
ai.num_answer as numAnswer,
a.answer_time as answerTime, a.active AS answerActive,
u.name as username, u.ldap_id as ldapId, u.active as userActive
FROM QUESTION_CATEGORIES qc
INNER JOIN ANSWER_TYPES at on at.id = qc.answer_type_id
INNER JOIN QUESTIONS q ON q.category_id = qc.id
LEFT JOIN ANSWER_ITEMS ai ON ai.question_id = q.id
LEFT JOIN ANSWERS a ON a.id = ai.answer_id
LEFT JOIN USERS u ON (u.id = a.user_id AND u.ldap_id = :username)
WHERE q.active = true
AND :filter IS NULL OR (
(:#{#filter.getName()} IS NULL OR LOWER(qc.name) LIKE CONCAT('%', LOWER(:#{#filter.getName()}), '%'))
AND (:#{#filter.getActive()} IS NULL OR qc.active = :#{#filter.getActive()})
AND (:#{#filter.getOrderNumber()} IS NULL OR qc.order_number = :#{#filter.getOrderNumber()})
AND (:#{#filter.getAnswerTypeName()} IS NULL OR at.name = :#{#filter.getAnswerTypeName()})
)
ORDER BY qc.order_number
""")
List findAllResponsesByFilterAndUser(@Param("filter") QuestionCategoryFilter filter, @Param("username") String username);
и это работает, но только потому, что общедоступный класс QuestionCategoryFilter расширяет QueryParameterJavaObjectType. Без расширения QueryParameterJavaObjectType я получаю ошибку, о которой упоминал ранее.
Теперь я вижу, что каждый пример, которому я следовал, имеет только один аргумент, который является объектом фильтра, тогда как у меня есть объект и дополнительный аргумент.
В конце концов я решил проблему, которая у меня была, а именно конструктор, сгенерированный lombok, имеющий смехотворное количество аргументов, которые springdoc затем радостно вырвало на интерфейс swagger-ui.
Но для дальнейшего использования я хотел бы знать, каким будет правильное решение (вместо обратного проектирования Hibernate и удачи с расширением), или же наличие оболочки для параметров запроса и дополнительных параметров - это просто плохая практика и намеренно не поддерживается.

Вернуться в «JAVA»