[우아한테크코스] 8월 7일 TIL
[gradle] properties -> gradle 값 가져오기
경로에 따라 파일을 만들고 Properties로 생성 거기서 getProperty(그 이름)
def sonarProperties = new Properties()
sonarProperties.load(new FileInputStream(file("src/main/resources/config/sonar.properties")))
def sonarToken = sonarProperties.getProperty("SONAR_TOKEN")
[JPA] QueryDsl
@Query
만으로는 조회 기능에 한계가 있어 정적 타입을 지원하는 조회 프레임워크 지원
- gradle 설정
plugins {
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}
ext {
querydslPluginVersion = '1.0.10'
}
dependencies {
implementation 'com.querydsl:querydsl-jpa'
}
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
Tasks -> other -> compileQuerydsl 실행하면 QSpace 파일들 생성
- configuration 추가
@Configuration
public class QuerydslConfiguration {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
QuerydslRepositorySupport 상속을 통한 구현
public interface SpaceRepository extends JpaRepository<Space, Long> {
}
@Repository
public class SpaceRepositorySupport extends QuerydslRepositorySupport {
private final JPAQueryFactory queryFactory;
public SpaceRepositorySupport(final JPAQueryFactory jpaQueryFactory) {
super(Space.class);
this.queryFactory = jpaQueryFactory;
}
// 서브쿼리 select * from space s left outer join (select * from reservation res where r.startTime >='2021-08-07' and res.end_date >= '2021-08-07') as r on r.space_id = s.id
public List<Space> findById(final Long id) {
QSpace space = QSpace.space;
return queryFactory
.selectFrom(space)
.where(space.id.eq(id))
.fetch();
}
}
검증
@SpringBootTest
public class justTest {
@Autowired
private SpaceRepository spaceRepository;
@Autowired
private SpaceRepositorySupport spaceRepositorySupport;
@Test
void findByIdTest() {
spaceRepository.save(BE);
List<Space> spacesList = spaceRepositorySupport.findById(1L);
assertThat(spacesList.size()).isEqualTo(1);
assertThat(spacesList.get(0).getDescription()).isEqualTo(BE.getDescription());
}
}
Custom Repository를 이용한 구현
SpaceRepositoryCustom
public interface SpaceRepositoryCustom {
List<Space> findById(Long id);
}
인터페이스 상속을 통한 querydsl 적용
public class SpaceRepositoryImpl implements SpaceRepositoryCustom {
private final JPAQueryFactory queryFactory;
public SpaceRepositoryImpl (JPAQueryFactory queryFactory) {
this.queryFactory = queryFactory;
}
@Override
public List<Space> findById(final Long id) {
QSpace space = QSpace.space;
return queryFactory
.selectFrom(space)
.where(space.id.eq(id))
.fetch();
}
}
상속, 구현 없이 구현
JPAQueryFactory 주입을 통한 적용
@Repository
public class SpaceQueryRepository {
private final JPAQueryFactory jpaQueryFactory;
public SpaceQueryRepository(JPAQueryFactory jpaQueryFactory) {
this.jpaQueryFactory = jpaQueryFactory;
}
public List<Space> findById(final Long id) {
QSpace space = QSpace.space;
return jpaQueryFactory
.selectFrom(space)
.where(space.id.eq(id))
.fetch();
}
}
queryDsl은 내부적으로 jpql을 사용하고, 이 둘은 join 문 내부에 서브쿼리를 지원하지 않는다.
따라서 하나의 쿼리문 안에서 해결하는 것은 불가능하다.
마지막 시도.. 였다..
public List<Space> findById(final Long id, final LocalDateTime start, final LocalDateTime end) {
return jpaQueryFactory.selectFrom(space).distinct()
.leftJoin(ExpressionUtils.as(JPAExpressions.select(reservation).from(space)
.where(reservation.startTime.goe(start), reservation.endTime.loe(end)), "reservation"))
.on(reservation.space.id.eq(space.id))
.where(space.id.eq(id))
.fetchResults()
.getResults();
}
서브쿼리는 안티패턴이다. 속도도 느리고, 좋지 않다. 관련
참고
[database] 더미데이터 넣기
프로시져를 이용해 db에 더미데이터를 넣고 테스트할 수 있다.
DELIMITER $$
DROP procedure IF EXISTS loopInsert$$
create procedure loopInsert()
begin
declare i int default 1;
while i <= 10000 do
INSERT INTO `zzimkkong`.`reservation` (`description`, `end_time`, `password`, `start_time`, `user_name`, `space_id`) VALUES ('asdf', date_add('2021-08-09 00:00:00', interval i minute), '1234', date_add('2021-08-09 00:01:00', interval i minute), 'asdf', '1');
set i = i + 1;
end while;
end$$
DELIMITER $$
call loopInsert;
- Delimiter : 구문자로 세미콜론이랑 같다. 문법의 시작과 끝 알려줌
- Procedure 이름이 loopInsert가 아니면 오류가 난다.