JPA Criteria API and type safety

criteria-api, java, jpa, type-safety

Solution

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.

Problem

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), "john@google.com")); //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 ("john@google.com"), 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", "john@google.com") .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?

Original source