[우아한테크코스] 8월 7일 TIL

2 minute read

[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 만으로는 조회 기능에 한계가 있어 정적 타입을 지원하는 조회 프레임워크 지원

  1. 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 파일들 생성

  1. 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가 아니면 오류가 난다.

이외 더미데이터 만들어주는 사이트