Pregunta

¿Qué alternativas tengo para implementar una consulta de unión usando hibernate? Sé que hibernate no admite consultas sindicales en este momento, en este momento, la única forma en que veo hacer un sindicato es usar una tabla de vista.

La otra opción es usar jdbc simple, pero de esta manera perdería todas mis ventajas de consultas de ejemplo / criterios, así como la validación de mapeo de hibernate que hibernate realiza contra las tablas / columnas.

¿Fue útil?

Solución

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

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

Otros consejos

Puedes usar id in (select id from ...) or id in (select id from ...)

p. en lugar de no trabajar

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

podrías 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 usando MySQL, se encontrará con problemas de rendimiento con él más adelante. A veces es más fácil unirse a un hombre pobre en dos consultas:

// 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 que una compleja.

EDITAR:

para dar un ejemplo, aquí está la salida EXPLICAR de la consulta MySQL resultante de la solución de 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)

Lo más importante: 1. fila no 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.

Tengo que estar de acuerdo con Vladimir. Yo también estudié el uso de UNION en HQL y no pude encontrar una forma de evitarlo. Lo extraño fue que pude encontrar (en las preguntas frecuentes de Hibernate) que UNION no es compatible, informes de errores relacionados con UNION marcados como 'arreglados', grupos de noticias de personas que dicen que las declaraciones se truncarían en UNION y otros grupos de noticias de personas que informan que funciona multa... Después de un día de muck 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.

Tengo una solución para un escenario crítico (por el cual luché mucho) con la unión en HQL.

p. En lugar de no funcionar: -

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 

TÚ podrías 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 puedes agregar ambas listas - >

l1.addAll(l2);

Una vista es un mejor enfoque, pero dado que hql generalmente devuelve una Lista o un Conjunto ... puede hacer list_1.addAll (list_2). Es una mierda en comparación con un sindicato, pero debería funcionar.

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

Dividí las tres selecciones (dos en un segundo caso) en selección múltiple y combiné las colecciones devueltas, reemplazando efectivamente una 'unión de todo'.

Yo también he pasado por este dolor: si la consulta se genera dinámicamente (por ejemplo, Criterios de Hibernación), entonces no podría encontrar una forma práctica de hacerlo.

La buena noticia para mí fue que solo estaba investigando el sindicato 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 usando un conjunto) mientras era fea (especialmente porque también quería hacer paginación de resultados) fue adecuada para mí.



Como dijo Patrick, agregar una LISTA de cada SELECCIONAR sería una buena idea, pero recuerde que actúa como UNION ALL . Para evitar este efecto secundario, solo controle si el objeto ya se agregó en la colección final o no. Si no, entonces agrégalo.
Algo más que debe preocuparte es que si tienes UNIR en cada SELECCIONAR , el resultado sería una lista de matriz de objetos ( Listar < Objetc [] > ) por lo que debe iterar sobre ella para mantener solo el objeto que necesita.

Espero que funcione.

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

Tengo algunas configuraciones de clases intermedias especiales, por lo que el código que llama a la consulta nombrada es breve y atractivo, pero puedes usar cualquier método que uses normalmente junto con las consultas nombradas para ejecutar tu consulta.

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

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

Como puede ver aquí, la consulta nombrada comienza a parecerse mucho a una declaración de 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 ella. En mi caso, lo llamé dual1 porque mi base de datos es Oracle, pero no creo que importe lo que se llama la tabla ficticia.

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

No olvides insertar tu registro ficticio:

SQL> insert into dual1 values (1);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top