사용자 도구

사이트 도구


java:querydsl

QueryDSL

프로젝트 이관

Q-Types multi thread loading 시 dead lock

// 미리 초기화를 호출하는게 좋다. packageToLoad 를 변경해준다.
ClassPathUtils.scanPackage(Thread.currentThread().getContextClassLoader(), packageToLoad);

join 성능 향상

  • Join 시 그 결과로 나오는 관계 객체를 Eager Loading 하려면 fetch() 를 사용해야 한다.
from(parent).innerJoin(parent.child).fetch()
....

JPA QueryDSL Cross Join 발생

  • Parent - Child가 1:1 관계일 때 다음과 같이 쿼리했더니 inner join과 cross join이 함께 발생했다.
    QParent parent = QParent.parent;
    QChild child = parent.child;
     
    from(parent).innerJoin(parent.child).fetch()
        .where(parent.something.gt(parent.child.somthing))....;
  • 다음처럼 innerJoin에 PATH를 지정했더니 cross join이 사라졌다.
    QParent parent = QParent.parent;
    QChild child = QChild.child; // 여기 달라짐
     
    from(parent).innerJoin(parent.child, child).fetch() // as 처리가 필요함.
        .where(parent.something.gt(child.somthing))....;

JPA Subquery & JPAExpressions

  • JPA Subquery 는 JPAExpressions.select 를 이용하여 각종 Sub Query 생성가능.
  • SELECT 에서 사용할 때는 ExpressionUtils.as([subquery]) 로 묶어준다.

Native 조건 condition

  • ExpressionsxxxTemplate를 통해서 native 쿼리 조건을 만들어 낼 수 있다.
  • JPA 사용시에는 해당 함수가 Hibernate dialect에 함수(registerFunction)로 등록돼 있어야 한다.
// MySQL의 TIMESTAMPDIFF를 Expression으로 만들기
 
Expressions.numberTemplate(Long.class,
  "TIMESTAMPDIFF(HOUR, {0}, {1})",
  QSomething.dateField,
  QOtherThing.anotherDateField);
 
// 이것을 메소드로 빼면 다음과 같은 형태가 된다.
public static NumberExpression<Long> hourDiff(
        Expression<? extends Date> left,
        Expression<? extends Date> right) {
    return Expressions.numberTemplate(Long.class, "TIMESTAMPDIFF(HOUR, {0}, {1})", left, right);
}

상수가 들어가는 표현식

// 0.12 - w.totalCost 를 나타내고자 한다.
Expressions.operation(Float.class, Ops.SUB, 
    Expressions.constant(0.12f), w.totalCost)
// or
NumberOperation.create(Float.class, Ops.SUB, 
    Expressions.constant(0.12f), w.totalCost)

복잡한 쿼리

  • 복잡한 쿼리는 BooleanBuilder를 사용한다.
    public List<Customer> getCustomer(String... names){
        QCustomer customer = QCustomer.customer;    
        HibernateQuery qry = new HibernateQuery(session).from(customer);    
        BooleanBuilder builder = new BoolenBuilder();
        for (String name : names){
            builder.or(customer.name.eq(name));
        }
        qry.where(builder); // customer.name eq name1 OR customer.name eq name2 OR ...  
        return qry.list(customer);
    }

Case/When

  • CaseBuilder 사용.
  • CaseBuilder 사용시 when과, otherwise? 파라미터로 안먹고 무조건 상수로만 먹는듯 보임.
  • 따라서, whenotherwise가 DB 입장에서 봤을 때 동일한 타입이 되는 상수로 맞춰줘야 한다.
// registerDate 가 DATE 컬럼 일때
// newRegisterDate 는 LocalDateTime.class
Expression<LocalDateTime> modifyDateTime = new CaseBuilder()
    .when(qUser.registerDate.eq(expectedRegisterDate))
    .then(qUser.registerDate) // DB 입장에서 DATE 컬럼
    .otherwise(
        // DB 입장에서 DATE 인 'TO_DATE' 함수를 사용해서 변경해줌. 상수값만
        // 들어가기 때문에 어쩔 수 없이 문자로 모든 것을 넣어줌
        Expressions.dateTimeTemplate(LocalDateTime.class, "to_date({0}, {1})",
            newDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
            Expressions.constant("yyyy-MM-dd HH:mm:ss"))
        );

@Embeddable의 필드 소스 생성

  • Embeddable안에 또 다른 Embeddable이 있을 경우, 더 자식쪽 Embeddable의 경우 명시적 컬럼 매핑(@Column)이 없으면 모델 소스 (Q*) 생성시에 더 자식쪽 Embeddable의 필드에 대한 모델 소스가 올바르게 생성되지 않았다.
  • 버그로 보인다.

Metamodel 생성

  • Java 7에서 SimpleSerializerConfig.getConfig에서 NullPointerException 발생하는 경우가 있는데 이는 QueryDSL과 아무 상관 없는 코드 중에 의존성에 들어가지 않는 Annotation을 넣은게 있었기 때문이었다(Java 8에서는 올바른 컴파일 오류 지점이 보였음).
    Caused by: java.lang.NullPointerException
    	at com.mysema.query.codegen.SimpleSerializerConfig.getConfig(SimpleSerializerConfig.java:29)
    	at com.mysema.query.apt.DefaultConfiguration.<init>(DefaultConfiguration.java:137)
    	at com.mysema.query.apt.jpa.JPAConfiguration.<init>(JPAConfiguration.java:54)
    	...

참고

java/querydsl.txt · 마지막으로 수정됨: 2023/12/06 13:44 저자 kwon37xi