viernes, 3 de febrero de 2012

CriteriaQuery de JPA vs Criteria de Hibernate vs QueryDSL

Estuve leyendo sobre la API de Criteria de JPA, y la verdad es que no me gusto.

Vamos por parte porque usar esta API? Porque nos provee la seguridad por tipo; escribiendo hql o ejbql por ejemplo tenemos el problema que al modificar un objeto; las consultas no se enteran y por lo tanto pueden explotar. Este problema se podría atacar haciendo una buena batería de test y ir corriéndola con maven por ejemplo. Pero lo ideal es que al modificar la clase se rompa donde esta siendo usada esa propiedad de esta manera el programa no compila hasta que no solucione todo. A la vez las herramientas de refactor me podrían ayudar modificando las consultas.

Veamos un ejemplo de criteria con JPA:

CriteriaQuery criteria = builder.createQuery( Person.class );
Root personRoot = criteria.from( Person.class );
criteria.select( personRoot );
criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) );
List people = em.createQuery( criteria ).getResultList();


Esto en hql seria: from Person p where p.eyeColor = 'brown' (un poco mas fácil) Pero porque tanta cosa? Veamo el ejemplo de JPA, antes que nada necesitamos crear un builder de la query luego con ese builder creamos la query donde le decimos lo que vamos a consultar y que vamos a seleccionar y luego con el builder agregamos la condición. Cuando agregamos la condición (si fueron observadores) vieron que llama a una clase Person_ esta clase es la que contiene la metainformación de la clase y obviamente hay que programarla seria algo así:

import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.StaticMetamodel;

@StaticMetamodel( Person.class )
public class Person_ {
    public static volatile SingularAttribute id;
    public static volatile SingularAttribute name;
        public static volatile SingularAttribute eyeColor;
    public static volatile SingularAttribute age;
    public static volatile SingularAttribute address;
    public static volatile SetAttribute orders;
}


Luego de hacer todo eso no me acuerdo que estoy consultando. Hay cosas que muy fácilmente se podrían mejorar como por ejemplo no puede suponer que lo que consultamos es lo que debe seleccionar? Todo hay que decirle!

Veamos como se puede hacer la consulta con Criteria de Hibernate:

List people = sess.createCriteria(Person .class).add( Restrictions.eq("eyeColor", "brown") ); 

Bastante más fácil pero se puede ver un pequeño problema si yo cambio en mi clase la propiedad "eyeColor" esta consulta va a seguir compilando pero va explotar cuando la ejecute. Ahora veamos como podríamos hacer esto con QueryDSL:

JPAQuery query = new JPAQuery(em);
QPerson p = new Qperson("p");
query.from(p).where(p.eyeColor.eq("brown"));


QueryDSL genera un tipo para consultar con el nombre Qperson que sería como este:

public class QPerson extends EntityPathBase{
…
}


Como conclusión podemos decir que es complejo hacer consultas de forma tipada y que queden bien es más complejo aun. De igual forma la API de Criteria de JPA se podria mejorar mucho más. Y esto se ve cuando miramos proyectos que nacieron para simplificar esta API como QueryDSL.