I seem to have missed something in JPA's criteria API and its type safety. Consider the following code:
@Entity
@Access(FIELD)
class User(
@Id
Long id;
@Column(unique=true)
String email;
String password;
}
Here the meta model:
@StaticMetamodel(User.class)
public static class User_ {
public static volatile SingularAttribute<User, Long> id;
public static volatile SingularAttribute<User, String> email;
public static volatile SingularAttribute<User, String> password;
}
Then some code to exercise the class, built using pages from the Java EE Tutorial:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> user = cq.from(User.class);
cq.select(user);
cq.where(cb.equal(user.get(User_.email), "[email protected]")); //this line is my problem
TypedQuery<User> q = em.createQuery(cq);
List<User> allUsers = q.getResultList();
assertEquals(1, allUsers.size());
It works fine. However, if I change the "where" clause to use an Integer instead of a String ("[email protected]"), I expected the code to not compile. Yet it compiles fine.
I thought the criteria API was supposed to be type safe? That is hardly more type safe than the following, using standard JPQL. I mean, what is the purpose of the meta model in the above code?? I have gained nothing from it.
User u = em.createQuery("select u from User u where u.email = :email", User.class)
.setParameter("email", "[email protected]")
.getSingleResult();
So the question is: can I make the criteria API query more type safe, so that I can only pass a String to the "from" clause?
Advertisements. The Criteria API is a predefined API used to define queries for entities. It is the alternative way of defining a JPQL query. These queries are type-safe, and portable and easy to modify by changing the syntax.
The Criteria API is used to define queries for entities and their persistent state by creating query-defining objects. Criteria queries are written using Java programming language APIs, are typesafe, and are portable. Such queries work regardless of the underlying data store.
The Criteria API allows us to build up a criteria query object programmatically, where we can apply different kinds of filtration rules and logical conditions. Since Hibernate 5.2, the Hibernate Criteria API is deprecated, and new development is focused on the JPA Criteria API.
Type-safe queries refer to a writing database query statement mechanism that allows developers to verify the correctness of database query statements at compile time. Normally developers write database queries in string literal style therefore compiler has no way to check if the statements are incorrect.
Type safety is restricted to the upper bound of the generic type of the Expression<T>
interface, not the exact type defined in the metamodel. So, because
CriteriaBuilder.equal(Expression<?> x, java.lang.Object y)
takes an argument of type Expression<?>
it allows to pass any object for comparison.
The other CriteriaBuiler
methods are more type safe, for example CriteriaBuilder.ge(Expression<? extends java.lang.Number> x, Expression<? extends java.lang.Number> y)
allows only numbers. But allows to compare an integer field with a float for example.
You can't do better than this. The methods should have been something like CriteriaBuilder.equal(Expression<T> x, T y)
with T being the field type from the metamodel.
Of course this is a hole in the type safety of the criteria API. I don't know why the JPA API creators choosed the wildcard version of these methods.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With