jakartaee / data

Data-API
Apache License 2.0
90 stars 26 forks source link

Proposal for Criteria Feature in Jakarta Data[Vote] #460

Open otaviojava opened 7 months ago

otaviojava commented 7 months ago

As a ...

I need to be able to ...

Expanding upon the capabilities of the Jakarta Data Query Language (#458), this proposal introduces the Criteria feature to enable programmatically constructing queries within the Jakarta Data framework. The Criteria API provides developers with an object-oriented approach to building query criteria, offering greater flexibility and control over data retrieval operations.

Which enables me to ...

The primary goal of this proposal is to initiate a discussion and a vote regarding the inclusion of the Criteria feature in the Jakarta Data project.

Key Features:

  1. Programmatic Query Construction: The Criteria API allows developers to programmatically build query criteria using a fluent and object-oriented interface, eliminating the need for raw queries.

  2. Filtering and Logical Conditions: Developers can apply various filtration rules and logical conditions to the criteria query object, enabling precise data retrieval based on specific criteria.

  3. Integration with Jakarta Data Query Language: The Criteria feature seamlessly integrates with the Jakarta Data Query Language, providing an additional method for constructing queries within the framework.

  4. Integration with Metadata: Criteria queries can be combined with metadata-driven queries, offering developers multiple options for constructing queries based on their requirements.

  5. Integration with Repository: Introducing a new interface, CriteriaRepository, that extends the existing PageableRepository, and enables seamless integration of criteria-based queries with repository pattern implementations.

Additional information

Example Usage:

// Combined with Metadata
List<Car> cars = template
    .select(Car.class)
    .where(_Car.city.eq("Rome").and(_Car.active.isTrue()))
    .orderBy(_Car.name.asc())
    .result();

// Combined with Repository
@Repository
public interface CardEntityRepository extends PageableRepository<Car, String>, CriteriaRepository<Car> {

    default Page<Car> findAll(String city, Pageable pageable) {
        Specification<Car> criteria = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(criteriaBuilder.equal(root.get("city"), city));
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
        return findAll(criteria, pageable);
    }
}

// Pure Implementation
List<Cat> cats = template.createCriteria(Cat.class)
    .add(Predicate.like("name", "Iz%"))
    .addOrder(Order.asc("age"))
    .result();

Benefits:

  1. Simplified Query Construction: The Criteria API simplifies query construction by providing a fluent and object-oriented interface, reducing the complexity of query writing.

  2. Enhanced Flexibility: Developers have greater flexibility in defining query criteria programmatically, allowing for dynamic and adaptable data retrieval operations.

  3. Integration with Metadata and Query Language: The Criteria feature seamlessly integrates with metadata-driven queries and the Jakarta Data Query Language, providing a comprehensive solution for data access within the Jakarta Data framework.

  4. Repository Integration: The introduction of CriteriaRepository enables developers to leverage criteria-based queries within the repository pattern, promoting code organization and reusability.

graemerocher commented 7 months ago

seems specific to JPA criteria

njr-11 commented 7 months ago

A pattern like this is something to consider post v1.0 when more is added to the static metamodel to better enable it.

gavinking commented 7 months ago

A pattern like this is something to consider post v1.0 when more is added to the static metamodel to better enable it.

Agreed. This isn't something I would bite off for 1.0. I think really it's tricky to get right. (Speaking from hard experience here.)

gavinking commented 7 months ago

By the way, since this issue is open, I just want to record for the future that @njr-11 has proposed something that I think is really nice, in some ways quite a lot nicer than a "full" criteria query API. The idea is to use the static metamodel to express "restrictions" which can be passed to a repository methods.

Conceptually, something like this:

repo.books(_Book.title.like("Jakarta Data%"), 
           _Book.publicationDate.between(pastDate, LocalDate.now()))

Or, if you need to combine this with ordering:

repo.books(where(_Book.title.like("Jakarta Data%"), 
                 _Book.publicationDate.between(pastDate, LocalDate.now())),
           by(_Book.title.ascIgnoreCase(),_Book.isbn.asc())))

[Don't focus on the details here, I know @njr-11 has thought about this longer than I have and surely has more-developed ideas about it.]

Of course, this is much less expressive than a real criteria API. But:

Now, on the other hand, it's also completely compatible with a full criteria API, it's not really in competition at all. The objects returned by like() and between() could be criteria expressions.

Anyway, I would love to have this, and I think users would stampede for something like it.

otaviojava commented 7 months ago

That is nice! IIMHO: it would be faster than the query language.