JPQL

The JPA standard specification has a description of JPQL, which allows developers to write queries for entities and their persistent state. JPQL is designed to be portable from one JPA provider to another. These JPQL are guaranteed to work regardless of the underlying database.

As with many technologies, especially when the Java EE platform is moving to the cloud, some features of JPQL are achievable and others are not. This might sound like a contradiction in terms, but it is very important when your business is deciding whether to invest in the cloud computing vendor. One of the most important facets of information technology is storing large and exceptionally huge quantities in multiple stores. The next part is deciding how to access and then analyze it for the results.

So JPQL is a large specification in itself, and this chapter only gives the essentials of how to get started. For JPQL, see the JPA specification for the query language syntax. JPQL is very similar to the native SQL language by design.

There are two forms of the JPQL queries, namely dynamic and named.

The dynamic queries

A dynamic query JPQL is a query that is generated from java.lang.String. We have already seen this example earlier in the chapter.

A dynamic JPQL example from the earlier discussion on composite keys is as follows:

@PersistenceContext entityManager;

public List<ContactConsumer1> findContactConsumers(long id)
throws Exception {
  Query query = entityManager.createQuery("SELECT c from "+ ContactConsumer1.class.getSimpleName()+ " as c where c.contactId = :contactId");
  query.setParameter("contactId", id);
  return query.getResultList();
  }

We create javax.persistence.Query from the entity manager with the JPQL syntax. To achieve better portability, we take the simple class name of the entity. The String also defines a JPQL WHERE clause to filter out the record with the matching contact ID, and return the list collection as a result to the caller.

JPQL for this business method looks like the following code:

SELECT c FROM ContactConsumer1 as c
WHERE c.contactId = :contactId

We can refer to the entity name by its class name, ContactConsumer1. If there is an ambiguity, we could use the fully qualified class name with the package name too.

The parameters in JPQL are prefixed with a colon (:) character as we can see with the token, :contactId. This is an example of a named parameter.

If there are no matching records in the database, we get an empty; otherwise, the caller will get one entity. We assume the contact record is unique in the database table.

The named queries

In the JPA programming, the named JPQL queries are annotated on the entity bean itself. This is called creating static queries on an entity bean. We define static queries using the annotation @javax.persistence.NamedQuery on the entity bean concerned. Most of the time you define more than one named static query on an entity, and therefore you use @javax.persistence.NamedQueries, which takes one argument: an array of the NamedQuery annotations.

It is best to illustrate this with an example code. The context is that we have a requirement to create the entity bean for running a Java user group for our location and territory, and we need to define some portable queries to find the members quickly, concisely, and efficiently.

We can use a single NamedQueries annotation with an array of the NameQuery annotations for this purpose. Here is the simplified code to a JUGMember entity bean. We have removed the cruft methods to make it easier to understand.

@Entity
@NamedQueries( {
  @NamedQuery(name = "JUGMember-findAllMembers", query = "select object(m) from JUGMember m"), @NamedQuery(name = "JUGMember-findByFirstAndLastName", query = "select object(m) from JUGMember m " + "where m.firstname = :firstname and " + "m.lastname = :lastname"), @NamedQuery(name = "JUGMember-findByLanguage", query = "select object(m) from JUGMember m "+ "where m.language = :language")})
public class JUGMember implements Serializable {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private long memberId;
  private String firstname;
  private String lastname;
  private String language;
  private String country;
  private int experience;
  
  // The usual JPA cruft methods omitted
  }

Here we have a JUGMember entity bean with three static named queries: JUGMember-findAllMembers, JUGMember-findByFirstAndLastName, and JUGMember-findByLanguage.

Inside the @NamedQuery annotations, an entity may declare as many JPQL statements as required. There is one important restriction: the names must be unique across the entire persistence unit, hence the long names, in our opinion, will be pragmatic and descriptive.

The query parameters

The named queries are great and would be useless if we could not use them in a program. In order to use a defined static query, we only require the entity manager. The entity manager has a special method named createNamedQuery that allows the session bean to reference a static named query defined in the persistence unit.

The restriction is that the code injecting EntityManager must be defined with the actual entity beans so that the queries can be located inside the persistence context.

A sample extract of a stateful session bean that illustrates how to access the named queries is as follows:

@Stateful
public class JUGMemberServiceBean {
  @PersistenceContext(unitName = "testDatabase", type = PersistenceContextType.EXTENDED)
  private EntityManager entityManager;
  // save() and delete() omitted
  
  public List<JUGMember> findAllMembers() {
    return entityManager.createNamedQuery("JUGMember-findAllMembers")
    .getResultList();
    }
  
  public List<JUGMember> findByFirstAndLastName(String firstname, String lastname) {
    return entityManager.createNamedQuery("JUGMember-findByFirstAndLastName")
    .setParameter("firstname", firstname)
    .setParameter("lastname",lastname)
    .getResultList();
    }
  
  public List<JUGMember> findByLanguage(String language) {
    return entityManager.createNamedQuery("JUGMember-findByLanguage")
    .setParameter("language", language)
    .getResultList();
    }
  }

The so-called finder methods of the session bean each reference the static query by name. For instance, the method findByLanguage() invokes the entity manager to create the static query named JUG-findByLanguage, which creates a query instance. Next, it sets the named parameter language to the method's only argument. It then executes the query with an invocation of getResultList().

The positional query arguments

JPQL also supports the positional arguments, which are probably very familiar to those of you who invested in Java before the turn of the century, in particular JDBC 1.0 had these too.

The positional arguments are identified by the (?) question mark followed by an index number. Let's look at the entity bean JUGMember class again with one additional name query with the positional argument:

@Entity
@NamedQueries( {
  /* ... as before ... */, @NamedQuery(name = "JUGMember-findByExperience", query = "select object(m) from JUGMember m "+ "where m.experience = ?1")
  })
public class JUGMember implements Serializable {
  /* ... as before ... */,
  }

There is an additional name query named JUG-findByExperience that uses a positional argument ?1. This query allows us to search each JUG member for the number of years of professional IT experience. The positional arguments start with the index of 1 just like in JDBC.

The corresponding implementation method in the session bean for the additional named query is as follows:

@Stateful
public class JUGMemberServiceBean {
  /* ... as before ... */,
  public List<JUGMember> findByExperience(int experience) {
    return entityManager.createNamedQuery("JUGMember-findByExperience")
    .setParameter(1, experience)
    .getResultList();
    }
  }

The only difference in this code and the previous is in the Query.setParameter call, which takes an integer parameter indicating the position in the JPQL statement.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.244.250