1. criteria builder 인스턴스 생성
2. criteria query(로직) 인스턴스 생성 => createQuery, createCriteriaDelete, createCriteriaUpdate
3. root설정
4. 조건설정
5. TypedQuery정의
6. 실행
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
class PatientTrackerSpringBootApplicationTests{
@Autowired
private PatientRepository patientRepository;
@Autowired
private EntityManager entityManager;
@Test
public void givenPatientsCreatedWhenLoadPatientsThenExpectCorrectPatientDetails(){
patientRepository.saveAll(getPatientList());
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Patient> patientCriteriaQuery = criteriaBuilder.createQuery(Patient.class);
Root<Patient> patientRoot = patientCriteriaQuery.from(Patient.class);
Predicate patientCategoryPredicate = criteriaBuilder.equal(patientRoot.get("sex"),"W");
patientCriteriaQuery.Where(patientCategoryPredicate);
TypedQuery<Patient> query = entityManager.createQuery(patientCriteriaQuery);
assertThat(query.getResultList().size()).IsEqualTo(2);
}
private List<Patient> getPatientList(){
return Arrays.asList(
new Patient(1,"전능","M",24),
new Patient(2,"능아","W",21),
new Patient(3,"이티","M",17),
new Patient(4,"티전","W",35),
new Patient(5,"전능아","M",3)
);
}
}
사용법
pom.xml 에 의존관계,플러그인 추가
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.java.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>Repository 가 추가적으로 Querydsl Predicate 도 상속하게
@Repository
public interface PatientRepository extends CrudRepository<Patient, int>,
QuerydslPredicateExecuter<Patient> {
}
사용
3.1 Flow
1. 자동생성 QPatient로 patient인스턴스 정의
2. JPAQuery 인스턴스 생성 => QueryDSL에서 JPA를 사용할 수 있게 해주즌 JPQL인터페이스의 기본 구현
3. QueryDSL의 from(), where() 메서드를 상요해서 from where 절 정의
4. 쿼리 실행 => fetch()
@SpringBootTest
class PatientSpringBootApplicationTest{
@Autowired
private PatientRepository patientRepository;
@Autowired
private EntityManager entityManager;
@Test
public void givenPatientsCreatedWhenLoadPatientsThenExpectCorrectPatientDetails() {
patientRepository.saveAll(getPatientList());
Qpatient patient = Qpatient.patient;
JPAQuery query1 = new JPAQuery(entityManager);
query1.from(patient).where(patient.sex.eq("W"));
assertThat(query1.fetch().size().isEqualTo(2));
JPAQuery query2 = new JPAQuery(entityManager);
query1.from(patient).where(patient.sex.eq("W").and(patient.age.gt(30)));
assertThat(query1.fetch().size().isEqualTo(1));
OrderSpecifier<Integer> descOrderSpecifier = patient.age.desc();
new asserThat(Lists.newArrayList(patientRepository.findAll(descOrderSpecifier)).get(0).getName())
.isEqualTo("티전");
}
private List<Patient> getPatientList(){
return Arrays.asList(
new Patient(1,"전능","M",24),
new Patient(2,"능아","W",21),
new Patient(3,"이티","M",17),
new Patient(4,"티전","W",35),
new Patient(5,"전능아","M",3)
);
}
}
| Criteria API | QueryDSL | |
|---|---|---|
| 종류 | JPA 표준 API | 외부 라이브러리 (QueryDSL) |
| 사용성 | 상대적으로 복잡함 | 간편하고 직관적임 |
| 쿼리 작성 방식 | 메서드 체이닝 방식 | 자바 코드로 쿼리를 작성 |
| 타입 안정성 | 상대적으로 낮음 | 높음 |
| 동적 쿼리 처리 | 복잡하고 가독성이 낮음 | 간편하고 가독성이 높음 |
| 코드 생성 | 수동으로 코드를 작성해야 함 | 자동으로 코드를 생성함 |
| 성능 | JPA 구현체에 의존하여 성능 차이가 있을 수 있음 | 성능 향상을 위한 최적화 가능 |
사용법
public interface NameOnly{
String getName();
}
@Repository
public interface PatientRepository extends CrudRepository<Patient,int>{
Iterable<NameOnly> getPatientById(int id);
}
@Test
public void getPatientByIdSuccess(){
Iterable<NameOnly> result =
patientRepository.getPatientById(1);
assertThat(result).extracting("Name").contains("전능");
}
1:1

1:n

n:1 - 1:n reverse
n:m

sql 생성
CREATE TABLE patients(
id long NOT NULL,
name string,
age int,
sex string,
primary key (id)
);
CREATE TABLE patients_hospitals(
patient_id long not null,
hospital_id long not null,
primary key (patient_id,hospital_id)
);
CREATE TABLE hospitals(
id long not null,
name string,
hospital_type int,
primary key (id)
)
ALTER TABLE patients_hospitals
ADD CONSTRAINT patient_id_fk FOREIGN KEY (patient_id)
references patients (id);
ALTER TABLE patients_hospitals
ADD CONSTRAINT hospital_id_fk FOREIGN KEY (hospital_id)
references hospitals (id);
엔티티 클래스 생성
package com.manning.sbip.ch03.model;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity(name = "Patient")
@Table(name="Patients")
public class Patient {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String sex;
private int age;
@ManyToMany
@JoinTable(name = "patients_hospitals",
joinColumns = {@JoinColumn(name="patient_id", referencedColumnName = "id", nullable = false, updatable = false)},
inverseJoinColumns = {@JoinColumn(name="hospital_id", referencedColumnName = "id", nullable = false, updatable = false)}
)
private Set<hospital> hospitals = new HashSet<>();
// ctor, getter, setter 생략
}
package com.manning.sbip.ch03.model;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@Entity(name = "hospital")
@Table(name = "hospital")
public class Hospital {
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "NAME")
private String name;
@Column(name = "HOSPITAL_TYPE")
private String hospital_type;
@ManyToMany(mappedBy = "hospital")
private Set<Patient> patients = new HashSet<>();
//ctor, getter,setter 생략
}
@Entity(name="patients_hospitals")
@TABLE(name = "patients_hospitals")
public class AuthorCourse{
@Id
@Column(name="patient_id")
private long patientId;
@Column(name="hopital_id")
private long hopitalId;
}
package com.manning.sbip.ch03.dto;
public class AuthorCourseDto {
private long id;
private String patientName;
private String hospitalName;
public PatientHospitalDto(long id, String patientName, String hospitalName) {
this.id = id;
this.authorName = authorName;
this.courseName = courseName;
}
@Override
public String toString() {
return "{" +
"id=" + id +
", patientName='" + patientName + '\'' +
", hospitalName='" + hospitalName + '\'' +
'}';
}
}
package com.manning.sbip.ch03.repository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.manning.sbip.ch03.dto.PatientHospitalDto;
import com.manning.sbip.ch03.model.Author;
@Repository
public interface PatientRepository extends CrudRepository<patient, int> {
@Query("SELECT new com.manning.sbip.ch03.dto.PatientHospitalDto(c.id, a.name, c.name)" +
"from Patients a, Hospitals c, patients_hospitals ac where a.id = ac.patientId and c.id=ac.hospitalId and ac.patientId=?1")
Iterable<PatientHospitalDto> getPatientHospitalInfo(long patientId);
}