2010-09-09 16 views
6

Estoy en una posición en la que nuestra empresa tiene un servicio de búsqueda de bases de datos que es altamente configurable, por lo que es muy útil para configurar consultas de forma programática. La API de Criteria es potente, pero cuando uno de nuestros desarrolladores refactoriza uno de los objetos de datos, las restricciones de los criterios no indicarán que están rotos hasta que ejecutemos nuestras pruebas unitarias o, peor aún, en vivo y en nuestro entorno de producción. Recientemente, tuvimos un proyecto de refactorización esencialmente doble en tiempo de trabajo inesperadamente debido a este problema, una brecha en la planificación del proyecto que, si hubiéramos sabido cuánto tiempo realmente tomaría, probablemente hubiéramos tomado un enfoque alternativo.¿Cómo superar las limitaciones de los criterios de Hibernate y las API de ejemplo?

Me gustaría utilizar el API de ejemplo para resolver este problema. El compilador de Java puede indicar en voz alta que nuestras consultas están modificadas si estamos especificando las condiciones 'donde' en las propiedades reales de POJO. Sin embargo, solo hay mucha funcionalidad en la API de ejemplo y es limitante de muchas maneras. Tomemos el ejemplo siguiente

Product product = new Product(); 
product.setName("P%"); 
Example prdExample = Example.create(product); 
prdExample.excludeProperty("price"); 
prdExample.enableLike(); 
prdExample.ignoreCase(); 

Aquí, la propiedad "nombre" se está consultando contra (donde nombre como 'P%'), y si tuviera que quitar o cambiar el nombre del campo "name", que sabría al instante . Pero, ¿qué pasa con el "precio" de la propiedad? Se está excluyendo porque el objeto Producto tiene un valor predeterminado para él, por lo que estamos pasando el nombre de la propiedad "precio" a un filtro de exclusión. Ahora, si se eliminó el "precio", esta consulta sería sintácticamente inválida y usted no lo sabría hasta el tiempo de ejecución. COJO.

Otro problema - ¿y si añadimos una segunda cláusula where:

product.setPromo("Discounts up to 10%"); 

Debido a la llamada a enableLike(), este ejemplo coincidirá en el texto promocional, "Descuentos de hasta el 10%", sino también "Descuentos de hasta 10,000,000 de dólares" o cualquier otra cosa que coincida. En general, las modificaciones de toda la consulta del objeto Ejemplo, como enableLike() o ignoreCase() no siempre serán aplicables a cada propiedad que se comprueba.

Aquí hay un tercer y más importante problema: ¿qué hay de otros criterios especiales? No hay forma de obtener cada producto con un precio superior a $ 10 utilizando el marco de ejemplo estándar. No hay forma de pedir resultados por promoción, descendiendo. Si el objeto Producto se unió a algún fabricante, tampoco hay forma de agregar un criterio al objeto fabricante relacionado. No hay forma de especificar con seguridad FetchMode en los criterios para el fabricante (aunque esto es un problema con la API Criteria en general, las relaciones buscadas no válidas fallan silenciosamente, incluso más una bomba de tiempo)

Por todo lo anterior Por ejemplo, debería volver a la API de Criteria y usar representaciones de cadenas de propiedades para realizar la consulta, una vez más, eliminando el mayor beneficio de las consultas de Ejemplo.

¿Qué alternativas existen para la API de ejemplo que puede obtener el tipo de asesoramiento en tiempo de compilación que necesitamos?

+0

Esto es algo muy interesante, pero stackoverflow no es un foro de discusión, no recibirá comentarios aquí y su pregunta probablemente se cerrará en su forma actual. Como se menciona en las preguntas frecuentes, * "también está perfectamente bien hacer y responder su propia pregunta, siempre y cuando finja que está en Jeopardy: frasee en forma de pregunta" *. Sugiero reformularlo y publicar los detalles como respuesta. –

+0

@Pascal gracias, voy a seguir adelante y volver a formatear la pregunta –

+0

De nada. Y gracias por publicar esto. –

Respuesta

5

Mi compañía brinda a los desarrolladores días en los que podemos experimentar y trabajar en proyectos favoritos (al estilo de Google) y pasé un tiempo trabajando en un framework para usar consultas de ejemplo mientras cumplía con las limitaciones descritas anteriormente. He encontrado algo que podría ser útil para otras personas interesadas en consultas de ejemplo también. Aquí hay una muestra del marco usando el ejemplo del Producto.

Criteria criteriaQuery = session.createCriteria(Product.class); 

Restrictions<Product> restrictions = Restrictions.create(Product.class); 
Product example = restrictions.getQueryObject(); 
example.setName(restrictions.like("N%")); 
example.setPromo("Discounts up to 10%"); 

restrictions.addRestrictions(criteriaQuery); 

He aquí un intento de solucionar los problemas en el ejemplo de código de la cuestión - el problema del valor predeterminado para el campo "precio" ya no existe, ya que este marco requiere que se establecen explícitamente criterios. El segundo problema de tener un enableLike() de toda la consulta se ha ido - el matcher está solo en el campo "nombre".

Los otros problemas mencionados en la pregunta también desaparecen en este marco. Aquí hay ejemplos de implementaciones.

product.setPrice(restrictions.gt(10)); // price > 10 
product.setPromo(restrictions.order(false)); // order by promo desc 
Restrictions<Manufacturer> manufacturerRestrictions 
     = Restrictions.create(Manufacturer.class); 
//configure manuf restrictions in the same manner... 
product.setManufacturer(restrictions.join(manufacturerRestrictions)); 
/* there are also joinSet() and joinList() methods 
    for one-to-many relationships as well */ 

Aún se aplican restricciones más sofisticadas.

product.setPrice(restrictions.between(45,55)); 
product.setManufacturer(restrictions.fetch(FetchMode.JOIN)); 
product.setName(restrictions.or("Foo", "Bar")); 

Después de mostrar el marco de un compañero de trabajo, mencionó que muchos de datos asignada objetos tienen los emisores privados, por lo que este tipo de criterios de ajuste difícil también (un problema diferente con la API de ejemplo!). Por lo tanto, también he explicado eso. En lugar de usar setters, los getters también son consultables.

restrictions.is(product.getName()).eq("Foo"); 
restrictions.is(product.getPrice()).gt(10); 
restrictions.is(product.getPromo()).order(false); 

También he añadido algunas comprobaciones extra en los objetos para garantizar una mejor seguridad de tipo - por ejemplo, los criterios relativos (GT, GE, le, LT) todos requieren un valor? se extiende Comparable para el parámetro. Además, si utiliza un getter en el estilo especificado anteriormente, y hay una anotación @Transient presente en el getter, generará un error de tiempo de ejecución.

¡Pero espera, hay más!

Si te gusta que la utilidad de Restricciones incorporada de Hibernate se puede importar estáticamente, para que puedas hacer cosas como criteria.addRestriction (eq ("nombre", "foo")) sin hacer que tu código sea realmente detallado, hay una opción para eso también.

Restrictions<Product> restrictions = new Restrictions<Product>(){ 
     public void query(Product queryObject){ 
     queryObject.setPrice(gt(10)); 
     queryObject.setPromo(order(false)); 
     //gt() and order() inherited from Restrictions 
     }    
} 

Eso es todo por ahora, ¡muchas gracias de antemano por cualquier comentario! Hemos publicado el código en Sourceforge para aquellos que estén interesados. http://sourceforge.net/projects/hqbe2/

+0

Me gusta. Parece que te inspiraron varios marcos de burla? – waxwing

+0

@waxwing sí, me encanta EasyMock :) –

+0

¿Cómo combinarías las restricciones con o? Como en, "de una persona con edad> 18 o (edad> 16 y Aprobación parental)"? – meriton

1

¡La API se ve genial!

Restrictions.order (boolean) huele a control de acoplamiento. No está claro qué representan los valores del argumento booleano.

Sugiero reemplazar o complementar con orderAscending() y orderDescending().

1

Eche un vistazo a Querydsl. Su módulo JPA/Hibernate requiere generación de código. Su módulo de colecciones Java usa proxies pero no puede ser utilizado con JPA/Hibernate en este momento.

Cuestiones relacionadas