Prerequisites

Applications are required to provide a CDI exposed EntityManager.

public class EntityManagerProducer
{

    @PersistenceContext
    private EntityManager em;

    @Produces
    public EntityManager expose()
    {
        return em;
    }

}

Repositories

Repositories are marked with the @Repository annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
@InvocationHandlerBinding
public @interface Repository
{
    Class<?> forEntity() default Object.class;
    String methodPrefix() default "findBy";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Query
{

    /**
     * Defines the Query to execute. Can be left empty for method expression queries
     * or when referencing a {@link #named()} query.
     */
    String value() default "";

    /**
     * References a named query.
     */
    String named() default "";

    /**
     * Flag indicating a native SQL query.
     */
    String isNative() default false;

    /**
     * Limits the number of results the query returns.
     */
    int max() default 0;

    /**
     * Defines a lock mode for the query.
     */
    LockModeType lockMode() default LockModeType.NONE;

    /**
     * (Optional) Query properties and hints.  May include vendor-specific query hints.
     */
    QueryHint[] hints() default {
    };

}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface QueryParam
{
    String value() default "";
}

@Repository(forEntity = Person.class)
public interface PersonRepository
{

    // Method expression with ordering
    List<Person> findByLastNameLikeAndBirthdateAndAddress_ZipOrderByLastNameAsc(String lastName, Date birthdate, String zip);

    // JPQL Query with indexed parameters
    @Query("from Person p where p.firstName = ?1 and p.lastName = ?2")
    Person findByFullName(String first, String last);

    // JPQL Query with named parameters - @QueryParam optional with Java 8
    // Lock mode for update
    @Query(value = "from Person p where p.age >= :minAge", lockMode = LockModeType.PESSIMISTIC_WRITE)
    List<Person> findAllAdultsForUpdate(@QueryParam("minAge") int minAge);

    // Named Query with indexed parameter
    @Query(named = "Person.findBySSN")
    Person findBySSN(String ssn);

    // Native SQL
    @Query(value = "SELECT * FROM PERSON_TABLE p WHERE p.AGE > ?1", isNative = true)
    List<Person> findOlderThan(int age);

}

Supported in method expressions:

Equal
NotEqual
Like
GreaterThan
GreaterThanEquals
LessThan
LessThanEquals
Between
IsNull
IsNotNull

Limiting Queries

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface FirstResult
{
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface MaxResults
{
}

@Repository(forEntity = Person.class)
public interface PersonRepository
{

    // Static limit
    @Query(max = 10)
    List<Person> findByAge(int age)

    // Paged queries
    @Query("from Person p where p.lastName like ?2")
    List<Person> findByLastNameLike(String last, @FirstResult int from, @MaxResults max);

}

Updating Queries

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Modifying
{
}

@Repository(forEntity = Person.class)
public interface PersonDao {

    @Modifying
    @Query("update Person as p set p.classifyer = ?1 where p.classifyer = ?2")
    int updateClassifyer(Classifier current, Classifier next);

}

Base Types

Base interface to extend, contains most common persistence operations. Example queries are mainly interesting for search screens, although it only supports like and equal queryies. Can be made optional.

public interface EntityRepository<E, PK extends Serializable>
{

    /**
     * Persist (new entity) or merge the given entity.
     * @param entity            Entity to save.
     * @return                  Returns the modified entity.
     */
    E save(E entity);

    /**
     * {@link #save(Object)}s the given entity and flushes the persistence context afterwards.
     * @param entity            Entity to save.
     * @return                  Returns the modified entity.
     */
    E saveAndFlush(E entity);

    /**
     * {@link #save(Object)}s the given entity and flushes the persistence context afterwards,
     * followed by a refresh (e.g. to load DB trigger modifications).
     * @param entity            Entity to save.
     * @return                  Returns the modified entity.
     */
    E saveAndFlushAndRefresh(E entity);

    /**
     * Convenience access to {@link javax.persistence.EntityManager#remove(Object)}.
     * @param entity            Entity to remove.
     */
    void remove(E entity);

    /**
     * Convenience access to {@link javax.persistence.EntityManager#remove(Object)}
     * with a following flush.
     * @param entity            Entity to remove.
     */
    void removeAndFlush(E entity);

    /**
     * Convenience access to {@link javax.persistence.EntityManager#refresh(Object)}.
     * @param entity            Entity to refresh.
     */
    void refresh(E entity);

    /**
     * Convenience access to {@link javax.persistence.EntityManager#flush()}.
     */
    void flush();

    /**
     * Entity lookup by primary key. Convenicence method around
     * {@link javax.persistence.EntityManager#find(Class, Object)}.
     * @param primaryKey        DB primary key.
     * @return                  Entity identified by primary or null if it does not exist.
     */
    E findBy(PK primaryKey);

    /**
     * Lookup all existing entities of entity class {@code <E>}.
     * @return                  List of entities, empty if none found.
     */
    List<E> findAll();

    /**
     * Lookup a range of existing entities of entity class {@code <E>} with support for pagination.
     * @param start             The starting position.
     * @param max               The maximum number of results to return
     * @return                  List of entities, empty if none found.
     */
    List<E> findAll(int start, int max);

    /**
     * Query by example - for a given object and a specific set of properties.
     * @param example           Sample entity. Query all like.
     * @param attributes        Which attributes to consider for the query.
     * @return                  List of entities matching the example, or empty if none found.
     */
    List<E> findBy(E example, SingularAttribute<E, ?>... attributes);

    /**
     * Query by example - for a given object and a specific set of properties with support for pagination.
     * @param example           Sample entity. Query all like.
     * @param start             The starting position.
     * @param max               The maximum number of results to return
     * @param attributes        Which attributes to consider for the query.
     * @return                  List of entities matching the example, or empty if none found.
     */
    List<E> findBy(E example, int start, int max, SingularAttribute<E, ?>... attributes);

    /**
     * Query by example - for a given object and a specific set of properties using a like operator for Strings.
     * @param example           Sample entity. Query all like.
     * @param attributes        Which attributes to consider for the query.
     * @return                  List of entities matching the example, or empty if none found.
     */
    List<E> findByLike(E example, SingularAttribute<E, ?>... attributes);

    /**
     * Query by example - for a given object and a specific set of properties
     * using a like operator for Strings with support for pagination.
     * @param example           Sample entity. Query all like.
     * @param start             The starting position.
     * @param max               The maximum number of results to return
     * @param attributes        Which attributes to consider for the query.
     * @return                  List of entities matching the example, or empty if none found.
     */
    List<E> findByLike(E example, int start, int max, SingularAttribute<E, ?>... attributes);

    /**
     * Count all existing entities of entity class {@code <E>}.
     * @return                  Counter.
     */
    Long count();

    /**
     * Count existing entities of entity class {@code <E>}
     * with for a given object and a specific set of properties..
     * @param example           Sample entity. Query all like.
     * @param attributes        Which attributes to consider for the query.
     *
     * @return                  Counter.
     */
    Long count(E example, SingularAttribute<E, ?>... attributes);

    /**
     * Count existing entities of entity class using the like operator for String attributes {@code <E>}
     * with for a given object and a specific set of properties..
     * @param example           Sample entity. Query all like.
     * @param attributes        Which attributes to consider for the query.
     *
     * @return                  Counter.
     */
    Long countLike(E example, SingularAttribute<E, ?>... attributes);

}

@Repository
public abstract class AbstractEntityRepository<E, PK extends Serializable>
        implements EntityRepository<E, PK>
{

    /**
     * Utility method to get hold of the entity manager for this Repository.
     *
     * @return          Entity manager instance.
     */
    protected abstract EntityManager getEntityManager();

    /**
     * Utility method to create a criteria query.
     * @return          Criteria query
     */
    protected abstract CriteriaQuery<E> getCriteriaQuery();

    /**
     * Get the entity class this Repository is related to.
     * @return          Repository entity class.
     */
    protected abstract Class<E> getEntityClass();

}

// @Repository inherited
// Entity is extracted from type parameters
public abstract class PersonRepository extends AbstractEntityRepository<Person, Long>
{

    // Concrete query sample with some logic
    // Can also be done with the Criteria API
    public List<Person> findByFullNameBirtdateZip(String first, String last, Date bdate, String zip)
    {
        String jpql = "from Person p where ";
        if (!isEmpty(last))
        {
            jpql += "p.lastName = :last";
        }
        ...
        return getEntityManager().createQuery(jpql) ...
    }

}

Annotation inheritance

// @Repository  inherited
public abstract class PersonRepository extends AbstractEntityRepository<Person, Long>
{
    ...
}

@Repository // caution, NOT inherited
public interface PersonRepository extends EntityRepository<Person, Long>
{
    ...
}

@Repository // caution, NOT inherited
public abstract class PersonRepository implements EntityRepository<Person, Long>
{
    ...
}

Pagination, Sorting and Dynamic Query Options

/**
 * Can be used as query result type, which will not execute the query immediately.
 * Allows some post processing like defining query ordering.
 *
 * @param <E> Entity type
 */
public interface QueryResult<E>
{

    /**
     * Sort the query result ascending by the given entity singular attribute.
     * This is the typesafe version, alternatively a {@link #orderAsc(String)}
     * String can be used.
     *
     * @param attribute         Sort attribute.
     * @return                  Fluent API: the result instance.
     */
    <X> QueryResult<E> orderAsc(SingularAttribute<E, X> attribute);

    /**
     * Sort the query result ascending by the given entity attribute.
     *
     * @param attribute         Sort attribute.
     * @return                  Fluent API: the result instance.
     */
    QueryResult<E> orderAsc(String attribute);

    /**
     * Sort the query result descending by the given entity singular attribute.
     * This is the typesafe version, alternatively a {@link #orderDesc(String)}
     * String can be used.
     *
     * @param attribute         Sort attribute.
     * @return                  Fluent API: the result instance.
     */
    <X> QueryResult<E> orderDesc(SingularAttribute<E, X> attribute);

    /**
     * Sort the query result descending by the given entity attribute.
     *
     * @param attribute         Sort attribute.
     * @return                  Fluent API: the result instance.
     */
    QueryResult<E> orderDesc(String attribute);

    /**
     * Revert an existing order attribute sort direction. Defaults to ascending
     * order if the sort attribute was not used before.
     *
     * @param attribute         Sort attribute.
     * @return                  Fluent API: the result instance.
     */
    <X> QueryResult<E> changeOrder(SingularAttribute<E, X> attribute);

    /**
     * Remove any ordering from the query result object.
     * @return                  Fluent API: the result instance.
     */
    QueryResult<E> clearOrder();

    /**
     * Revert an existing order attribute sort direction. Defaults to ascending
     * order if the sort attribute was not used before.
     *
     * @param attribute         Sort attribute.
     * @return                  Fluent API: the result instance.
     */
    QueryResult<E> changeOrder(String attribute);

    /**
     * Limit the number of results returned by the query.
     *
     * @param max               Max number of results.
     * @return                  Fluent API: the result instance.
     */
    QueryResult<E> maxResults(int max);

    /**
     * Pagination: Set the result start position. 0-based (as the JPA Query API).
     *
     * @param first             Result start position.
     * @return                  Fluent API: the result instance.
     */
    QueryResult<E> firstResult(int first);

    /**
     * Sets the query lock mode.
     *
     * @param lockMode          Query lock mode to use in the query.
     * @return                  Fluent API: the result instance.
     */
    QueryResult<E> lockMode(LockModeType lockMode);

    /**
     * Apply a query hint to the query to execute.
     *
     * @param hint              Hint name.
     * @param value             Hint value.
     * @return                  Fluent API: the result instance.
     */
    QueryResult<E> hint(String hint, Object value);

    /**
     * Fetch the result set.
     *
     * @return                  List of entities retrieved by the query.
     */
    List<E> getResultList();

    /**
     * Fetch a single result entity.
     *
     * @return                  Entity retrieved by the query.
     */
    E getSingleResult();

    /**
     * Count the result set.
     * @return                  Result count.
     */
    long count();

    /**
     * Turns the query into a paged query with the given page size.
     * Defaults to 10 if paging methods are called without this.
     *
     * @param pageSize          Page size for further queries.
     * @return                  Fluent API: the result instance.
     */
    QueryResult<E> withPageSize(int pageSize);

    /**
     * Move the page cursor to a specific page. First page is page 0.
     *
     * @param page              Page to move to for the next query.
     * @return                  Fluent API: the result instance.
     */
    QueryResult<E> toPage(int page);

    /**
     * Move to the next page.
     *
     * @return                  Fluent API: the result instance.
     */
    QueryResult<E> nextPage();

    /**
     * Move to the previous page.
     *
     * @return                  Fluent API: the result instance.
     */
    QueryResult<E> previousPage();

    /**
     * Count the number of pages.
     * @return                  Page count.
     */
    int countPages();

    /**
     * Return the actual page.
     * @return                  Page position.
     */
    int getCurrentPage();

    /**
     * Return the actual page size.
     * @return                  Page size.
     */
    int getPageSize();

}

Counting queries will have to modify the JPQL. For named queries this needs access to the underlying provider classes.

Usage:

@Repository
public interface PersonRepository extends EntityRepository<Person, Long>
{
    QueryResult<Person> findByAge(int age);
    QueryResult<Person> findBySSN(String ssn);
}

// Sorting
personRepository.findByAge(age)
    .sortAsc(Person_.lastName)
    .getResultList();

QueryResult<Person> result = personRepository.findByAge(age)
    .sortDesc("lastName");
result.changeOrder(Person_.lastName)
    .getResultList();

// Dynamic Query Options
personRepository.findBySSN(ssn)
    .lockMode(LockModeType.WRITE)
    .hint("org.hibernate.timeout", Integer.valueOf(10))
    .getSingleResult();

// Count
long total = personRepository.findByAge(age).count();

// Pagination
// Query API style
QueryResult<Person> paged = personRepository.findByAge(age)
    .maxResults(10)
    .firstResult(50);

// or paging style
QueryResult<Person> paged = personRepository.findByAge(age)
    .withPageSize(10) // equivalent to maxResults
    .toPage(5);

int totalPages = paged.countPages();

Repository Extensions

Used to add custom new behavior to Repositories:

import com.mysema.query.jpa.impl.JPAQuery;

public interface QueryDslSupport
{
    JPAQuery jpaQuery();
}

public class QueryDslRepositoryExtension implements QueryDslSupport, DelegateQueryHandler
{
    @Inject
    private QueryInvocationContext context;

    @Override
    public JPAQuery jpaQuery() {
        return new JPAQuery(context.getEntityManager());
    }
}

@Repository(forEntity = Person.class)
public interface PersonRepository extends QueryDslSupport
{
}

API:

public interface DelegateQueryHandler
{
}

/**
 * Expose the current query invocation to extensions.
 */
public interface QueryInvocationContext
{

    /**
     * Entity Manager used for the query.
     */
    EntityManager getEntityManager();

    /**
     * The class of the Entity related to the invoked Repository.
     */
    Class<?> getEntityClass();

    /**
     * Given the object parameter is an entity, checks if the entity is
     * persisted or not.
     * @param entity            Entity object, non nullable.
     * @return                  true if the entity is not persisted, false otherwise and if no entity.
     */
    boolean isNew(Object entity);

}

Multiple EntityManagers

public interface EntityManagerResolver
{
    EntityManager resolveEntityManager();
}

@Target({ TYPE })
@Retention(RUNTIME)
@Documented
public @interface EntityManagerConfig
{
    /**
     * References the type which provides the EntityManager for a specific repository.
     * Must be resolvable over the BeanManager.
     */
    Class<? extends EntityManagerResolver> entityManagerResolver() default EntityManagerResolver.class;

    /**
     * Set the flush mode for the repository EntityManager.
     */
    FlushModeType flushMode() default FlushModeType.AUTO;
}

Usage:

@Repository
@EntityManagerConfig(entityManagerResolver = CrmEntityManagerResolver.class, flushMode = FlushModeType.COMMIT)
public interface PersonRepository extends EntityRepository<Person, Long>
{
    ...
}

public class CrmEntityManagerResolver implements EntityManagerResolver
{
    @Inject @CustomerData // Qualifier - assumes producer is around...
    private EntityManager em;

    @Override
    public EntityManager resolveEntityManager()
    {
        return em;
    }
}

It's probably worth noting that the same restrictions for annotation inheritance apply here as for @Repository (interface annotations don't inherit on classes).

Auditing

API:

/**
 * Marks a property which should be updated with a timestamp when the entity gets persisted.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface CreatedOn
{
}

/**
 * Marks a property which should be updated with a timestamp when the entity gets updated.
 * By settings {@link #onCreate()} to {@code true}, the property gets also set when
 * the entity is persisted.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface ModifiedOn
{
    boolean onCreate() default false;
}

/**
 * Marks a property which should keep track on the last changing user.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface ModifiedBy
{
}

/**
 * Identifies the current user responsible for entity creation or modification.
 */
@Qualifier
@Target({ TYPE, METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
@Documented
public @interface CurrentUser
{
}

Usage:

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd" version="2.0">

    <persistence-unit-metadata>
        <persistence-unit-defaults>
            <entity-listeners>
                <entity-listener class="org.apache.deltaspike.data.impl.audit.AuditEntityListener" />
            </entity-listeners>
        </persistence-unit-defaults>
    </persistence-unit-metadata>

</entity-mappings>
@Entity
public class AuditedEntity
{
    @Temporal(TemporalType.TIMESTAMP)
    @CreatedOn
    private Calendar created;

    @ModifiedBy
    private String changer;

    @Temporal(TemporalType.TIME)
    @ModifiedOn(onCreate = true)
    private Date modified;

    ...
}

@Produces
@CurrentUser
public String who()
{
    return currentUser.username;
}

Criteria API simplifications

Reduces boilerplate for common cases around the criteria API without giving up on typesafety. There's also only support for metamodel classes, skipping any bloating string attribute based API calls.

Use case first, API makes probably more sense then:

@Repository(forEntity = Person.class)
public abstract class PersonDao implements CriteriaSupport
{

    // AND criteria with sorting

    public List<Person> findAdultFamilyMembers(String name, Integer minAge)
    {
        return criteria()
                .like(Person_.name, "%" + name + "%")
                .gtOrEq(Person_.age, minAge)
                .eq(Person_.validated, Boolean.TRUE)
                .orderDesc(Person_.age)
                .getResultList();
    }

    // Joins

    public List<Person> findByCompanyName(String companyName)
    {
        return criteria()
                .join(Person_.company,
                    where(Company.class)
                        .eq(Company_.name, companyName)
                )
                .eq(Person_.validated, Boolean.TRUE)
                .getResultList();
    }

    public Person findBySSN(String ssn)
    {
        return criteria()
                .fetch(Person_.familyMembers)
                .eq(Person_.ssn, ssn)
                .distinct()
                .getSingleResult();
    }

    // OR

    public List<Person> findAdults()
    {
        return criteria()
                .or(
                    criteria().
                        .gtOrEq(Person_.age, 18)
                        .eq(Person_.origin, Country.SWITZERLAND),
                    criteria().
                        .gtOrEq(Person_.age, 21)
                        .eq(Person_.origin, Country.USA)
                )
                .getResultList();
    }

    // Selections

    public Statistics ageStatsFor(Segment segment)
    {
        return criteria()
                .select(Statistics.class, avg(Person_.age), min(Person_.age), max(Person_.age))
                .eq(Person_.segment, segment)
                .getSingleResult();
    }

    public List<Object[]> personViewForFamily(String name)
    {
        return criteria()
                .select(upper(Person_.name), attribute(Person_.age), substring(Person_.firstname, 1))
                .like(Person_.name, name)
                .getResultList();
    }

}

API:


/**
 * Criteria API utilities, base class.
 *
 * @param <C> Entity type.
 * @param <R> Result type.
 */
public interface Criteria<C, R>
{

    /**
     * Executes the query and returns the result list.
     * @return List of entities matching the query.
     */
    List<R> getResultList();

    /**
     * Executes the query which has a single result.
     * @return Entity matching the search query.
     */
    R getSingleResult();

    /**
     * Creates a JPA query object to be executed.
     * @return A {@link TypedQuery} object ready to return results.
     */
    TypedQuery<R> createQuery();

    /**
     * Boolean OR with another Criteria.
     * @param criteria      The right side of the boolean OR.
     * @return              Fluent API: Criteria instance.
     */
    Criteria<C, R> or(Criteria<C, R>... criteria);

    /**
     * Boolean OR with another Criteria.
     * @param criteria      The right side of the boolean OR.
     * @return              Fluent API: Criteria instance.
     */
    Criteria<C, R> or(Collection<Criteria<C, R>> criteria);

    /**
     * Join an attribute with another Criteria.
     * @param att           The attribute to join.
     * @param criteria      The join criteria.
     * @return              Fluent API: Criteria instance.
     */
    <P, E> Criteria<C, R> join(SingularAttribute<? super C, P> att, Criteria<P, P> criteria);

    /**
     * Join a collection attribute with another Criteria.
     * @param att           The attribute to join.
     * @param criteria      The join criteria.
     * @return              Fluent API: Criteria instance.
     */
    <P, E> Criteria<C, R> join(ListAttribute<? super C, P> att, Criteria<P, P> criteria);

    /**
     * Join a collection attribute with another Criteria.
     * @param att           The attribute to join.
     * @param criteria      The join criteria.
     * @return              Fluent API: Criteria instance.
     */
    <P, E> Criteria<C, R> join(CollectionAttribute<? super C, P> att, Criteria<P, P> criteria);

    /**
     * Join a collection attribute with another Criteria.
     * @param att           The attribute to join.
     * @param criteria      The join criteria.
     * @return              Fluent API: Criteria instance.
     */
    <P, E> Criteria<C, R> join(SetAttribute<? super C, P> att, Criteria<P, P> criteria);

    /**
     * Join a collection attribute with another Criteria.
     * @param att           The attribute to join.
     * @param criteria      The join criteria.
     * @return              Fluent API: Criteria instance.
     */
    <P, E> Criteria<C, R> join(MapAttribute<? super C, E, P> att, Criteria<P, P> criteria);


    /**
     * Fetch join an attribute.
     * @param att           The attribute to fetch.
     * @return              Fluent API: Criteria instance.
     */
    <P, E> Criteria<C, R> fetch(SingularAttribute<? super C, P> att);

    /**
     * Fetch join an attribute.
     * @param att           The attribute to fetch.
     * @param joinType      The JoinType to use.
     * @return              Fluent API: Criteria instance.
     */
    <P, E> Criteria<C, R> fetch(SingularAttribute<? super C, P> att, JoinType joinType);

    /**
     * Fetch join an attribute.
     * @param att           The attribute to fetch.
     * @return              Fluent API: Criteria instance.
     */
    <P, E> Criteria<C, R> fetch(PluralAttribute<? super C, P, E> att);

    /**
     * Fetch join an attribute.
     * @param att           The attribute to fetch.
     * @param joinType      The JoinType to use.
     * @return              Fluent API: Criteria instance.
     */
    <P, E> Criteria<C, R> fetch(PluralAttribute<? super C, P, E> att, JoinType joinType);

    /**
     * Apply sorting by an attribute, ascending direction.
     * @param att           The attribute to order for.
     * @return              Fluent API: Criteria instance.
     */
    <P> Criteria<C, R> orderAsc(SingularAttribute<? super C, P> att);

    /**
     * Apply sorting by an attribute, descending direction.
     * @param att           The attribute to order for.
     * @return              Fluent API: Criteria instance.
     */
    <P> Criteria<C, R> orderDesc(SingularAttribute<? super C, P> att);

    /**
     * Create a select query.
     * @param resultClass   The query result class.
     * @param selection     List of selects (attributes, scalars...)
     * @return              Fluent API: Criteria instance.
     */
    <N> Criteria<C, N> select(Class<N> resultClass, QuerySelection<? super C, ?>... selection);

    /**
     * Create a select query.
     * @param selection     List of selects (attributes, scalars...)
     * @return              Fluent API: Criteria instance.
     */
    Criteria<C, Object[]> select(QuerySelection<? super C, ?>... selection);

    /**
     * Apply a distinct on the query.
     * @return              Fluent API: Criteria instance.
     */
    Criteria<C, R> distinct();

    /**
     * Equals predicate.
     * @param att           The attribute to compare with.
     * @param value         The comparison value.
     * @return              Fluent API: Criteria instance.
     */
    <P> Criteria<C, R> eq(SingularAttribute<? super C, P> att, P value);

    /**
     * Not Equals predicate.
     * @param att           The attribute to compare with.
     * @param value         The comparison value.
     * @return              Fluent API: Criteria instance.
     */
    <P> Criteria<C, R> notEq(SingularAttribute<? super C, P> att, P value);

    /**
     * Like predicate.
     * @param att           The attribute to compare with.
     * @param value         The comparison value.
     * @return              Fluent API: Criteria instance.
     */
    <P> Criteria<C, R> like(SingularAttribute<? super C, String> att, String value);

    /**
     * Not like predicate.
     * @param att           The attribute to compare with.
     * @param value         The comparison value.
     * @return              Fluent API: Criteria instance.
     */
    <P> Criteria<C, R> notLike(SingularAttribute<? super C, String> att, String value);

    /**
     * Less than predicate.
     * @param att           The attribute to compare with.
     * @param value         The comparison value.
     * @return              Fluent API: Criteria instance.
     */
    <P extends Number> Criteria<C, R> lt(SingularAttribute<? super C, P> att, P value);

    /**
     * Less than or equals predicate.
     * @param att           The attribute to compare with.
     * @param value         The comparison value.
     * @return              Fluent API: Criteria instance.
     */
    <P extends Comparable<? super P>> Criteria<C, R> ltOrEq(SingularAttribute<? super C, P> att, P value);

    /**
     * Greater than predicate.
     * @param att           The attribute to compare with.
     * @param value         The comparison value.
     * @return              Fluent API: Criteria instance.
     */
    <P extends Number> Criteria<C, R> gt(SingularAttribute<? super C, P> att, P value);

    /**
     * Greater than or equals predicate.
     * @param att           The attribute to compare with.
     * @param value         The comparison value.
     * @return              Fluent API: Criteria instance.
     */
    <P extends Comparable<? super P>> Criteria<C, R> gtOrEq(SingularAttribute<? super C, P> att, P value);

    /**
     * Between predicate.
     * @param att           The attribute to compare with.
     * @param lower         The lower bound comparison value.
     * @param upper         The upper bound comparison value.
     * @return              Fluent API: Criteria instance.
     */
    <P extends Comparable<? super P>> Criteria<C, R> between(SingularAttribute<? super C, P> att, P lower, P upper);

    /**
     * IsNull predicate.
     * @param att           The null attribute.
     * @return              Fluent API: Criteria instance.
     */
    <P> Criteria<C, R> isNull(SingularAttribute<? super C, P> att);

    /**
     * NotNull predicate.
     * @param att           The non-null attribute.
     * @return              Fluent API: Criteria instance.
     */
    <P> Criteria<C, R> notNull(SingularAttribute<? super C, P> att);

    /**
     * Empty predicate.
     * @param att           The collection attribute to check for emptyness.
     * @return              Fluent API: Criteria instance.
     */
    <P extends Collection<?>> Criteria<C, R> empty(SingularAttribute<? super C, P> att);

    /**
     * Not empty predicate.
     * @param att           The collection attribute to check for non-emptyness.
     * @return              Fluent API: Criteria instance.
     */
    <P extends Collection<?>> Criteria<C, R> notEmpty(SingularAttribute<? super C, P> att);

    /**
     * In predicte.
     * @param att           The attribute to check for.
     * @param values        The values for the in predicate.
     * @return
     */
    <P> Criteria<C, R> in(SingularAttribute<? super C, P> att, P... values);

    /**
     * Return the list of predicates applicable for this Criteria instance.
     * @param builder       A CriteriaBuilder used to instantiate the Predicates.
     * @param path          Current path.
     * @return              List of predicates applicable to this Criteria.
     */
    List<Predicate> predicates(CriteriaBuilder builder, Path<C> path);

}

/**
 * From an entity criteria query to a selection query.
 */
public interface QuerySelection<P, X>
{

    /**
     * Convert the instance to a criteria selection.
     * @param query         The current criteria query.
     * @param builder       The query builder used to instantiate the selection.
     * @param path          Current path.
     * @return              Criteria API selection instance corresponding to the
     *                      QuerySelection implementation.
     */
    <R> Selection<X> toSelection(CriteriaQuery<R> query, CriteriaBuilder builder, Path<? extends P> path);

}

/**
 * Entry type to create criteria queries.
 * Provides also helper methods for joins and selections.
 */
public interface CriteriaSupport<E>
{

    /**
     * Create a {@link Criteria} instance.
     * @return          Criteria instance related to the Repository entity class.
     */
    Criteria<E, E> criteria();

    /**
     * Create a {@link Criteria} instance.
     * @param <T>       Type related to the current criteria class.
     * @param clazz     Class other than the current entity class.
     * @return          Criteria instance related to a join type of the current entity class.
     */
    <T> Criteria<T, T> where(Class<T> clazz);

    /**
     * Create a {@link Criteria} instance with a join type.
     * @param <T>       Type related to the current criteria class.
     * @param clazz     Class other than the current entity class.
     * @param joinType  Join type to apply.
     * @return          Criteria instance related to a join type of the current entity class.
     */
    <T> Criteria<T, T> where(Class<T> clazz, JoinType joinType);

    /**
     * Create a query selection for an Entity attribute.
     * @param attribute Attribute to show up in the result selection
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    <X> QuerySelection<E, X> attribute(SingularAttribute<E, X> attribute);

    /**
     * Create a query selection for the
     * {@link javax.persistence.criteria.CriteriaBuilder#abs(javax.persistence.criteria.Expression)}
     * over an attribute.
     * @param attribute Attribute to use in the aggregate.
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    <N extends Number> QuerySelection<E, N> abs(SingularAttribute<E, N> attribute);

    /**
     * Create a query selection for the
     * {@link javax.persistence.criteria.CriteriaBuilder#avg(javax.persistence.criteria.Expression)}
     * over an attribute.
     * @param attribute Attribute to use in the aggregate.
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    <N extends Number> QuerySelection<E, N> avg(SingularAttribute<E, N> attribute);

    /**
     * Create a query selection for the
     * {@link javax.persistence.criteria.CriteriaBuilder#count(javax.persistence.criteria.Expression)}
     * over an attribute.
     * @param attribute Attribute to use in the aggregate.
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    <N extends Number> QuerySelection<E, N> count(SingularAttribute<E, N> attribute);

    /**
     * Create a query selection for the
     * {@link javax.persistence.criteria.CriteriaBuilder#max(javax.persistence.criteria.Expression)}
     * over an attribute.
     * @param attribute Attribute to use in the aggregate.
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    <N extends Number> QuerySelection<E, N> max(SingularAttribute<E, N> attribute);

    /**
     * Create a query selection for the
     * {@link javax.persistence.criteria.CriteriaBuilder#min(javax.persistence.criteria.Expression)}
     * over an attribute.
     * @param attribute Attribute to use in the aggregate.
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    <N extends Number> QuerySelection<E, N> min(SingularAttribute<E, N> attribute);

    /**
     * Create a query selection for the
     * {@link javax.persistence.criteria.CriteriaBuilder#neg(javax.persistence.criteria.Expression)}
     * over an attribute.
     * @param attribute Attribute to use in the aggregate.
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    <N extends Number> QuerySelection<E, N> neg(SingularAttribute<E, N> attribute);

    /**
     * Create a query selection for the
     * {@link javax.persistence.criteria.CriteriaBuilder#sum(javax.persistence.criteria.Expression)}
     * over an attribute.
     * @param attribute Attribute to use in the aggregate.
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    <N extends Number> QuerySelection<E, N> sum(SingularAttribute<E, N> attribute);

    /**
     * Create a query selection for the
     * {@link javax.persistence.criteria.CriteriaBuilder#mod(javax.persistence.criteria.Expression, Integer)}
     * for an attribute.
     * @param attribute Attribute to use in the aggregate.
     * @param modulo    Modulo what.
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    QuerySelection<E, Integer> modulo(SingularAttribute<E, Integer> attribute, Integer modulo);

    /**
     * Create a query selection for the
     * {@link javax.persistence.criteria.CriteriaBuilder#upper(javax.persistence.criteria.Expression)}
     * over a String attribute.
     * @param attribute Attribute to uppercase.
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    QuerySelection<E, String> upper(SingularAttribute<E, String> attribute);

    /**
     * Create a query selection for the
     * {@link javax.persistence.criteria.CriteriaBuilder#lower(javax.persistence.criteria.Expression)}
     * over a String attribute.
     * @param attribute Attribute to lowercase.
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    QuerySelection<E, String> lower(SingularAttribute<E, String> attribute);

    /**
     * Create a query selection for the
     * {@link javax.persistence.criteria.CriteriaBuilder#substring(javax.persistence.criteria.Expression, int)}
     * over a String attribute.
     * @param attribute Attribute to create a substring from.
     * @param from      Substring start.
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    QuerySelection<E, String> substring(SingularAttribute<E, String> attribute, int from);

    /**
     * Create a query selection for the
     * {@link javax.persistence.criteria.CriteriaBuilder#substring(javax.persistence.criteria.Expression, int, int)}
     * over a String attribute.
     * @param attribute Attribute to create a substring from.
     * @param from      Substring start.
     * @param length    Substring length.
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    QuerySelection<E, String> substring(SingularAttribute<E, String> attribute, int from, int length);

    /**
     * Create a query selection for the {@link javax.persistence.criteria.CriteriaBuilder#currentDate()}.
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    QuerySelection<E, Date> currDate();

    /**
     * Create a query selection for the {@link javax.persistence.criteria.CriteriaBuilder#currentTime()}.
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    QuerySelection<E, Time> currTime();

    /**
     * Create a query selection for the {@link javax.persistence.criteria.CriteriaBuilder#currentTimestamp()}.
     * @return          {@link QuerySelection} part of a {@link Criteria#select(Class, QuerySelection...)} call.
     */
    QuerySelection<E, Timestamp> currTStamp();

}
  • No labels