UFO ET IT

Hibernate Union 대안

ufoet 2021. 1. 11. 21:12
반응형

Hibernate Union 대안


최대 절전 모드를 사용하여 통합 쿼리를 구현하려면 어떤 대안이 필요합니까? 나는 최대 절전 모드가 현재 통합 쿼리를 지원하지 않는다는 것을 알고 있습니다. 지금 당장은 통합을 만드는 유일한 방법은 뷰 테이블을 사용하는 것입니다.

다른 옵션은 일반 jdbc를 사용하는 것이지만이 방법을 사용하면 테이블 / 열에 대해 최대 절전 모드가 수행하는 최대 절전 모드 매핑 유효성 검사뿐만 아니라 모든 예제 / 기준 쿼리 기능을 잃게됩니다.


VIEW를 사용하십시오. 엔티티 이름을 사용하여 동일한 클래스를 다른 테이블 / 뷰에 매핑 할 수 있으므로 중복이 많지 않습니다. 거기에 있으면 잘 작동합니다.

Plain JDBC에는 또 다른 숨겨진 문제가 있습니다. Hibernate 세션 캐시를 인식하지 못하므로 트랜잭션이 끝날 때까지 캐시되고 Hibernate 세션에서 플러시되지 않은 경우 JDBC 쿼리에서 찾을 수 없습니다. 때때로 매우 혼란 스러울 수 있습니다.


당신은 사용할 수 있습니다 id in (select id from ...) or id in (select id from ...)

예 : 작동하지 않는 대신

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

당신은 할 수 있습니다

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

적어도 MySQL을 사용하면 나중에 성능 문제가 발생합니다. 때로는 두 쿼리에 대해 가난한 사람의 조인을 수행하는 것이 더 쉽습니다.

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

복잡한 쿼리 하나보다 간단한 쿼리 두 개를 수행하는 것이 더 낫습니다.

편집하다:

예를 들어, 다음은 subselect 솔루션의 결과 MySQL 쿼리의 EXPLAIN 출력입니다.

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)

가장 중요한 것은 1. 행은 인덱스를 사용하지 않고 20 만 개 이상의 행을 고려한다는 것입니다. 나쁜! 이 쿼리를 실행하는 데 0.7 초가 걸렸으며 두 하위 쿼리가 모두 밀리 초 단위입니다.


나는 블라디미르에 동의해야합니다. 나도 HQL에서 UNION을 사용하는 방법을 살펴 보았지만 그 방법을 찾지 못했습니다. 이상한 점은 UNION이 지원되지 않는다는 것을 (Hibernate FAQ에서) 찾을 수 있다는 것, UNION과 관련된 버그 보고서가 '수정 됨'으로 표시, UNION에서 문장이 잘릴 것이라고 말하는 사람들의 뉴스 그룹 및 작동한다고보고하는 사람들의 다른 뉴스 그룹 괜찮아요 ... 하루를 보낸 후 HQL을 일반 SQL로 다시 이식했지만 데이터베이스의보기에서 수행하는 것이 좋은 옵션이 될 것입니다. 필자의 경우 쿼리의 일부가 동적으로 생성되었으므로 대신 코드에서 SQL을 빌드해야했습니다.


나는 HQL의 결합과 함께 (내가 많이 고생 한) 하나의 중요한 시나리오에 대한 해결책을 가지고 있습니다.

예 : 작동하지 않는 대신 :-

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

또는

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

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

그런 다음 두 목록을 모두 추가 할 수 있습니다->

l1.addAll(l2);

A view is a better approach but since hql typically returns a List or Set... you can do list_1.addAll(list_2). Totally sucks compared to a union but should work.


Perhaps I had a more straight-forward problem to solve. My 'for instance' was in JPA with Hibernate as the JPA provider.

I split the three selects (two in a second case) into multiple select and combined the collections returned myself, effectively replacing a 'union all'.


I too have been through this pain - if the query is dynamically generated (e.g. Hibernate Criteria) then I couldn't find a practical way to do it.

The good news for me was that I was only investigating union to solve a performance problem when using an 'or' in an Oracle database.

The solution Patrick posted (combining the results programmatically using a set) while ugly (especially since I wanted to do results paging as well) was adequate for me.




As Patrick said, appending the LISTs from each SELECT would be a good idea but remember that it acts like UNION ALL. To avoid this side effect, just control if the object is already added in final collection or not. If no, then add it.
Something else that you should care about is that if you have any JOIN in each SELECT, the result would be a list of object array(List<Objetc[]>) so you have to iterate over it to only keep the object that you need.

Hope it works.


Here is a special case, but might inspire you to create your own work around. The goal here is to count the total number of records from two different tables where records meet a particular criteria. I believe this technique will work for any case where you need to aggregate data from across multiple tables/sources.

I have some special intermediate classes setup, so the code which calls the named query is short and sweet, but you can use whatever method you normally use in conjunction with named queries to execute your query.

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

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

As you can see here, the named query begins to look an aweful lot like a union statement:

@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;
    }
}

Part of the magic here is to create a dummy table and insert one record into it. In my case, I named it dual1 because my database is Oracle, but I don't think it matters what you call the dummy table.

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

Don't forget to insert your dummy record:

SQL> insert into dual1 values (1);

ReferenceURL : https://stackoverflow.com/questions/201023/hibernate-union-alternatives

반응형