MySQL의 Full text search를 Native Query로 사용하고 있었는데,
이를 Querydsl을 사용하는 형태로 변경하려고 한다.
- Dialect란?
- application.yml 파일 설정
- CustomDialect 구현
- 사용
- 구현 중 어려움
우리가 JPA를 사용하는 큰 이유는 개발자가 쿼리를 잘 몰라도 된다는 점에 있다.
일단 난 그것이 JPA의 가장 큰 장점이라고 본다. 개발자가 데이터베이스의 문법을 알지 않아도 hibernate가 이를 해당 데이터베이스의 쿼리로 바꿔서 날려주기 때문이다.
이를 수행해주는 것이 "Dialect(방언)"이다.
application.yml (혹은 다른 이름의 설정파일)을 보면 jpa 밑에 사용하는 DB의 dialect가 설정되어 있을 것이다.
Full text search는 Dialect에 정의되어 있지 않아 사용할 수 없다.
그래서 full text search를 구현한 custom Dialect를 구현할 것이다.
현재 사용하는 Dialect를 custom할 Dialect로 변경한다.
- 현재 사용하는 Dialect를 상속받는 customDialect 클래스 생성
- 이를 application.yml jpa 속성에 설정
import org.hibernate.dialect.MySQL8Dialect;
public class CustomDialect extends MySQL8Dialect {
}
jpa:
database-platform: com.~~.config.CustomDialect
2-1에서 생성한 CustomDialect를 구현한다.
SELECT <필요한 것들>
FROM <테이블이름>
WHERE MATCH(검색하는 칼럼들) AGAINST(검색 키워드 in 검색 모드)
위의 형태로 구현되는데 여기서 MATCH ~ AGAINST 구문이 Dialect에 없기 때문에 이를 구현해주면 된다.
public class CustomDialect extends MySQL8Dialect {
public CustomDialect() {
super();
registerFunction("match",
new SQLFunctionTemplate(StandardBasicTypes.DOUBLE,"match(?1, ?2) against (?3 in boolean mode)"));
}
}
문법은 공식 문서 확인하면서 하면 좋을 것 같다!
hibernate의 Querydsl 관련 공식문서
Dialect 관련 문서가 많이 없어서 공식문서에서 확인할 수 있다.
나는 full text search 시에, 칼럼 두개로 인덱스를 타도록 로직을 짜서 위와 같은 형태로 구성했다.
Querydsl 관련 사용 Repository 구성은 이미 다 되어있다고 가정하고 작성하면,
BooleanBuilder builder=new BooleanBuilder();
NumberTemplate booleanTemplate= Expressions.numberTemplate(Double.class,
"function('match',{0},{1},{2})",entity.Name,entity.address,keyword);
builder.and(booleanTemplate.gt(0));
return queryFactory.select(new QResponse(entity.id,entity.Name,entity.address,
Expressions.numberTemplate(Double.class,
"function('match',{0},{1},{2})",entity.Name,entity.address,keyword).as("score")))
.from(entity)
.where(builder)
.fetch();
이런 식으로 사용할 수 있다. 로직을 짜던 코드라 코드를 좀 변경해서 올리긴 하지만,
- BooleanBuilder 생성
- NumberTemplate 생성하여 MATCH ~ AGAINST 쿼리 작성
- 2에서 생성한 쿼리에 gt(>) 수행
-> 이는 full text search score가 0.0 이상인 것을 얻기 위함- builder에 3 결과 커리 and로 추가하기
🚨 만약 더 조건이 있으면 builder에 or, and로 추가할 수 있다. (실제 코드에서는 필요한 로직이 있어서 추가하고 있음!)- JPAQueryFactory로 select, from, where, fetch 함수를 이용하여 DB에 실제로 쿼리를 날린다.
사용 시에, entity를 반환하는 게 아닌 projection을 이용하여 특정 DTO를 반환하도록 구성했다.
(사실 entity를 반환해도 되는데 즉시 로딩을 걸어놓은 게 있어서 쿼리 두 번 날라가는 게 구현 측면으로는 별로라고 판단하여 projection을 반환하도록 하였다.)
projection은 @QueryProjection 어노테이션으로 생성했다.
score를 반환해야 했는데 구글링을 해도 이와 같은 코드를 확인할 수 없어서 해당 부분을 구현하는 데에 어려움이 있었다.
select에 쿼리 문(MATCH ~ AGAINST)을 넣어야 하는데 native query로는 수행되지만 querydsl을 이용해서는 대체 어떻게 하는지..
살면서 처음으로 스택오버플로우에 질문까지 했다 ㅠㅠ
근데 QResponse 구현 클래스를 확인하니
com.querydsl.core.types.Expression<Double> score
double score 파라미터의 타입으로 구성되어 있었다.
원래는 Querydsl을 처음 써보는 터라 이상한 쿼리를 막 넣어댔음 ㅎㅎ
그래서 단순하게 생성한 Numbertemplate를 파라미터로 넣어줬다.
-> 성공!
원래 이런 건 잘 안 보이는 것 같다...
도서관에서 기숙사 가기 전에 다시 확인해보니 보였다 ㅎㅎ;;