Spring Data JPA greatly simplifies persistence layer development, but in practice there are many areas where advanced dynamic queries are required

Criteria API

Criteria queries are based on the concept of a metamodel defined for the managed entities of a concrete persistence unit, which can be an entity class, an embedded class, or the parent of a map.

CriteriaQuery Interface: Represents a specific top-level query object that contains the various parts of the query, such as SELECT, FROM, WHERE, Group by, Order BY, etc. Note: CriteriaQuery objects work only for Criteria queries of entity types or embedded types

Root interface: Represents the Root object of the Criteria query, which defines the entity type for future navigation to obtain the desired results, similar to the FROM clause in SQL queries

1: The Root instance is typed and defines the types that can appear in the FROM clause of the query.

2: The query root instance can be obtained by passing an entity type to the AbstractQuery.from method.

3: Criteria queries, which can have multiple query roots.

4: AbstractQuery is the parent class of the CriteriaQuery interface, which provides methods for obtaining the query root. CriteriaBuilder interface: the builder object used to build a CritiaQuery. Predicate: A simple or complex Predicate type that acts as a condition or combination of conditions

If the compiler can perform syntactic correctness checks on the query, then the query is type-safe for Java objects. Version 2.0 of the Java™Persistence API (JPA) introduces the Criteria API, which first introduces type-safe queries into Java applications and provides a mechanism for dynamically constructing queries at run time.

The JPA metamodel

In JPA, standard queries are based on the concept of a metamodel. A metamodel is defined for the managed entities of a concrete persistence unit. These entities can be entity classes, embedded classes, or the parent classes of the map. The classes that provide meta-information about managed entities are metamodel classes.

The biggest advantage of using metamodel classes is that instantiation allows access to persistent attributes of entities at compile time. This feature makes criteria queries more type-safe.

Item_ is the metamodel corresponding to the Item entity class

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Item.class)
public abstract class Item_ {
   public static volatile SingularAttribute<Item, Integer> itemId;
   public static volatile SingularAttribute<Item, String> itemName;
   public static volatile SingularAttribute<Item, Integer> itemStock;
   public static volatile SingularAttribute<Item, Integer> itemPrice;
}Copy the code

Instead of creating such a metamodel manually, add a plug-in to Maven, and the @Entity annotated class will automatically generate the corresponding metamodel after compilation

<! Hibernate JPA automatic metamodel generation
<! -- Related dependencies -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-jpamodelgen</artifactId>
            <version>5.2.10. The Final</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.1.0. The Final</version>
        </dependency>Copy the code
<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
       <configuration>
           <source>1.8</source>
             <target>1.8</target>
              <compilerArguments>
                 <processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
              </compilerArguments>
         </configuration>
 </plugin>Copy the code

There is more than one method, see Chapter 2. Usage

Query simple Demo using Criteria

@Service
public class ItemServiceImpl implements ItemService {

    @Resource
    private EntityManager entityManager;

    @Override
    public List<Item> findByConditions(String name, Integer price, Integer stock) {
          Create the CriteriaBuilder security query factory
        // A CriteriaBuilder is a factory object, the beginning of a security query. Used to build JPA security queries.
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        // Create a CriteriaQuery security query subject clause
        CriteriaQuery objects must work on Criteria queries on entity types or embedded types.
        CriteriaQuery<Item> query = criteriaBuilder.createQuery(Item.class);
        //Root defines the types that can appear in the From clause of the query
        Root<Item> itemRoot = query.from(Item.class);
          //Predicate filters conditions that can build a WHERE sentence
          // Select * from List
        List<Predicate> predicatesList = new ArrayList<>();
          //name fuzzy query,like statement
        if(name ! =null) {
            predicatesList.add(
                    criteriaBuilder.and(
                            criteriaBuilder.like(
                                    itemRoot.get(Item_.itemName), "%" + name + "%")));
        }
         ItemPrice <= statement
        if(price ! =null) {
            predicatesList.add(
                    criteriaBuilder.and(
                            criteriaBuilder.le(
                                    itemRoot.get(Item_.itemPrice), price)));
        }
        //itemStock greater than or equal to >= statement
        if(stock ! =null) {
            predicatesList.add(
                    criteriaBuilder.and(
                            criteriaBuilder.ge(
                                    itemRoot.get(Item_.itemStock), stock)));
        }
          //where() concatenates query conditions
        query.where(predicatesList.toArray(new Predicate[predicatesList.size()]));
        TypedQuery<Item> typedQuery = entityManager.createQuery(query);
        List<Item> resultList = typedQuery.getResultList();
        returnresultList; }}Copy the code

The statements corresponding to each method in the criteriaBuilder

equle : filed = value

gt / greaterThan : filed > value

lt / lessThan : filed < value

ge / greaterThanOrEqualTo : filed >= value

le / lessThanOrEqualTo: filed <= value

notEqule : filed ! = value

like : filed like value

notLike : filed not like value

It would feel like too much trouble to write this for every dynamic query.

In fact, when using Spring Data JPA, as long as our Repo layer interface inherits the JpaSpecificationExecutor interface we can use Specification for dynamic queries, Let’s take a look at the JpaSpecificationExecutor interface:

public interface JpaSpecificationExecutor<T> {
    T findOne(Specification<T> var1);

    List<T> findAll(Specification<T> var1);

    Page<T> findAll(Specification<T> var1, Pageable var2);

    List<T> findAll(Specification<T> var1, Sort var2);

    long count(Specification<T> var1);
}Copy the code

There is an important interface Specification here

public interface Specification<T> {
    Predicate toPredicate(Root
       
         var1, CriteriaQuery
         var2, CriteriaBuilder var3)
       ;
}Copy the code

This interface has only one method, which returns the data structure of the dynamic query and is used to construct the SQL for various dynamic queries

Specification Interface Example

public Page<Item> findByConditions(String name, Integer price, Integer stock, Pageable page) {
     Page<Item> page = itemRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicatesList = new ArrayList<>();
            //name fuzzy query,like statement
            if(name ! =null) {
                predicatesList.add(
                        criteriaBuilder.and(
                                criteriaBuilder.like(
                                        root.get(Item_.itemName), "%" + name + "%")));
            }
            ItemPrice <= statement
            if(price ! =null) {
                predicatesList.add(
                        criteriaBuilder.and(
                                criteriaBuilder.le(
                                        root.get(Item_.itemPrice), price)));
            }
            //itemStock greater than or equal to >= statement
            if(stock ! =null) {
                predicatesList.add(
                        criteriaBuilder.and(
                                criteriaBuilder.ge(
                                        root.get(Item_.itemStock), stock)));
            }
            return criteriaBuilder.and(
                    predicatesList.toArray(new Predicate[predicatesList.size()]));
        }, page);
    return page;
}Copy the code

This is because the Specification

parameter in the findAll(Specification

var1, Pageable var2) method is an anonymous inner class

So here we can use lambda expressions to simplify the code directly.

Writing this way, it is much simpler than securely querying factories using CriteriaBuilder.

Call:

Page<Item> itemPageList = findByConditions("Car".300.null.new PageRequest(1.10));Copy the code

The Specification

interface and metamodel of JPA are used to implement dynamic query.

FindByConditions = findByConditions = findByConditions = findByConditions = findByConditions = findByConditions = findByConditions = findByConditions = findByConditions The simpler the better, of course.

The next article will cover a more convenient use of JPASpecification. The original link: Spring – Data – JPA criteria queries | fire yao