2008-10-14 12 views
45

¿Qué alternativas tengo para implementar una consulta de unión utilizando hibernate? Sé que hibernate no es compatible con consultas sindicales en este momento, en este momento la única forma en que veo hacer una unión es usar una tabla de visualización.Hibernate Union alternatives

La otra opción es usar jdbc simple, pero de esta forma perdería todas las bondades de consultas de ejemplo/criterio, así como la validación de mapeo de hibernación que realiza Hibernate contra las tablas/columnas.

+2

Una de las soluciones es utilizar la entidad agregada, como se describe [aquí] (http://stackoverflow.com/questions/ 7322526/hibernate-jpa-es-es-posible-recuperar-entidades heterogéneas-en-una-que/7322738 # 7322738). –

Respuesta

22

Usar VER. Las mismas clases se pueden asignar a diferentes tablas/vistas utilizando el nombre de la entidad, por lo que ni siquiera tendrá mucha duplicación. Estar allí, hecho eso, funciona bien.

Normal JDBC tiene otro problema oculto: no tiene conocimiento de la caché de sesión de Hibernate, por lo que si algo se almacena en caché hasta el final de la transacción y no se descarga de la sesión de Hibernate, la consulta JDBC no lo encontrará. Podría ser muy desconcertante a veces.

+2

Y aún más desconcertante es cuando tienes un caché de segundo nivel en su lugar, pero usa JDBC simple (digamos para informar) desde alguna otra parte de la aplicación. – javashlook

5

Tengo que estar de acuerdo con Vladimir. Yo también investigué el uso de UNION en HQL y no pude encontrar una forma de evitarlo. Lo curioso fue que pude encontrar (en las preguntas frecuentes de Hibernate) que UNION no está respaldado, informes de fallas pertenecientes a UNION marcados como 'fijos', grupos de noticias de personas que decían que las declaraciones se truncarían en UNION y otros grupos de noticias de personas informando que funciona bien ... Después de un día de mucking con él, terminé portando mi HQL de nuevo a SQL simple, pero hacerlo en una vista en la base de datos sería una buena opción. En mi caso, partes de la consulta se generaron dinámicamente, por lo que tuve que construir el SQL en el código.

+1

Gracias por enumerar las diversas teorías que encontró y cuáles no fueron correctas. Las diferencias entre lo que un "desarrollador razonable" podría esperar y las cosas difieren de eso, son algunas de las notas más útiles. –

3

Una vista es un enfoque mejor, pero dado que hql típicamente devuelve una lista o un conjunto ... puede hacer list_1.addAll (list_2). Totalmente apesta en comparación con una unión, pero debería funcionar.

0

Yo también he pasado por este dolor: si la consulta se genera dinámicamente (por ejemplo, Hibernate Criteria), entonces no pude encontrar una forma práctica de hacerlo.

La buena noticia para mí fue que solo estaba investigando la unión para resolver un problema de rendimiento al usar un 'o' en una base de datos Oracle.

La solución que Patrick publicó (combinando los resultados mediante programación utilizando un conjunto) aunque fea (especialmente porque quería hacer paginación de resultados también) fue adecuada para mí.

2

Quizás tuve un problema más sencillo de resolver. Mi 'por ejemplo' estaba en JPA con Hibernate como el proveedor de JPA.

Dividí las tres selecciones (dos en una segunda caja) en selección múltiple y combiné las colecciones devueltas por mí mismo, reemplazando efectivamente una 'unión de todas'.

+2

Esto funciona, pero es menos eficiente. –

54

Usted podría utilizar id in (select id from ...) or id in (select id from ...)

por ejemplo, en lugar de que no funciona

from Person p where p.name="Joe" 
union 
from Person p join p.children c where c.name="Joe" 

que podría hacer

from Person p 
    where p.id in (select p1.id from Person p1 where p1.name="Joe") 
    or p.id in (select p2.id from Person p2 join p2.children c where c.name="Joe"); 

Al menos el uso de MySQL, que se ejecutará en problemas de rendimiento con él más adelante, sin embargo. A veces es más fácil hacer de un hombre pobre se unen en dos consultas en su lugar:

// use set for uniqueness 
Set<Person> people = new HashSet<Person>((List<Person>) query1.list()); 
people.addAll((List<Person>) query2.list()); 
return new ArrayList<Person>(people); 

A menudo es mejor hacer dos consultas simples de un complejo.

EDIT:

Para dar un ejemplo, aquí es el resultado de la consulta MySQL resultante explican desde la solución subselección:

mysql> explain 
    select p.* from PERSON p 
    where p.id in (select p1.id from PERSON p1 where p1.name = "Joe") 
     or p.id in (select p2.id from PERSON p2 
     join CHILDREN c on p2.id = c.parent where c.name="Joe") \G 
*************************** 1. row *************************** 
      id: 1 
    select_type: PRIMARY 
     table: a 
     type: ALL 
possible_keys: NULL 
      key: NULL 
     key_len: NULL 
      ref: NULL 
     rows: 247554 
     Extra: Using where 
*************************** 2. row *************************** 
      id: 3 
    select_type: DEPENDENT SUBQUERY 
     table: NULL 
     type: NULL 
possible_keys: NULL 
      key: NULL 
     key_len: NULL 
      ref: NULL 
     rows: NULL 
     Extra: Impossible WHERE noticed after reading const tables 
*************************** 3. row *************************** 
      id: 2 
    select_type: DEPENDENT SUBQUERY 
     table: a1 
     type: unique_subquery 
possible_keys: PRIMARY,name,sortname 
      key: PRIMARY 
     key_len: 4 
      ref: func 
     rows: 1 
     Extra: Using where 
3 rows in set (0.00 sec) 

que es más importante, la fila 1. No se utiliza ningún índice y considera 200k + filas. ¡Malo! La ejecución de esta consulta tomó 0.7s donde ambas subconsultas están en milisegundos.

4

Tengo una solución para un escenario crítico (por el que tuve problemas mucho) con la unión en HQL.

por ejemplo En lugar de no trabajar: -

select i , j from A a , (select i , j from B union select i , j from C) d where a.i = d.i 

O

select i , j from A a JOIN (select i , j from B union select i , j from C) d on a.i = d.i 

que podría hacer en Hibernate HQL ->

Query q1 =session.createQuery(select i , j from A a JOIN B b on a.i = b.i) 
List l1 = q1.list(); 

Query q2 = session.createQuery(select i , j from A a JOIN C b on a.i = b.i) 
List l2 = q2.list(); 

entonces u puede añadir tanto la lista ->

l1.addAll(l2); 
+1

La unión estará en proceso, no hecha por la base de datos, es lo mismo que la sugerencia de Walt. –

+0

Entonces, ¿y qué pasa si desea ordenar aquí? puede terminar duplicando el mecanismo de clasificación implementando otro algoritmo de combinación. muy mal – Bondax

0



Como dijo Patrick, añadiendo el LISTA s de cada SELECT sería una buena idea, pero recuerde que actúa como UNION ALL. Para evitar este efecto secundario, simplemente controle si el objeto ya está agregado en la colección final o no. Si no, entonces agrégalo.
Otra cosa que debes tener en cuenta es que si usted tiene alguna JOIN en cada SELECT, el resultado sería una lista de tabla de objeto (List<Objetc[]>) así que hay que iterar sobre ella para mantener sólo el objeto Que tu necesitas.

Espero que funcione.

0

Aquí hay un caso especial, pero podría inspirarlo a crear su propio trabajo. El objetivo aquí es contar el número total de registros de dos tablas diferentes donde los registros cumplen un criterio particular. Creo que esta técnica funcionará para cualquier caso en el que necesite agregar datos de múltiples tablas/fuentes.

Tengo algunas clases especiales de configuración intermedia, por lo que el código que llama a la consulta indicada es breve y dulce, pero puede usar cualquier método que utilice normalmente junto con consultas con nombre para ejecutar su consulta.

QueryParms parms=new QueryParms(); 
parms.put("PROCDATE",PROCDATE); 

Long pixelAll = ((SourceCount)Fetch.row("PIXEL_ALL",parms,logger)).getCOUNT(); 

Como se puede ver aquí, la consulta con nombre comienza a parecer una gran cantidad horrible como una declaración de la Unión:

@Entity 
@NamedQueries({ 
     @NamedQuery(
      name ="PIXEL_ALL", 
      query = "" + 
        " SELECT new SourceCount(" + 
        "  (select count(a) from PIXEL_LOG_CURR1 a " + 
        "  where to_char(a.TIMESTAMP, 'YYYYMMDD') = :PROCDATE " + 
        " )," + 
        "  (select count(b) from PIXEL_LOG_CURR2 b" + 
        "  where to_char(b.TIMESTAMP, 'YYYYMMDD') = :PROCDATE " + 
        " )" + 
        ") from Dual1" + 
        "" 
    ) 
}) 

public class SourceCount { 
    @Id 
    private Long COUNT; 

    public SourceCount(Long COUNT1, Long COUNT2) { 
     this.COUNT = COUNT1+COUNT2; 
    } 

    public Long getCOUNT() { 
     return COUNT; 
    } 

    public void setCOUNT(Long COUNT) { 
     this.COUNT = COUNT; 
    } 
} 

Parte de la magia aquí es crear una tabla ficticia e insertar un registro en eso. En mi caso, lo llamé dual1 porque mi base de datos es Oracle, pero no creo que importe lo que usted llama la tabla ficticia.

@Entity 
@Table(name="DUAL1") 
public class Dual1 { 
    @Id 
    Long ID; 
} 

No se olvide de insertar su registro ficticio:

SQL> insert into dual1 values (1);