El uso de JPA Criteria nos aporta muchas ventajas en cuanto a la construcción de SQL Dinámico utilizando JPA . Ahora bien su su sintaxis y su forma de trabajar son bastante diferentes a la forma clásica. Vamos a apoyarnos en el ejemplo anterior de JPA SQL Injection para construir el mismo ejemplo utilizando JPA Criteria . En el ejemplo anterior teníamos el siguiente código:
package com.arquitecturajava; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.TypedQuery; public class Principal { public static void main(String[] args) { seleccionarPersona("pedro", "perez"); } private static void seleccionarPersona(String nombre, String apellidos) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("UnidadPersonas"); EntityManager em = emf.createEntityManager(); try { String sql = "select p from Persona p "; if (nombre != null || apellidos != null) { sql += " where "; } if (nombre != null) { sql += " nombre='" + nombre + "'"; } if (apellidos != null) { if(nombre!=null) { sql += " and apellidos='" + apellidos + "'"; }else{ sql += " apellidos='" + apellidos + "'"; } } TypedQuery<Persona&> consulta = em.createQuery(sql, Persona.class); List<Persona&> lista = consulta.getResultList(); lista.forEach((p) -&> { System.out.println(p.getNombre()); }); } catch (Exception e) { e.printStackTrace(); } finally { em.close(); } } }
El código nos generaba una SQL dinámica pero ya habíamos visto que si cambiamos los parámetros e intentabamos inyectar SQL , podríamos generar un agujero de seguridad.
JPA Criteria API
Para solventar este problema que se genera a partir de la necesidad de construir SQL Dinámico podemos recurrir al API de Criteria y diseñar la consulta con ella.
ppackage com.arquitecturajava; pimport java.util.List; pimport javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; ppublic class Principal2 { public static void main(String[] args) { seleccionarPersonas("pedro","perez"); } private static void seleccionarPersonas(String nome, String apellidos) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("UnidadPersonas"); EntityManager em = emf.createEntityManager(); try { CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<Persona&> consulta = cb.createQuery(Persona.class); Root<Persona&> personas = consulta.from(Persona.class); Predicate p1 = null,p2 = null; if(nome!=null) { p1= cb.equal(personas.get("nome"),"pedro"); } if(apellidos!=null) { p2= cb.equal(personas.get("apellidos"),"perez"); } Predicate nomeApellidos=cb.or(p1,p2); consulta.select(personas).where(nomeApellidos); List<Persona&> lista = em.createQuery(consulta).getResultList(); lista.forEach((p) -&> { System.out.println(p.getNome()); }); } catch (Exception e) { e.printStackTrace(); } finally { em.close(); } } }
¿Como funciona exactamente el API de Criteria? . Este API se encarga de diseñar la consulta a través de un Builder, un patrón de diseño que construye un objeto paso a paso
A través de estos pasos se construye el objeto por complejo verificando que todo es correcto. Este es el enfoque JPA Criteria API , vamos construyendo poco a poco cada trazo.
Para después ejecutar esa consulta construida utilizando el EntityManager:
De tal forma que si invocamos la consulta y pasamos los siguientes parámetros
seleccionarPersonas(“pedro”,”perez”);
La consulta se ejecutara sin problemas :
Como el API de Criteria valida cada parámetro , tampoco tendremos problemas si nos inyectan código no válido.
seleccionarPersonas(“pedro”,“”='”);
El Api de Criteria se encargará de revisarlo y generar la consulta parametrizada correcta.
No sufriremos una inyección de SQL.
Otros artículos relacionados:
Usando este segundo enfoque, si la consulta requiere 2 o mas entidades, como se agregan?
gracias.
Con la clausula join 🙂
Hola, gracias por el articulo…tengo una pregunta.
¿En criteria hay evaluación perezosa?, ¿en que orden??
En… Predicate nombreApellidos=cb.or(p1,p2);
¿Es el primer parametro p1 el que prevalece?, ¿es p1 el que si es evaluado a cierto hace que no se evalúe p2?
No , ya que se va construyendo dinamicamente la query y esta es la que se ejecuta completa
Gracias.. por tan excelentes artículos
De nada 🙂