java - inner - Alternativas de Hibernate Union
hibernate union query example (9)
¿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.
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 hibernate realiza contra las tablas / columnas.
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 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 configuraciones de clases intermedias especiales, por lo que el código que llama a la consulta nombrada 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 puede ver aquí, la consulta con nombre comienza a parecer un montón atemorizante como una declaración del sindicato:
@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 usted 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);
Tal vez tuve un problema más directo para resolver. Mi ''por ejemplo'' estaba en JPA con Hibernate como el proveedor de JPA.
Dividí los tres seleccionados (dos en un segundo caso) en selección múltiple y combiné las colecciones devueltas por mí mismo, reemplazando efectivamente a ''unión de todo''.
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 que informaron que funciona bien ... Después de un día de rebuscar con él, terminé portando mi HQL de regreso 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 que tuve problemas) con la unión en HQL.
por ejemplo, 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 hacerlo 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 típicamente devuelve una Lista o Conjunto ... puede hacer list_1.addAll (list_2). Totalmente apesta en comparación con una unión, pero debería funcionar.
Use 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.
Plain 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 vacía de la sesión de Hibernate, la consulta JDBC no lo encontrará. Podría ser muy desconcertante a veces.
Yo también he pasado por este dolor: si la consulta se genera dinámicamente (por ejemplo, Hibernate Criteria), entonces no pude encontrar una manera práctica de hacerlo.
La buena noticia para mí fue que solo estaba investigando una 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í.
Puedes usar id in (select id from ...) or id in (select id from ...)
por ejemplo, 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 hacerlo
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");
Sin embargo, al menos con MySQL, se encontrará con problemas de rendimiento más adelante. A veces es más fácil hacer que un pobre se una 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á el resultado EXPLAIN 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 que es más importante, 1. row no usa ningún índice y considera 200k + filas. ¡Malo! La ejecución de esta consulta tomó 0.7s donde ambas subconsultas están en milisegundos.
Como dijo Patrick, agregar una LISTA de cada SELECCIÓN sería una buena idea, pero recuerda que actúa como UNION TODO . 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 debería preocuparse es que si tiene JOIN en cada SELECT , el resultado sería una lista de matriz de objetos ( List<Objetc[]>
) por lo que debe iterar sobre ella para mantener solo el objeto que necesita. .
Espero que funcione.