Description
Feature Request
A very common use case is querying based on forms submitted from the frontend. Users fill out one or more query conditions on the interface. For example, we define a query object for searching Books:
class BookQuery {
private String bookName;
private String authorName;
// Getters and setters
}
Then we build the query based on this query object:
Specification<Book> byQuery(BookQuery queryParam) {
return (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (StringUtils.isNotBlank(queryParam.getBookName())) {
predicates.add(cb.like(
root.get("bookName"),
"%" + queryParam.getBookName() + "%"
));
}
if (StringUtils.isNotBlank(queryParam.getAuthorName())) {
predicates.add(cb.like(
root.get("author").get("name"),
"%" + queryParam.getAuthorName() + "%"
));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
}
Or by mybatis xml
<!-- BookMapper.xml -->
<mapper namespace="com.example.mapper.BookMapper">
<sql id="bookQueryConditions">
<where>
<if test="bookName != null and bookName != ''">
AND book_name LIKE CONCAT('%', #{bookName}, '%')
</if>
<if test="authorName != null and authorName != ''">
AND EXISTS (
SELECT 1 FROM author
WHERE author.id = book.author_id
AND author.name LIKE CONCAT('%', #{authorName}, '%')
)
</if>
</where>
</sql>
<select id="findByQuery" resultType="com.example.model.Book">
SELECT * FROM book
<include refid="bookQueryConditions"/>
ORDER BY id
</select>
<select id="countByQuery" resultType="long">
SELECT COUNT(*) FROM book
<include refid="bookQueryConditions"/>
</select>
</mapper>
In my daily work, a significant amount of time is wasted on this meaningless translation process - simply converting query POJOs into ORM-recognizable expressions.
When we used only JPA, this manifested as Predicates in CriteriaQuery. With Spring Data JPA, it's now wrapped as Specifications. This repetitive boilerplate code adds no business value but consumes considerable development time.
This pattern violates the DRY (Don't Repeat Yourself) principle and significantly impacts development efficiency. We need a standardized approach to eliminate this repetitive translation layer.
I propose a solution to automate this translation process, which I call Annotation-based Dynamic Query. This approach uses annotations to mark query objects, then automatically converts POJOs into ORM-recognizable query conditions through a processor.Core Concept:
By annotating fields in query objects, we can dynamically generate the corresponding query predicates (JPA Specification, MyBatis-plus QueryWrapper, etc.) without manual translation.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface QueryCondition {
String field() default ""; // Target entity field (default: same as query field)
Operator operator() default Operator.EQ; // =, LIKE, >, <, etc.
boolean ignoreNull() default true; // Skip if value is null
}
public enum Operator {
EQ, LIKE, GT, LT, IN, BETWEEN...
}
public class BookQuery {
@QueryCondition(operator = Operator.LIKE)
private String bookName;
@QueryCondition(field = "author.name", operator = Operator.LIKE)
private String authorName;
@QueryCondition(operator = Operator.GT, field = "publishDate")
private LocalDate minPublishDate;
}
We can implement a utility that transforms BookQuery into Specification through annotation reflection. This solution effectively eliminates repetitive and tedious coding while seamlessly integrating with frontend-backend separation architectures.
The workflow becomes straightforward:
Deserialize frontend parameters (e.g., JSON payload) into BookQuery POJO
Automatically convert to Specification
Execute various operations (pagination/list/count queries) with the generated specification
Key Advantages:
✅ Proven Solution - This isn't theoretical. We've successfully implemented this pattern across multiple projects, where it handles over 80% of query cases
✅ Replaces Complex APIs - Eliminates the need for cumbersome Criteria API constructions and error-prone HQL
✅ Full Coverage - Our real-world validation confirms it satisfies most application scenarios
✅ Seamless Integration - Naturally fits modern RESTful APIs with JSON payloads
While this has proven valuable in our projects, I'm uncertain about broader interest. Should the Spring Data maintainers find this enhancement worthwhile, I'd be glad to discuss.