In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
Editor to share with you how to use the JPA 2.0 dynamic query mechanism Criteria API, I believe most people do not know much about it, so share this article for your reference, I hope you can learn a lot after reading this article, let's go to know it!
Since JPA was first introduced in 2006, it has been widely supported by the Java development community. The next major update to the specification-version 2.0 (JSR 317)-will be completed by the end of 2009. One of the key features introduced by JPA 2.0 is Criteria API, which brings a unique ability to the Java language: to develop a query that the Java compiler can verify at run time. Criteria API also provides a mechanism for dynamically building queries at run time.
This article introduces Criteria API and its closely related metamodel (metamodel) concepts. You will learn how to use Criteria API to develop queries that the Java compiler can check for correctness, thereby reducing run-time errors over traditional string-based Java Persistence Query Language (JPQL) queries. With the help of sample queries that use database functions or match template instances, I'll demonstrate the power of programmatic query construction mechanisms and compare them with JPQL queries that use predefined syntax. This article assumes that you have a basic knowledge of Java language programming and knowledge of common JPA uses, such as EntityManagerFactory or EntityManager.
What are the drawbacks of JPQL queries?
JPA 1.0 introduces JPQL, a powerful query language that has largely contributed to the popularity of JPA. However, JPQL, which is string-based and uses limited syntax, has some limitations. To understand one of the main limitations of JPQL, look at the simple code snippet in listing 1, which selects a list of Person older than 20 years old by executing a JPQL query:
Listing 1. A simple (and incorrect) JPQL query
EntityManager em =...; String jpql = "select p from Person where p.age > 20"; Query query = em.createQuery (jpql); List result = query.getResultList ()
This basic example shows the following key aspects of the query execution model in JPA 1.0:
The JPQL query is specified as a String (line 2).
EntityManager is the factory that constructs an executable query instance containing a given JPQL string (line 3).
The result of the query execution contains elements of an untyped java.util.List.
But this simple example has a validation error. The code compiles smoothly, but will fail at run time because the syntax of the JPQL query string is incorrect. The correct syntax for line 2 of listing 1 is:
String jpql = "select p from Person p where p.age > 20"
Unfortunately, the Java compiler cannot find such errors. At run time, the error will appear on line 3 or 4 (the number of lines depends on whether the JPA provider parses the JPQL string according to the JPQL syntax during query construction or execution).
How can type-safe queries help?
One of the biggest advantages of Criteria API is that it forbids the construction of syntactically incorrect queries. Listing 2 rewrites the JPQL query in listing 1 using the CriteriaQuery interface:
Listing 2. The basic steps of writing CriteriaQuery
EntityManager em =... QueryBuilder qb = em.getQueryBuilder (); CriteriaQuery
< Person>C = qb.createQuery (Person.class); Root
< Person>P = c.from (Person.class); Predicate condition = qb.gt (p.get (Person_.age), 20); c.where (condition); TypedQuery
< Person>Q = em.createQuery (c); List
< Person>Result = q.getResultList ()
Listing 2 shows the core construction of Criteria API and its basic usage:
Line 1 gets an instance of EntityManager in one of several available ways.
In line 2, EntityManager creates an instance of QueryBuilder. QueryBuilder is the factory of CriteriaQuery.
In line 3, the QueryBuilder factory constructs an instance of CriteriaQuery. CriteriaQuery is given a generic type. The generic parameter declares the type of result that CriteriaQuery returns when executing. When constructing a CriteriaQuery, you can provide a variety of result type parameters-- from persistent entities (such as Person.class) to more flexible forms of Object [].
Line 4 sets the query expression on the CriteriaQuery instance. Query expressions are core cells or nodes assembled in a tree to specify CriteriaQuery. Figure 1 shows the hierarchy of query expressions defined in Criteria API:
Figure 1. Interface hierarchy in query expressions
First, set CriteriaQuery to query from Person.class. The result returns Root
< Person>Instance p. Root is a query expression that represents the scope of a persistent entity. Root
< T>It actually means: "evaluate this query for all instances of type T." This is similar to the FROM clause of a JPQL or SQL query. One more thing to note, Root
< Person>Is generic (virtually every expression is generic). The type parameter is the type of value that the expression evaluates. So Root
< Person>Represents an expression that evaluates Person.class. Line 5 constructs a Predicate. Predicate is a common form of query expression that evaluates to true or false. Predicates are constructed by QueryBuilder, and QueryBuilder is not only the factory of CriteriaQuery, but also the factory of query expressions. QueryBuilder contains API methods for constructing all query expressions supported by traditional JPQL syntax, as well as additional methods. In listing 2, QueryBuilder is used to construct an expression that evaluates whether the value of the first expression parameter is greater than that of the second parameter. The method signature is:
Predicate gt (Expression
< ? extends Number>X, Number y)
This method signature is a good example of using strongly typed languages (such as Java) to define API that can check correctness and prevent errors. The method signature specifies that only an expression with a value of Number can be compared with another expression whose value is also Number (for example, it cannot be compared with an expression with a value of String):
Predicate condition = qb.gt (p.get (Person_.age), 20)
Line 5 has more knowledge. Notice the first input parameter of the qb.gt () method: p.get (Person_.age), where p is the previously obtained Root
< Person>Expression. P.get (Person_.age) is a path expression. A path expression is the result of navigating from the root expression through one or more persistent attributes. Therefore, the expression p.get (Person_.age) means to navigate from the root expression p using the age attribute of Person. You may not understand what Person_.age is. For the time being, you can think of it as a way to represent the age property of Person. I will explain Person_.age in detail when talking about the new Metamodel API introduced in JPA 2.0.
As mentioned earlier, each query expression is generic to represent the type of value evaluated by the expression. If the age attribute in Person.class is declared as type Integer (or int), the expression p.get (Person_.age) evaluates to type Integer. Because of type-safe inheritance in API, the editor itself throws errors for meaningless comparisons, such as:
Predicate condition = qb.gt (p.get (Person_.age, "xyz"))
Line 6 sets the predicate to its WHERE clause on CriteriaQuery.
In line 7, EntityManager creates an executable query with input as CriteriaQuery. This is similar to constructing an executable query that is input as a JPQL string. But because the input CriteriaQuery contains more type information, the result is TypedQuery, which is an extension of the familiar javax.persistence.Query. As its name suggests, TypedQuery knows the type of result it returns when executed. It is defined as follows:
Public interface TypedQuery
< T>Extends Query {List
< T>GetResultList ();}
Contrary to the corresponding untyped hyperinterface:
Public interface Query {List getResultList ();}
Obviously, the TypedQuery result has the same Person.class type, which is specified by the QueryBuilder when constructing the input CriteriaQuery (line 3).
In line 8, when the query is finally executed to get a list of results, the type information carried shows its advantage. The result is a Person list with types, which saves developers the hassle of casting when traversing the generated elements (while reducing ClassCastException runtime errors).
Now summarize the basic aspects of the simple example in listing 2:
CriteriaQuery is a tree of query expression nodes. In traditional string-based query languages, these expression nodes are used to specify query clauses, such as FROM, WHERE, and ORDER BY. Figure 2 shows the clauses related to the query:
Figure 2. CriteriaQuery encapsulates the clauses of traditional queries
Query expressions are given generics. Some typical expressions are:
Root
< T>Which is equivalent to a FROM clause
Predicate, which evaluates to a Boolean true or false (in fact, it is declared as interface Predicate extends Expression
< Boolean>).
Path
< T>, indicating that from the Root
< ?>The persistence property that the expression navigates to. Root
< T>Is a special Path without a parent class
< T>.
QueryBuilder is the factory for CriteriaQuery and various query expressions.
The CriteriaQuery is passed to an executable query and the type information is retained so that the elements of the select list can be accessed directly without any runtime cast.
Metamodel of persistence domain
When discussing listing 2, we point out an unusual construct: Person_.age, which represents the persistence property age of Person. Listing 2 uses Person_.age to form a path expression from Root through p.get (Person_.age)
< Person>The expression p is navigated. Person_.age is a public static field in the Person_ class, and Person_ is a static, instantiated specification metamodel class that corresponds to the original Person entity class.
The metamodel class describes the metadata of the persistence class. If a class installs the JPA 2.0 specification to accurately describe the metadata of persistent entities, then the metamodel class is canonical. The canonical metamodel class is static, so all of its member variables are declared static (also public). Person_.age is one of the static member variables. You can instantiate a canonical class by generating a concrete Person_.java in the source code at development time. Once instantiated, it can refer to Person's persistence properties in a strongly typed manner during compilation.
This Person_metamodel class is an alternative to referencing the meta-information of Person. This approach is similar to Java Reflection API, which is often used (some might think it is abusive), but is conceptually very different. You can use reflection to get meta-information about an instance of java.lang.Class, but you cannot reference meta-information about Person.class in a way that the compiler can check. For example, when using reflection, you would reference the age field in Person.class as follows:
Field field = Person.class.getField ("age")
However, this approach also has significant limitations, similar to those found in the string-based JPQL query in listing 1. The compiler can compile the code smoothly, but is not sure whether it will work properly. If the code contains any error input, it is sure to fail at run time. Reflection does not implement the function of JPA 2.0's type-safe query API.
The type-safe query API must enable your code to reference the persistence property age in the Person class, while allowing the compiler to check for errors during compilation. The solution provided by JPA 2.0 is to instantiate a metamodel class named Person_ (corresponding to Person) by statically exposing the same persistence properties.
Discussions about meta-information are usually drowsy. So I'll show a concrete metamodel class example (domain.Person) for the familiar Plain Old Java Object (POJO) entity class, as shown in listing 3:
Listing 3. A simple persistent entity
Package domain;@Entitypublic class Person {@ Id private long ssn; private string name; private int age; / / public gettter/setter methods public String getName () {...}}
This is a typical definition of POJO and contains annotations (such as @ Entity or @ Id), allowing JPA providers to manage instances of this class as persistent entities.
Listing 4 shows the corresponding static canonical metamodel class for domain.Person:
Listing 4. A canonical metamodel of a simple entity
Package domain;import javax.persistence.metamodel.SingularAttribute;@javax.persistence.metamodel.StaticMetamodel (domain.Person.class) public class Person_ {public static volatile SingularAttribute
< Person,Long>Ssn; public static volatile SingularAttribute
< Person,String>Name; public static volatile SingularAttribute
< Person,Integer>Age;}
The metamodel class declares each persistent attribute of the original domain.Person entity as of type SingularAttribute
< Person,?>Gets or sets the static public field of the By leveraging this Person_ metamodel class, you can reference domain.Person 's persistence property age at compile time-not through Reflection API, but directly to the static Person_.age field. The compiler can then perform type checking based on the type declared by the age property. I have given an example of such restrictions: QueryBuilder.gt (p.get (Person_.age), "xyz") will cause a compiler error because the compiler uses QueryBuilder.gt (..) The type of signature and Person_.age determines that the age property of Person is a numeric field and cannot be compared with String.
Other key points to note include:
The metamodel Person_.age field is declared as type javax.persistence.metamodel.SingularAttribute. SingularAttribute is one of the interfaces defined in JPA Metamodel API, which I'll describe in the next section. SingularAttribute
< Person, Integer>Indicates that the class declares the type of the original persistent property and the persistent property itself
The metamodel class is annotated as @ StaticMetamodel (domain.Person.class) to mark it as a metamodel class corresponding to the original persistent domain.Person entity.
Metamodel API
I define a metamodel class as a description of a persistent entity class. Just as Reflection API needs other interfaces (such as java.lang.reflect.Field or java.lang.reflect.Method) to describe the composition of java.lang.Class, JPA Metamodel API needs other interfaces (such as SingularAttribute and PluralAttribute) to describe the types of metamodel classes and their properties.
Figure 3 shows the interface defined in Metamodel API to describe the type:
Figure 3. Hierarchical structure of persistent type interfaces in Metamodel API
Figure 4 shows the interface defined in Metamodel API to describe properties:
Figure 4. Hierarchical structure of interfaces for persistent properties in Metamodel API
JPA's Metamodel API interface is more specialized than Java Reflection API. More nuances are needed to express rich meta-information about persistence. For example, Java Reflection API represents all Java types as java.lang.Class. That is, concepts are not distinguished by independent definitions, such as classes, abstract classes, and interfaces. Of course, you can ask Class whether it is an interface or an abstract class, but this is different from expressing the difference between an interface and an abstract class through two separate definitions.
Java Reflection API was introduced when the Java language was born (which used to be a very cutting-edge concept for a common multipurpose programming language), but it took years of development to realize the usefulness and power of strongly typed systems. JPA Metamodel API introduces strong typing into persistent entities. For example, persistent entities are semantically divided into MappedSuperClass, Entity, and Embeddable. Prior to JPA 2.0, this semantic distinction was represented by corresponding class-level annotations in persistent class definitions. JPA Metamodel describes three separate interfaces (MappedSuperclassType, EntityType, and EmbeddableType) in the javax.persistence.metamodel package to contrast their semantic features more sharply. Similarly, persistent properties can be distinguished at the type definition level by interfaces such as SingularAttribute, CollectionAttribute, and MapAttribute.
In addition to being easy to describe, these specialized metamodel interfaces have practical advantages to help build type-safe queries to reduce run-time errors. You saw some of the advantages in the previous example, and as I describe the example of connectivity through CriteriaQuery, you will see more advantages.
Runtime scope
In general, you can compare the traditional interface of Java Reflection API with the interface of javax.persistence.metamodel that is specifically used to describe persistent metadata. To make a further analogy, you need to use the equivalent runtime scope concept for the metamodel interface. The scope of the java.lang.Class instance is divided by java.lang.ClassLoader at run time. A set of mutually referenced Java class instances must be defined under the ClassLoader scope. The boundaries of the scope are strict or closed, and if a class A defined under the ClassLoader L scope tries to reference a class B that is not within the scope of ClassLoader L, you will receive a terrible ClassNotFoundException or NoClassDef FoundError (complicating the problem for developers or deployers dealing with environments with multiple ClassLoader).
A strict set of mutually referenced classes is now called a runtime scope, while in JPA 1.0 it is a persistence unit. The persistence entity of the persistence unit scope is found in the META-INF/persistence.xml file
< class>Enumerate in the clause. In JPA 2.0, developers can use scopes at run time through the javax.persistence.metamodel.Metamodel interface. The Metamodel interface is a container for all persistent entities known to a particular persistence unit, as shown in figure 5:
Figure 5. A metamodel interface is a container of types in a persistence unit
This interface allows access to metamodel elements through the corresponding persistent entity class of the metamodel elements. For example, to get a reference to the persistence metadata of an Person persistent entity, you can write:
EntityManagerFactory emf =...; Metamodel metamodel = emf.getMetamodel (); EntityType
< Person>PClass = metamodel.entity (Person.class)
This is an analogy of getting Class through ClassLoader using the name of the class:
ClassLoader classloader = Thread.currentThread () .getContextClassLoader (); Class
< ?>Clazz = classloader.loadClass ("domain.Person")
You can browse EntityType at run time
< Person>Gets the persistent attribute declared in the Person entity. If the application calls a method on pClass (such as pClass.getSingularAttribute ("age", Integer.class), it will return a SingularAttribute
< Person, Integer>Most importantly, the properties that an application can reference at run time through Metamodel API are provided to the Java compiler by instantiating the static specification metamodel Person_ class.
In addition to decomposing persistent entities into corresponding metamodel elements, Metamodel API also allows access to all known metamodel classes (Metamodel.getManagedTypes ()) or through class persistence information, such as embeddable (Address.class), which returns an EmbeddableType
< Address>Instance (ManagedType
< >The subinterface of
Persistence meta-information is divided into two main categories: persistence (such as @ Entity) and mapping (such as @ Table). In JPA 2.0, the metamodel captures metadata only for persistence annotations (not mapping annotations). Therefore, with the current version of Metamodel API, you can know which fields are persistent, but you cannot find the database columns to which they are mapped.
Norms and non-norms
If an application developer writes metamodel classes, these classes are called irregular metamodels. Currently, the specification of the non-canonical metamodel is not very detailed, so support for the irregular metamodel cannot be portable between JPA providers. You may have noticed that public static fields are only declared in the canonical metamodel and are not initialized. After the declaration, you can reference these fields when developing CriteriaQuery. However, they must be assigned values at run time to make sense. Although it is the responsibility of the JPA provider to assign values to fields in the canonical metamodel, this requirement does not exist in the non-canonical metamodel. Applications that use a non-canonical metamodel must rely on specific vendor mechanisms or develop their own mechanisms to initialize the field values of metamodel properties at run time.
Annotation processing and metamodel generation
If you have many persistent entities, it is only natural that you will not write metamodel classes yourself. The persistence provider should generate these metamodel classes for you. This tool or generation mechanism is not mandatory in the specification, but there is a private agreement between JPA that they will use the Annotation Processor tool integrated in the Java 6 compiler to generate the specification metamodel. Apache OpenJPA provides a tool to generate these metamodel classes either implicitly when you compile the source code for the persistent entity or by explicitly calling the script. Before Java 6, there was a widely used Annotation Processor tool called apt, but in Java 6, the merging of compiler and Annotation Processor was defined as part of the standard.
To generate these metamodel classes in OpenJPA like a persistence provider, simply compile the POJO entity using the OpenJPA class library in the compiler's classpath:
$javac domain/Person.java
The canonical metamodel Person_ class will be generated, which will be located in the same directory as Person.java and will be part of the compilation.
Write type-safe queries
So far, I have built the components of CriteriaQuery and related metamodel classes. Now, I'll show you how to develop some queries using Criteria API.
Function expression
A function expression applies a function to one or more input parameters to create a new expression. The type of function expression depends on the nature of the function and the type of its parameters. The input parameter itself can be an expression or a text value. The compiler's type-checking rules are combined with API signatures to determine what is legal input.
Consider a single parameter expression that applies an average to the input expression. CriteriaQuery selects the average balance of all Account, as shown in listing 5:
Listing 5. Function expressions in CriteriaQuery
CriteriaQuery
< Double>C = cb.createQuery (Double.class); Root
< Account>A = c.from (Account.class); c.select (cb.avg (a.get (Account_.balance)
The equivalent JPQL query is:
String jpql = "select avg (a.balance) from Account a"
In listing 5, the QueryBuilder factory (represented by the variable cb) creates an avg () expression and uses it in the select () clause of the query.
The query expression is a building block that can be assembled to define the final selection predicate for the query. The example in listing 6 shows the Path expression created by navigating to the balance of Account, and the Path expression is then used as the input expression for two binary function expressions (greaterThan () and lessThan ()), both of which result in a Boolean expression or a predicate. Then, merge the predicate through the and () operation to form the final choice predicate, which is evaluated by the query's where () clause:
Listing 6. The where () predicate in CriteriaQuery
CriteriaQuery
< Account>C = cb.createQuery (Account.class); Root
< Account>Account = c.from (Account.class); Path
< Integer>Balance = account.get (Account_.balance); c.where (cb.and (cb.greaterThan (balance, 100), cb.lessThan (balance), 200))
The equivalent JPQL query is:
"select a from Account a where a.balance > 100 and a.balance
< 200"; 符合谓词 某些表达式(比如 in())可以应用到多个表达式。清单 7 给出了一个例子: 清单 7. CriteriaQuery 中的多值表达式 CriteriaQuery< Account>C = cb.createQuery (Account.class); Root
< Account>Account = c.from (Account.class); Path
< Person>Owner = account.get (Account_.owner); Path
< String>Name = owner.get (Person_.name); c.where (cb.in (name) .value ("X"). Value ("Y"). Value ("Z"))
This example navigates from Account in two steps to create a path that represents the name of the account owner. It then creates an in () expression that uses the path expression as input. The in () expression evaluates whether its input expression is equal to one of its parameters. These parameters are found in the In through the value () method
< T>Specified on the expression, In
< T>The signature is as follows:
In
< T>Value (T value)
Note how to use Java generics to specify that In is calculated only for members of type T of value
< T>Expression. Because the path expression that represents the name of the Account owner is of type String, it is valid to compare with a parameter of type String, which can be a literal or another expression that evaluates to String.
Compare the query in listing 7 with the equivalent (correct) JPQL:
"select a from Account a where a.owner.name in ('Xerogramme, Yee, Z')."
A slight oversight in JPQL not only goes unchecked by the editor, it can also lead to unexpected results. For example:
"select a from Account a where a.owner.name in (X, Y, Z)"
Connection relationship
Although the examples in listing 6 and listing 7 use expressions as building blocks, queries are based on an entity and its attributes. But queries usually involve multiple entities, which requires you to join multiple entities together. CriteriaQuery joins two entities through a type join expression. A type connection expression has two type parameters: the type of the connection source and the bindable type of the connection target property. For example, if you want to query a Customer that has one or more PurchaseOrder not emitted, you need to connect the Customer to the PurchaseOrder through an expression, where Customer has an orders type named java.util.Set
< PurchaseOrder>, as shown in listing 8
Listing 8. Connect multi-valued attributes
CriteriaQuery
< Customer>Q = cb.createQuery (Customer.class); Root
< Customer>C = q.from (Customer.class); SetJoin
< Customer, PurchaseOrder>O = c.join (Customer_.orders)
The join expression is created from the root expression c, and the persistence attribute Customer.orders is parameterized by the bindable type of the connection source (Customer) and the Customer.orders attribute, which is PurchaseOrder rather than the declared type java.util.Set
< PurchaseOrder>. Also note that because the initial attribute is of type java.util.Set, the generated join expression is SetJoin, which is specific to the Join of the attribute whose type is declared as java.util.Set. Similarly, for other supported multi-valued persistent attribute types, the API defines CollectionJoin, ListJoin, and MapJoin. (figure 1 shows the various join expressions). No explicit conversion is required in line 3 of listing 8, because CriteriaQuery and Metamodel API can identify and distinguish attribute types declared as java.util.Collection or List or Set or Map by overriding join ().
Use joins to form a predicate on a join entity in a query. Therefore, if you want to select a Customer with one or more unsent PurchaseOrder, you can navigate from the connection expression o through the state property, compare it to the DELIVERED state, and negate the predicate:
Predicate p = cb.equal (o.get (PurchaseOrder_.status), Status.DELIVERED) .negate ()
One thing to note about creating a join expression is that each time you join an expression, a new expression is returned, as shown in listing 9:
Listing 9. Create a unique instance for each connection
SetJoin
< Customer, PurchaseOrder>O1 = c.join (Customer_.orders); SetJoin
< Customer, PurchaseOrder>O2 = c.join (Customer_.orders); assert o1 = O2
The equality assertion in listing 9 for two join expressions from the same expression c will fail. Therefore, if the predicate of the query involves an unsent PurchaseOrder with a value greater than $200, the correct construction is to join the PurchaseOrder to the root Customer expression (only once), assign the generated join expression to the local variable (equivalent to the scope variable in JPQL), and use the local variable when constructing the predicate.
Use parameter
Review the initial JPQL query in this article (the correct one):
String jpql = "select p from Person p where p.age > 20"
Although queries are usually written with constant text values, this is not a good practice. It is a good practice to parameterize the query so that it is parsed or prepared only once, and then cached and reused. Therefore, the best way to write a query is to use named parameters:
String jpql = "select p from Person p where p.age >: age"
The parameterized query binds the value of the parameter before the query executes:
Query query = em.createQuery (jpql) .setParameter ("age", 20); List result = query.getResultList ()
In a JPQL query, the parameters in the query string are encoded either named (preceded by a colon, for example: age) or positional (preceded by a question mark, for example,? 3). In CriteriaQuery, the parameters themselves are query expressions. Like other expressions, they are strongly typed and constructed by expression factories (that is, QueryBuilder). You can then parameterize the query in listing 2, as shown in listing 10:
Listing 10. Using parameters in CriteriaQuery
ParameterExpression
< Integer>Age = qb.parameter (Integer.class); Predicate condition = qb.gt (p.get (Person_.age), age); c.where (condition); TypedQuery
< Person>Q = em.createQuery (c); List
< Person>Result = q.setParameter (age, 20). GetResultList ()
Compare this parameter use with the parameter use in JPQL: the parameter expression is created as Integer with explicit type information and is directly used to bind the value 20 to the executable query. Additional type information is useful for reducing run-time errors because it prevents parameters from being compared to expressions that contain incompatible types, or by preventing parameters from binding to values of incompatible types. The parameters of an JPQL query do not provide any compile-time security.
The example in listing 10 shows an unnamed expression that is directly used for binding. You can also assign a second name to the parameter during the construction of the parameter. In this case, you can use this name to bind the parameter value to the query. However, you cannot use position parameters. Integer positions in linear JPQL query strings make sense, but cannot be used in CriteriaQuery contexts where the conceptual model is a query expression tree.
Another interesting aspect of JPA query parameters is that they have no internal values. Values are bound to parameters in the context of an executable query. Therefore, you can legally create two independently executable queries from the same CriteriaQuery and bind two integer values to the same parameters of those executable queries.
Prediction result
You have seen that the result returned by CriteriaQuery on execution has been specified when QueryBuilder constructs the CriteriaQuery. The result of the query is specified as one or more prediction conditions. You can specify prediction conditions on the CriteriaQuery interface in one of two ways:
CriteriaQuery
< T>Select (Selection
< ? extends T>Selection); CriteriaQuery
< T>Multiselect (Selection
< ?>... Selections)
The simplest and most commonly used prediction condition is to query candidate classes. It can be implicit, as shown in listing 11:
Listing 11. Candidate sections selected by default by CriteriaQuery
CriteriaQuery
< Account>Q = cb.createQuery (Account.class); Root
< Account>Account = q.from (Account.class); List
< Account>Accounts = em.createQuery (Q) .getResultList ()
In listing 11, the query from Account does not explicitly specify its selection criteria, and is the same as the candidate class that was explicitly selected. Listing 12 shows a query that uses explicit selection criteria:
Listing 12. CriteriaQuery using a single explicit selection condition
CriteriaQuery
< Account>Q = cb.createQuery (Account.class); Root
< Account>Account = q.from (Account.class); q.select (account); List
< Account>Accounts = em.createQuery (Q) .getResultList ()
If the predicted result of the query is not the candidate persistence entity itself, then several other constructions can be used to generate the results of the query. These constructors are included in the QueryBuilder interface, as shown in listing 13:
Listing 13. The method of generating query results
< Y>CompoundSelection
< Y>Construct (Class
< Y>Result, Selection
< ?>... Terms); CompoundSelection
< Object[]>Array (Selection
< ?>... Terms); CompoundSelection
< Tuple>Tuple (Selection
< ?>... Terms)
The method in listing 13 builds a prediction condition consisting of several other optional expressions. The construct () method creates an instance of the given class parameter and calls a constructor with the value from the input selection condition. For example, if CustomerDetails-a non-persistent entity-has a constructor that accepts String and int parameters, then CriteriaQuery can return CustomerDetails as its result by creating an instance from the selected Customer-a persistent entity-the name and age of the instance, as shown in listing 14:
Listing 14. Put the query result package into the instance of the class through construct ()
CriteriaQuery
< CustomerDetails>Q = cb.createQuery (CustomerDetails.class); Root
< Customer>C = q.from (Customer.class); q.select (cb.construct (CustomerDetails.class, c.get (Customer_.name), c.get (Customer_.age))
Multiple prediction conditions can be combined to form a composite condition that represents Object [] or Tuple. Listing 15 shows how to wrap the results in Object []:
Listing 15. Wrap the result into Object []
CriteriaQuery
< Object[]>Q = cb.createQuery (Object [] .class); Root
< Customer>C = q.from (Customer.class); q.select (cb.array (c.get (Customer_.name), c.get (Customer_.age)); List
< Object[]>Result = em.createQuery (Q) .getResultList ()
This query returns a list of results, each of which is an Object [] of length 2, the name of the 0th array element is the name of Customer, and the first array element is the age of Customer.
Tuple is a JPA definition interface that represents a row of data. Conceptually, Tuple is a list of TupleElement-where TupleElement is derived from the unit and the root of all query expressions. The values contained in the Tuple can be accessed by an integer index based on 0 (similar to the familiar JDBC result), by an alias of TupleElement, or directly through TupleElement. Listing 16 shows how to wrap the results in Tuple:
Listing 16. Wrap the query results into Tuple
CriteriaQuery
< Tuple>Q = cb.createTupleQuery (); Root
< Customer>C = q.from (Customer.class); TupleElement
< String>Tname = c.get (Customer_.name) .alias ("name"); q.select (cb.tuple (tname, c.get (Customer_.age) .alias ("age"); List
< Tuple>Result = em.createQuery (Q). GetResultList (); String name = result.get (0). Get (name); String age = result.get (0). Get (1)
This query returns a list of results, each of which is a Tuple. In turn, each tuple has two elements-accessible by the index or alias (if any) of each TupleElement, or directly by TupleElement. The two things to note in listing 16 are the use of alias (), which is a way to bind a name to a query expression (to create a new copy), and the createTupleQuery () method on QueryBuilder, which is just a substitute for createQuery (Tuple.class).
The behavior of these methods that can change the results and the results of the type parameters specified as CriteriaQuery during construction together constitute the semantics of the multiselect () method. This method interprets its input conditions according to the result type of the CriteriaQuery that finally implements the result. To construct a CustomerDetails instance using multiselect () as in listing 14, you need to specify the type of CriteriaQuery as CustomerDetails, and then call multiselect () with the conditions that will make up the CustomerDetails constructor, as shown in listing 17:
Listing 17. Multiselect () interpretation condition based on result type
CriteriaQuery
< CustomerDetails>Q = cb.createQuery (CustomerDetails.class); Root
< Customer>C = q.from (Customer.class); q.multiselect (c.get (Customer_.name), c.get (Customer_.age))
Because the query result type is CustomerDetails,multiselect (), its prediction condition is interpreted as a CustomerDetails constructor parameter. If the query is specified to return Tuple, the multiselect () method with the same parameters creates an instance of Tuple, as shown in listing 18:
Listing 18. Create a Tuple instance using the multiselect () method
CriteriaQuery
< Tuple>Q = cb.createTupleQuery (); Root
< Customer>C = q.from (Customer.class); q.multiselect (c.get (Customer_.name), c.get (Customer_.age))
The behavior of multiselect () becomes more interesting if you use Object as the result type or if you do not specify a type parameter. In these cases, if multiselect () uses a single input condition, the return value will be the selected condition. But if multiselect () contains more than one input condition, the result is an Object [].
Advanced featur
So far, I've focused on Criteria API's strong typing and how it can help reduce semantic errors in string-based JPQL queries. Criteria API is also a mechanism for programmatically building queries, so it is often referred to as dynamic query API. The power of programmatic queries to construct API is infinite, but its utilization also depends on the creativity of users. I will show you four examples:
Building dynamic queries using weakly typed API
Use the functions supported by the database as query expressions to extend the syntax
Edit query to realize the function of "search in results"
Query by example-a pattern familiar to the database community
Weak typing and dynamic query construction
Criteria API's strongly typed checking is based on the availability of instantiated metamodel classes during the open period. In some cases, however, the selected entity can only be decided at run time. To support this usage, the Criteria API method provides a side-by-side version in which persistent properties are referenced by their names (similar to Java Reflection API) rather than by instantiating static metamodel properties. This side-by-side version of the API can truly support dynamic query construction at the expense of compile-time type checking. Listing 19 rewrites the code in listing 6 using weakly typed API:
Listing 19. Weakly typed query
Class
< Account>Cls = Class.forName ("domain.Account"); Metamodel model = em.getMetamodel (); EntityType
< Account>Entity = model.entity (cls); CriteriaQuery
< Account>C = cb.createQuery (cls); Root
< Account>Account = c.from (entity); Path
< Integer>Balance = account.
< Integer>Get ("balance"); c.where (cb.and (cb.greaterThan (balance, 100), cb.lessThan (balance), 200)
However, the weakly typed API cannot return the correct generic expression, so an editor is generated to warn against unchecked conversions. One way to eliminate these annoying warning messages is to use a tool that is not commonly used in Java generics: parameterized method calls, such as getting path expressions by calling the get () method in listing 19.
Extensible database expression
The unique advantage of dynamic query construction mechanism is that its syntax is extensible. For example, you can use the function () method in the QueryBuilder interface to create expressions supported by the database:
< T>Expression
< T>Function (String name, Class
< T>Type, Expression
< ?>... args)
The function () method creates an expression with a given name and 0 or more input expressions. The function () expression evaluates to the given type. This allows the application to create a query that calculates the database. For example, the MySQL database supports the CURRENT_USER () function, which returns a UTF-8 string consisting of a user name and host name for the MySQL account that the server uses to authenticate the current client. Applications can use the CURRENT_USER () function without arguments in CriteriaQuery, as shown in listing 20:
Listing 20. Using database-specific functions in CriteriaQuery
CriteriaQuery
< Tuple>Q = cb.createTupleQuery (); Root
< Customer>C = q.from (Customer.class); Expression
< String>CurrentUser = cb.function ("CURRENT_USER", String.class, (Expression
< ?>[]) null); q.multiselect (currentUser, c.get (Customer_.balanceOwed))
Note that an equivalent query cannot be expressed in JPQL because its syntax only supports a fixed number of expressions. Dynamic API is not strictly limited by a fixed number of expressions.
Editable query
You can edit the CriteriaQuery programmatically. You can change the clause of a query, such as its selection condition, the selection predicate in the WHERE clause, and the sort condition in the ORDER BY clause. You can use this editing feature in the typical search in results tool to add more restrictions to further refine the query predicate in the next steps.
The example in listing 21 creates a query that sorts the results by name, and then edits the query to query by zip code:
Listing 21. Edit CriteriaQuery
CriteriaQuery
< Person>C = cb.createQuery (Person.class); Root
< Person>P = c.from (Person.class); c.orderBy (cb.asc (p.get (Person_.name); List
< Person>Result = em.createQuery (c) .getResultList (); / / start editingList
< Order>Orders = c.getOrderList (); List
< Order>NewOrders = new ArrayList
< Order>(orders); newOrders.add (cb.desc (p.get (Person_.zipcode); c.orderBy (newOrders); List
< Person>Result2 = em.createQuery (c) .getResultList ()
The setter method on CriteriaQuery-select (), where (), or orderBy ()-replaces the previous value with a new parameter. The list returned by the corresponding getter method (such as getOrderList ()) is not active, that is, adding or removing elements from the return list does not cause CriteriaQuery; to be modified. In addition, some vendors even return immutable lists to prevent accidental use. Therefore, it is good practice to copy the return list to a new list before adding and removing new expressions.
Query according to examples
Another useful feature in dynamic query API is that it can easily support queries based on examples. Querying by example (developed by IBM ®Research in 1970) is often referenced as an early example of software end-user usability. The idea of querying based on examples uses template instances instead of specifying precise predicates for the query. With a given template instance, a federated predicate is created, where each predicate is a non-null and non-default attribute value of the template instance. Executing the query evaluates the predicate to find all instances that match the template instance. The query based on the example was considered to be added to JPA 2.0, but in the end it was not added. OpenJPA supports this query through its extended OpenJPAQueryBuilder interface, as shown in listing 22:
Listing 22. Use CriteriaQuery of OpenJPA to query according to the example
CriteriaQuery
< Employee>Q = cb.createQuery (Employee.class); Employee example = new Employee (); example.setSalary (10000); example.setRating (1); q.where (q.from (Employee.class), example)
As this example shows, OpenJPA's QueryBuilder interface extension supports the following expressions:
Public
< T>Predicate qbe (From
< ?, T>From, T template)
This expression generates a joint predicate based on the property values of the given template instance. For example, this query will query all Employee with a salary of 10000 and a rating of 1. To further control the comparison, you can specify optional properties that are not used for the comparison, and specify the comparison method for the property whose value is String.
These are all the contents of this article entitled "how to use JPA 2.0 dynamic query mechanism Criteria API". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.