JPA
JPA Specification를 사용하여 조인,검색기능 만들기
dev.mk
2023. 12. 25. 15:21
반응형
이 포스트는 Specification의 사용법만 설명한다. 기타 페이징 적용, response방법 등은 생략한다
JPA Specification란?
1. JPA Specification은 Java Persistence API (JPA)에서 제공하는 기능 중 하나로, 동적으로 쿼리를 생성하는 방법을 제공
2. JPA는 자바 어플리케이션에서 관계형 데이터베이스와 상호 작용할 수 있게 해주는 API
3. JpaRepository 상속받을때 JpaSpecificationExecutor도 같이 속받으면 된다.
테스트를 진행하려면 아래의 파일들이 필요하다.
AccountSpecification.java
public class AccountSpecification {
//거래처명 검색
public static Specification<Account> likeAccountName(String accountName) {
return new Specification<Account>() {
private static final long serialVersionUID = 1L;
@Override
public Predicate toPredicate(Root<Account> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
// 2) like
return criteriaBuilder.like(root.get("accountName"), "%" + accountName + "%");
}
};
}
//본인 부서
public static Specification<Account> equalDepartmentCode(Integer departmentCode) {
return (Specification<Account>) (root, query, builder) -> {
if (departmentCode == null) {
return null;
}
Join<Account, Employee> empJoin = root.join("employee", JoinType.INNER); //사원테이블과 조인 (emplyCode) 기준
Join<Employee, Department> deptJoin = empJoin.join("department", JoinType.INNER); // 부서테이블과 조인 (departmentCode 기준)
return builder.equal(deptJoin.get("departmentCode"), departmentCode); // 부서 코드 일치 조건
};
}
}
거래처테이블 Account.java (Entity)
@Entity
@Table(name = "tb_account")
@NoArgsConstructor(access = PROTECTED)
@Getter
@EntityListeners(AuditingEntityListener.class)
public class Account {
@Id
@GeneratedValue(strategy = IDENTITY)
private Long accountCode; //거래처코드
@Column(nullable = false)
private String accountNumber; //거래처연락처
@Column(nullable = false)
private String accountName; //거래처명
@JoinColumn(name = "emplyCode") //직원테이블 관계 설정
private Employee employee;
}
사원(회원)테이블 Employee.java(Entity)
@Entity
@Table(name = "tb_employee")
@Getter
@NoArgsConstructor(access = PROTECTED)
@EntityListeners(AuditingEntityListener.class)
public class Employee {
@Id
@GeneratedValue(strategy = IDENTITY)
private Long emplyCode; //사원코드
private String emplyName; //사원이름
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "departmentCode")
private Department department; //부서코드
}
부서 테이블 Department.java(Entity)
@Entity
@NoArgsConstructor(access = PROTECTED)
@Getter
@Table(name = "tb_department")
public class Department {
@Id
@GeneratedValue(strategy = IDENTITY)
private Long departmentCode; //부서코드
@Column(nullable = false)
private String departmentName; //부서이름
}
<!-- 내용 검색 콤보박스 -->
<slect id="schType" name="schType">
<option value="">전체</option>
<option value="accountName">거래처명</option>
<option value="accountNumber">거래처번호</option>
</slect>
<!-- 검색어 키워드 -->
<input type="text" name="schText" id="schText" placeholder="검색어를 입력하세요.">
<!-- 부서 검색 콤보박스 -->
<slect id="departmentCode" name="departmentCode">
<option value="">전체</option>
<option value="1">영업부</option>
<option value="2">개발부</option>
</slect>
AccountService.java
@Transactional(readOnly = true)
public Page<AccountListResponse> getFindAll(final Integer page, Integer departmentCode, String schType, String schText) {
Specification<Account> spec = (root, query, criteriaBuilder) -> null;
//1. 검색타입이 거래처명일때
if ("accountName".equals(schType)) {
spec = spec.and(AccountSpecification.likeAccountName(schText));
}
//2. 부서코드가 0이면 전체, 0이 아니면 선택한 부서코드로 조회
if(departmentCode != 0) {
spec = spec.and(AccountSpecification.equalDepartmentCode(departmentCode));
}
Page<Account> accountList = accountRepository.findAll(spec, getPageable(page));
return accountList.map(account -> AccountListResponse.from(account));
}
※ 서비스 자바 내용중 if의 조건이 충족하면 spec에 and키워드로 조건이 누적이 된다.
-- 검색조건테스트
1. 거래처명을 '테슬라' 라는 키워드로 검색하기 (테이블에 있는 컬럼)
-- SQL 결과
select
account0_.account_code as account_1_0_,
account0_.account_name as account_4_0_,
account0_.account_number as account_5_0_
from
tb_account account0_
where
account0_.account_name like '%테슬라%'
order by
account0_.account_code desc limit 10
2. 거래처명을 '테슬라' 라는 키워드로 검색하고 부서코드 1로 조회하기(조인 후 다른 테이블의 컬럼검색)
select
account0_.account_code as account_1_0_,
account0_.account_name as account_4_0_,
account0_.account_number as account_5_0_
from
tb_account account0_
inner join
tb_employee employee1_
on account0_.emply_code=employee1_.emply_code
inner join
tbl_department department2_
on employee1_.department_code=department2_.department_code
where
department2_.department_code=1
and (
account0_.account_name like '%테슬라%'
)
order by
account0_.account_code desc limit 10
거래처명을 테슬라로 검색하고 부서코드를 1로 조회했다.
AccountService.java에서 if문의 조건이 전부 성립하여 spec에 쿼리가 누적되어 SQL이 완성되었다.
반응형