diff --git a/querydsl/src/main/java/com/example/querydsl/repository/MemberRepository.java b/querydsl/src/main/java/com/example/querydsl/repository/MemberRepository.java index bb6ed64b..aa10c8fe 100644 --- a/querydsl/src/main/java/com/example/querydsl/repository/MemberRepository.java +++ b/querydsl/src/main/java/com/example/querydsl/repository/MemberRepository.java @@ -2,10 +2,11 @@ package com.example.querydsl.repository; import com.example.querydsl.entity.Member; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.querydsl.QuerydslPredicateExecutor; import java.util.List; -public interface MemberRepository extends JpaRepository, MemberRepositoryCustom { +public interface MemberRepository extends JpaRepository, MemberRepositoryCustom, QuerydslPredicateExecutor { // select m from Member m where m.username = :username List findByUsername(String username); diff --git a/querydsl/src/main/java/com/example/querydsl/repository/MemberRepositoryImpl.java b/querydsl/src/main/java/com/example/querydsl/repository/MemberRepositoryImpl.java index 280956c4..bb4ab3bb 100644 --- a/querydsl/src/main/java/com/example/querydsl/repository/MemberRepositoryImpl.java +++ b/querydsl/src/main/java/com/example/querydsl/repository/MemberRepositoryImpl.java @@ -6,11 +6,13 @@ import com.example.querydsl.dto.QMemberTeamDto; import com.example.querydsl.entity.Member; import com.querydsl.core.QueryResults; import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.JPQLQuery; import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; import org.springframework.data.support.PageableExecutionUtils; import javax.persistence.EntityManager; @@ -20,22 +22,18 @@ import static com.example.querydsl.entity.QMember.member; import static com.example.querydsl.entity.QTeam.team; import static org.springframework.util.StringUtils.hasText; -public class MemberRepositoryImpl implements MemberRepositoryCustom { +public class MemberRepositoryImpl extends QuerydslRepositorySupport implements MemberRepositoryCustom { private final JPAQueryFactory queryFactory; public MemberRepositoryImpl(EntityManager em) { + super(Member.class); this.queryFactory = new JPAQueryFactory(em); } public List search(MemberSearchCondition condition) { - return queryFactory - .select(new QMemberTeamDto( - member.id.as("memberId"), - member.username, - member.age, - team.id.as("teamId"), - team.name.as("teamName"))) + + return from(member) .from(member) .leftJoin(member.team, team) .where( @@ -44,7 +42,29 @@ public class MemberRepositoryImpl implements MemberRepositoryCustom { ageGoe(condition.getAgeGoe()), ageLoe(condition.getAgeLoe()) ) + .select(new QMemberTeamDto( + member.id.as("memberId"), + member.username, + member.age, + team.id.as("teamId"), + team.name.as("teamName"))) .fetch(); +// return queryFactory +// .select(new QMemberTeamDto( +// member.id.as("memberId"), +// member.username, +// member.age, +// team.id.as("teamId"), +// team.name.as("teamName"))) +// .from(member) +// .leftJoin(member.team, team) +// .where( +// usernameEq(condition.getUsername()), +// teamNameEq(condition.getTeamName()), +// ageGoe(condition.getAgeGoe()), +// ageLoe(condition.getAgeLoe()) +// ) +// .fetch(); } @Override @@ -74,6 +94,32 @@ public class MemberRepositoryImpl implements MemberRepositoryCustom { return new PageImpl<>(content, pageable, total); } + public Page searchPageSimple2(MemberSearchCondition condition, Pageable pageable) { + JPQLQuery jpqlQuery = from(member) + .leftJoin(member.team, team) + .where( + usernameEq(condition.getUsername()), + teamNameEq(condition.getTeamName()), + ageGoe(condition.getAgeGoe()), + ageLoe(condition.getAgeLoe()) + ) + .select(new QMemberTeamDto( + member.id.as("memberId"), + member.username, + member.age, + team.id.as("teamId"), + team.name.as("teamName"))); + + JPQLQuery query = getQuerydsl().applyPagination(pageable, jpqlQuery); + + QueryResults results = query.fetchResults(); + + List content = results.getResults(); + long total = results.getTotal(); + + return new PageImpl<>(content, pageable, total); + } + @Override public Page searchPageComplex(MemberSearchCondition condition, Pageable pageable) { List content = queryFactory @@ -125,6 +171,4 @@ public class MemberRepositoryImpl implements MemberRepositoryCustom { private BooleanExpression ageLoe(Integer ageLoe) { return ageLoe != null ? member.age.loe(ageLoe) : null; } - - } diff --git a/querydsl/src/main/java/com/example/querydsl/repository/MemberTestRepository.java b/querydsl/src/main/java/com/example/querydsl/repository/MemberTestRepository.java new file mode 100644 index 00000000..bbaf824e --- /dev/null +++ b/querydsl/src/main/java/com/example/querydsl/repository/MemberTestRepository.java @@ -0,0 +1,103 @@ +package com.example.querydsl.repository; + +import com.example.querydsl.dto.MemberSearchCondition; +import com.example.querydsl.entity.Member; +import com.example.querydsl.entity.QMember; +import com.example.querydsl.repository.support.Querydsl4RepositorySupport; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQuery; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.support.PageableExecutionUtils; +import org.springframework.stereotype.Repository; + +import java.util.List; + +import static com.example.querydsl.entity.QMember.*; +import static com.example.querydsl.entity.QTeam.team; +import static org.springframework.util.StringUtils.hasText; + +@Repository +public class MemberTestRepository extends Querydsl4RepositorySupport { + + public MemberTestRepository() { + super(Member.class); + } + + public List basicSelect() { + return select(member) + .from(member) + .fetch(); + } + + public List basicSelectFrom() { + return selectFrom(member) + .fetch(); + } + + public Page searchPageByApplyPage(MemberSearchCondition condition, Pageable pageable) { + JPAQuery query = selectFrom(member) + .leftJoin(member.team, team) + .where( + usernameEq(condition.getUsername()), + teamNameEq(condition.getTeamName()), + ageGoe(condition.getAgeGoe()), + ageLoe(condition.getAgeLoe()) + ); + + List content = getQuerydsl().applyPagination(pageable, query).fetch(); + + return PageableExecutionUtils.getPage(content, pageable, query::fetchCount); + } + + public Page applyPagination(MemberSearchCondition condition, Pageable pageable) { + return applyPagination(pageable, query -> query + .selectFrom(member) + .leftJoin(member.team, team) + .where( + usernameEq(condition.getUsername()), + teamNameEq(condition.getTeamName()), + ageGoe(condition.getAgeGoe()), + ageLoe(condition.getAgeLoe()) + ) + ); + } + + public Page applyPagination2(MemberSearchCondition condition, Pageable pageable) { + return applyPagination(pageable, contentQuery -> contentQuery + .selectFrom(member) + .leftJoin(member.team, team) + .where( + usernameEq(condition.getUsername()), + teamNameEq(condition.getTeamName()), + ageGoe(condition.getAgeGoe()), + ageLoe(condition.getAgeLoe()) + ), countQuery -> countQuery + .select(member.id) + .from(member) + .leftJoin(member.team, team) + .where( + usernameEq(condition.getUsername()), + teamNameEq(condition.getTeamName()), + ageGoe(condition.getAgeGoe()), + ageLoe(condition.getAgeLoe()) + ) + ); + } + + private BooleanExpression usernameEq(String username) { + return hasText(username) ? member.username.eq(username) : null; + } + + private BooleanExpression teamNameEq(String teamName) { + return hasText(teamName) ? team.name.eq(teamName) : null; + } + + private BooleanExpression ageGoe(Integer ageGoe) { + return ageGoe != null ? member.age.goe(ageGoe) : null; + } + + private BooleanExpression ageLoe(Integer ageLoe) { + return ageLoe != null ? member.age.loe(ageLoe) : null; + } +} diff --git a/querydsl/src/main/java/com/example/querydsl/repository/support/Querydsl4RepositorySupport.java b/querydsl/src/main/java/com/example/querydsl/repository/support/Querydsl4RepositorySupport.java new file mode 100644 index 00000000..b42d4161 --- /dev/null +++ b/querydsl/src/main/java/com/example/querydsl/repository/support/Querydsl4RepositorySupport.java @@ -0,0 +1,84 @@ +package com.example.querydsl.repository.support; + +import com.querydsl.core.types.EntityPath; +import com.querydsl.core.types.Expression; +import com.querydsl.core.types.dsl.PathBuilder; +import com.querydsl.jpa.impl.JPAQuery; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.support.JpaEntityInformation; +import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport; +import org.springframework.data.jpa.repository.support.Querydsl; +import org.springframework.data.querydsl.SimpleEntityPathResolver; +import org.springframework.data.repository.support.PageableExecutionUtils; +import org.springframework.stereotype.Repository; +import org.springframework.util.Assert; +import javax.annotation.PostConstruct; +import javax.persistence.EntityManager; +import java.util.List; +import java.util.function.Function; + +@Repository +public abstract class Querydsl4RepositorySupport { + private final Class domainClass; + private Querydsl querydsl; + private EntityManager entityManager; + private JPAQueryFactory queryFactory; + public Querydsl4RepositorySupport(Class domainClass) { + Assert.notNull(domainClass, "Domain class must not be null!"); + this.domainClass = domainClass; + } + @Autowired + public void setEntityManager(EntityManager entityManager) { + Assert.notNull(entityManager, "EntityManager must not be null!"); + JpaEntityInformation entityInformation = + JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager); + SimpleEntityPathResolver resolver = SimpleEntityPathResolver.INSTANCE; + EntityPath path = resolver.createPath(entityInformation.getJavaType()); + this.entityManager = entityManager; + this.querydsl = new Querydsl(entityManager, new + PathBuilder<>(path.getType(), path.getMetadata())); + this.queryFactory = new JPAQueryFactory(entityManager); + } + @PostConstruct + public void validate() { + Assert.notNull(entityManager, "EntityManager must not be null!"); + Assert.notNull(querydsl, "Querydsl must not be null!"); + Assert.notNull(queryFactory, "QueryFactory must not be null!"); + } + protected JPAQueryFactory getQueryFactory() { + return queryFactory; + } + protected Querydsl getQuerydsl() { + return querydsl; + } + protected EntityManager getEntityManager() { + return entityManager; + } + protected JPAQuery select(Expression expr) { + return getQueryFactory().select(expr); + } + protected JPAQuery selectFrom(EntityPath from) { + return getQueryFactory().selectFrom(from); + } + protected Page applyPagination(Pageable pageable, + Function contentQuery) { + JPAQuery jpaQuery = contentQuery.apply(getQueryFactory()); + List content = getQuerydsl().applyPagination(pageable, + jpaQuery).fetch(); + return PageableExecutionUtils.getPage(content, pageable, + jpaQuery::fetchCount); + } + protected Page applyPagination(Pageable pageable, + Function contentQuery, Function countQuery) { + JPAQuery jpaContentQuery = contentQuery.apply(getQueryFactory()); + List content = getQuerydsl().applyPagination(pageable, + jpaContentQuery).fetch(); + JPAQuery countResult = countQuery.apply(getQueryFactory()); + return PageableExecutionUtils.getPage(content, pageable, + countResult::fetchCount); + } +} \ No newline at end of file diff --git a/querydsl/src/test/java/com/example/querydsl/repository/MemberRepositoryTest.java b/querydsl/src/test/java/com/example/querydsl/repository/MemberRepositoryTest.java index 4dc4e769..5440f625 100644 --- a/querydsl/src/test/java/com/example/querydsl/repository/MemberRepositoryTest.java +++ b/querydsl/src/test/java/com/example/querydsl/repository/MemberRepositoryTest.java @@ -3,6 +3,7 @@ package com.example.querydsl.repository; import com.example.querydsl.dto.MemberSearchCondition; import com.example.querydsl.dto.MemberTeamDto; import com.example.querydsl.entity.Member; +import com.example.querydsl.entity.QMember; import com.example.querydsl.entity.Team; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -89,4 +90,30 @@ class MemberRepositoryTest { assertThat(result.getSize()).isEqualTo(3); assertThat(result.getContent()).extracting("username").containsExactly("member1", "member2", "member3"); } + + @Test + void querydslPredicateExecutorTest() { + Team teamA = new Team("teamA"); + Team teamB = new Team("teamB"); + em.persist(teamA); + em.persist(teamB); + + Member member1 = new Member("member1", 10, teamA); + Member member2 = new Member("member2", 20, teamA); + Member member3 = new Member("member3", 30, teamB); + Member member4 = new Member("member4", 40, teamB); + em.persist(member1); + em.persist(member2); + em.persist(member3); + em.persist(member4); + + QMember member = QMember.member; + Iterable result = memberRepository.findAll( + member.age.between(10, 40) + .and(member.username.eq("member1")) + ); + for (Member findMember : result) { + System.out.println("findMember = " + findMember); + } + } } \ No newline at end of file diff --git a/querydsl/src/test/java/com/example/querydsl/repository/MemberTestRepositoryTest.java b/querydsl/src/test/java/com/example/querydsl/repository/MemberTestRepositoryTest.java new file mode 100644 index 00000000..7071b38c --- /dev/null +++ b/querydsl/src/test/java/com/example/querydsl/repository/MemberTestRepositoryTest.java @@ -0,0 +1,52 @@ +package com.example.querydsl.repository; + +import com.example.querydsl.dto.MemberSearchCondition; +import com.example.querydsl.dto.MemberTeamDto; +import com.example.querydsl.entity.Member; +import com.example.querydsl.entity.Team; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.EntityManager; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@Transactional +class MemberTestRepositoryTest { + + @Autowired + EntityManager em; + + @Autowired MemberTestRepository memberTestRepository; + + @Test + void searchPageSimple() { + Team teamA = new Team("teamA"); + Team teamB = new Team("teamB"); + em.persist(teamA); + em.persist(teamB); + + Member member1 = new Member("member1", 10, teamA); + Member member2 = new Member("member2", 20, teamA); + Member member3 = new Member("member3", 30, teamB); + Member member4 = new Member("member4", 40, teamB); + em.persist(member1); + em.persist(member2); + em.persist(member3); + em.persist(member4); + + MemberSearchCondition condition = new MemberSearchCondition(); + PageRequest pageRequest = PageRequest.of(0, 3); + + Page result = memberTestRepository.searchPageByApplyPage(condition, pageRequest); + + assertThat(result.getSize()).isEqualTo(3); + assertThat(result.getContent()).extracting("username").containsExactly("member1", "member2", "member3"); + } +} \ No newline at end of file