Pergunta

Que alternativas que eu tenho que implementar uma consulta de união usando hibernate? Eu sei de hibernação não suporta consultas de união no momento, agora a única maneira que vejo para fazer uma união é usar uma tabela vista.

A outra opção é usar JDBC simples, mas desta forma eu iria perder todos os meus exemplo / critérios consultas guloseimas, bem como a validação de mapeamento do Hibernate que executa hibernação contra as tabelas / colunas.

Foi útil?

Solução

Use VIEW. As mesmas classes pode ser mapeada para diferentes tabelas / vistas usando nome da entidade, para que você não vai mesmo ter muita duplicação. Estar lá, feito isso, funciona OK.

Plain JDBC tem outro problema oculto: é inconsciente de cache de sessão do Hibernate, por isso, se algo ficou em cache até o fim da transação e não corou de sessão do Hibernate, consulta JDBC não vai encontrá-lo. Poderia ser muito intrigante às vezes.

Outras dicas

Você pode usar id in (select id from ...) or id in (select id from ...)

por exemplo. em vez de não-trabalho

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

você poderia fazer

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");

Pelo menos usando MySQL, você vai correr em problemas de desempenho com ele mais tarde, no entanto. Às vezes é mais fácil fazer um homem pobre de se juntar em duas consultas em vez disso:

// 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);

Muitas vezes é melhor fazer duas consultas simples de um complexo.

EDIT:

para dar um exemplo, aqui é o resultado da consulta MySQL resultante da solução subselect EXPLICAR:

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)

O mais importante, 1. linha não usa qualquer índice e considera 200k + linhas. Ruim! A execução desta consulta levou 0,7s wheres ambos os subqueries estão nos milésimos de segundo.

Eu tenho que concordar com Vladimir. Eu também olhei para usando UNION em HQL e não poderia encontrar uma maneira de contornar isso. O estranho era que eu poderia encontrar (no Hibernate FAQ) que a união não é suportado, relatórios de erros relativos à UNIÃO marcado 'fixo', grupos de notícias de pessoas dizendo que as declarações seriam truncado no UNION, e outros grupos de notícias de pessoas relatando funciona bem... Após um dia de mucking com ele, acabei portabilidade do meu HQL volta para SQL simples, mas fazê-lo em uma exibição no banco de dados seria uma boa opção. No meu caso, partes da consulta foram gerados dinamicamente, então eu tive que construir o SQL no código em vez disso.

Eu tenho uma solução para um cenário crítico (para o qual eu me esforcei muito) com o sindicato em HQL.

por exemplo. Em vez de não trabalhar: -

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

ou

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

você poderia fazer em 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();

então u pode adicionar tanto list ->

l1.addAll(l2);

A vista é uma abordagem melhor, mas desde hql normalmente retorna um List ou Set ... você pode fazer list_1.addAll (list_2). Totalmente suga em comparação com uma união, mas deve funcionar.

Talvez eu tive um problema mais simples e direta para resolver. Meu 'por exemplo,' estava em JPA com Hibernate como o provedor JPA.

Eu divido os três seleciona (dois em um segundo caso) em várias selecionados e combinados das coleções voltou a mim mesmo, efetivamente substituindo uma 'união all'.

Eu também tenho passado por essa dor - se a consulta é gerado dinamicamente (por exemplo, critérios Hibernate), então eu não poderia encontrar uma maneira prática de fazê-lo.

A boa notícia para mim foi que eu só estava investigando união para resolver um problema de desempenho quando se usa um 'ou' numa base de dados Oracle.

A solução Patrick postou (combinando os resultados por meio de programação usando um conjunto), enquanto feio (especialmente desde que eu queria fazer resultados paging também) foi adequado para mim.


Como Patrick disse, acrescentando o LISTA s de cada SELECT seria uma boa idéia, mas lembre-se que ele age como UNION ALL . Para evitar este efeito colateral, apenas controlar se o objecto já é adicionado no final da colheita ou não. Se não, em seguida, adicioná-lo.
Outra coisa que você deve se preocupar é que se você tiver qualquer Junte-se em cada SELECT , o resultado seria uma lista de matriz de objeto ( List<Objetc[]> ) então você tem que iterar sobre ele para manter apenas o objeto que você precisa.
Espero que funcione.

Aqui é um caso especial, mas pode inspirá-lo a criar o seu próprio em torno do trabalho. O objetivo aqui é para contar o número total de registros de duas tabelas diferentes onde os registros de atender a critérios específicos. Eu acredito que esta técnica irá trabalhar para qualquer caso em que você precisa para agregar dados de em várias mesas / fontes.

Eu tenho alguma configuração especial classes intermediárias, para que o código que chama a consulta nomeada é curto e doce, mas você pode usar qualquer método que você usa normalmente em conjunto com consultas nomeadas para executar sua consulta.

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

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

Como você pode ver aqui, a consulta nomeada começa a olhar um lote aweful como uma declaração de união:

@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 da mágica aqui é criar uma tabela fictícia e inserir um registro para ele. No meu caso, eu nomeei-dual1 porque o meu banco de dados é Oracle, mas eu não acho que importa o que você chama tabela de manequim.

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

Não se esqueça de inserir o registro fictício:

SQL> insert into dual1 values (1);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top