The parameter-based Automatic Queries pattern already provides an alternative to much of what Query by Method Name offers for the repository author to define queries upfront, but some parts are lacking which we should address. Applications ought to be able to choose to fully utilize this pattern and not need to rely on Query by Method Name in places.
The most notable piece that is lacking is the ability to request conditions other than equality. One way to address that would be with a single Is annotation that accepts an enumerated value for the condition to apply. For example,
@Find
@OrderBy(_Product.PRICE)
@OrderBy(_Product.NAME)
@OrderBy(_Product.ID)
Page<Product> search(@By(_Product.NAME) @Is(Like) String pattern,
@By(_Product.PRICE) @Is(LessThan) float max,
PageRequest pageRequest);
The above directly corresponds to the following Query by Method Name method that could currently be used:
Page<Product> findByNameLikeAndPriceLessThanOrderByPriceAscNameAscIdAsc(String pattern,
float max,
PageRequest pageRequest);
Note that everything that is needed for the former is already in the Jakarta Data specification except for the mechanism for specifying the condition to be something other than equals. So the enhancement here has a direct equivalence to the Query by Method Name and achieves it with a minor addition that fits right in to an existing specification pattern.
There has been some confusion over how this differs from other new capability like Restrictions and new Special parameter types (Pattern/Range) with which the repository user can customize different types of conditions. These are two very different usage patterns. In some cases, the nature of the application involves a repository user needing the flexibility to compute different conditions at run time, such as in response to an end user defining different search conditions. But in other cases, the repository author wishes to provide specific capability and to externalize it to the repository user in the most simplistic and direct way, eliminating as much misuse and error conditions as possible.
When the repository author has a set of requirements that can be encoded into the repository method definition in advance, they ought to be able to do so, in order to abstract the complexity away from the repository user, who can then use the repository in the simplest way, without concern for complexities that the repository author has already taken care of.
A brief example of this is a repository for a library that wants to identify overdue books in order to remind the borrower to return them. The repository author might write the following method, encoding the LESS_THAN condition for the dueDate to identify overdue books, and appropriately naming the method so that the repository user knows exactly what it is for,
@Find
@OrderBy(_Book.DUE_DATE)
@OrderBy(_Book.BORROWER)
@OrderBy(_Book.TITLE)
List<Book> booksDueBefore(@Is(LESS_THAN) LocalDate dueDate);
The repository user can easily understand that they should invoke the method with today's date to find the books that are overdue,
overdueBooks = library.booksDueBefore(LocalDate.now());
If the repository author had instead written the method without encoding the LESS_THAN requirement, and instead only encoding that it needs to compare the dueDate in some way, then it might look something like this,
@Find
@OrderBy(_Book.DUE_DATE)
@OrderBy(_Book.BORROWER)
@OrderBy(_Book.TITLE)
List<Book> booksDueBefore(Range<LocalDate> dueDate);
The burden is then on the repository user to figure out what type of range to supply in order to make the method do what its name says it will do.
One right answer is that they can use Range.before, sending in the current date to make it return all past values to finds all overdue books,
overdueBooks = library.booksDueBefore(Range.before(LocalDate.now()));
The other right answer is that they can use Range.to, sending in 1 less than the current date, also making it return all past values to find all overdue books,
overdueBooks = library.booksDueBefore(Range.to(LocalDate.now().minus(Period.ofDays(1))));
There is (unwanted) flexibility to send in some other Ranges to the method, making it possible to do things that contradict what its name says it will do,
booksThatAreNotOverdue = library.booksDueBefore(Range.from(LocalDate.now()));
There are multiple problems here:
- The repository author is unable to encode/enforce the requirements that it knows it has
- The repository method is not guaranteed to do what its name says it will do, is vulnerable to mistakes, and can be misused for other purposes
- The repository user needs to stop and think extra about how to use the method in order to ensure the above
- The repository user is burdened with writing extra code, which is also more difficult to read when trying to understand the business logic of the application.
A best practice should be that the repository author always encodes in advance all of the restrictions that they know will remain constant across all usage of the method. Currently, the only ways to do that are Query by Method Name (but that pattern is discouraged and expected to go away) and Query Language (advanced users only). This issue is opened so that we have a simple recommended way for newer and non-advanced users. It should be straightforward, clear, and easily discoverable which restrictions are available.
The parameter-based Automatic Queries pattern already provides an alternative to much of what Query by Method Name offers for the repository author to define queries upfront, but some parts are lacking which we should address. Applications ought to be able to choose to fully utilize this pattern and not need to rely on Query by Method Name in places.
The most notable piece that is lacking is the ability to request conditions other than equality. One way to address that would be with a single
Isannotation that accepts an enumerated value for the condition to apply. For example,The above directly corresponds to the following Query by Method Name method that could currently be used:
Note that everything that is needed for the former is already in the Jakarta Data specification except for the mechanism for specifying the condition to be something other than equals. So the enhancement here has a direct equivalence to the Query by Method Name and achieves it with a minor addition that fits right in to an existing specification pattern.
There has been some confusion over how this differs from other new capability like Restrictions and new Special parameter types (Pattern/Range) with which the repository user can customize different types of conditions. These are two very different usage patterns. In some cases, the nature of the application involves a repository user needing the flexibility to compute different conditions at run time, such as in response to an end user defining different search conditions. But in other cases, the repository author wishes to provide specific capability and to externalize it to the repository user in the most simplistic and direct way, eliminating as much misuse and error conditions as possible.
When the repository author has a set of requirements that can be encoded into the repository method definition in advance, they ought to be able to do so, in order to abstract the complexity away from the repository user, who can then use the repository in the simplest way, without concern for complexities that the repository author has already taken care of.
A brief example of this is a repository for a library that wants to identify overdue books in order to remind the borrower to return them. The repository author might write the following method, encoding the LESS_THAN condition for the dueDate to identify overdue books, and appropriately naming the method so that the repository user knows exactly what it is for,
The repository user can easily understand that they should invoke the method with today's date to find the books that are overdue,
If the repository author had instead written the method without encoding the LESS_THAN requirement, and instead only encoding that it needs to compare the dueDate in some way, then it might look something like this,
The burden is then on the repository user to figure out what type of range to supply in order to make the method do what its name says it will do.
One right answer is that they can use
Range.before, sending in the current date to make it return all past values to finds all overdue books,The other right answer is that they can use
Range.to, sending in 1 less than the current date, also making it return all past values to find all overdue books,There is (unwanted) flexibility to send in some other Ranges to the method, making it possible to do things that contradict what its name says it will do,
There are multiple problems here:
A best practice should be that the repository author always encodes in advance all of the restrictions that they know will remain constant across all usage of the method. Currently, the only ways to do that are Query by Method Name (but that pattern is discouraged and expected to go away) and Query Language (advanced users only). This issue is opened so that we have a simple recommended way for newer and non-advanced users. It should be straightforward, clear, and easily discoverable which restrictions are available.