- ํน์ง
- ๋์์ ๊ฐ์ฒด (ํ ์ด๋ธ์ด ์๋)
- ์์์ฑ ์ปจํ์คํธ ํ์ฉ ๊ฐ๋ฅ
- ์ปดํ์ผ ์์ ์ ์ํฐํฐ-ํ๋ํ์ ํ์ธ ํ ์ค๋ฅ ๊ฐ์
โถ
TypeQuery
- ํ์ ์ด ๋ช ํํ ๋ ! (์ฃผ๋ก ์ํฐํฐ๋ ํน์ ํ์ ์ด ์์๋)
- ์ฃผ๋ก ์ฌ์ฉํ๋ค!
TypedQuery<Tutor> typeQuery1 = em.createQuery("select t from Tutor t", Tutor.class); TypedQuery<String> typeQuery2 = em.createQuery("select t.name from Tutor t", String.class);
โท
Query
- ๋ฐํ ํ์ ๋ช ํํ์ง ์์๋ ( = ๋ค์ํ ํ์ ์ ๋ฐ์ดํฐ ๋ฐํ)
- Object๋ก ๋ฐํํ๋ฏ๋ก ํ๋ณํ ํ์ํ ์๋
Query query = em.createQuery("select t.name, t.age from Tutor t");
โถ
getResultList()
- ๊ฒฐ๊ณผ๊ฐ ํ๋ ์ด์์ผ ๋ ์ฌ์ฉํ๋ค.
List๋ฐํ (๊ฒฐ๊ณผ๊ฐ ์๋ค๋ฉด ๋นList๋ฐํ)List resultList = em.createQuery("select t from Tutor t").getResultList();
โท
getSingleResult()
- ๊ฒฐ๊ณผ๊ฐ ๋ฑ ํ๋์ผ๋
- ์ฟผ๋ฆฌ ๋ค์ ์ํฐํฐ ํด๋์ค๋ฅผ ๋ช ์ํด์ค๋ค (๋ฐํํ์ )
Tutor singleResult = em.createQuery("select t from Tutor t where t.id = 1L", Tutor.class).getSingleResult();
๋์ ์ผ๋ก ๊ฐ์ ์ ๋ฌํ๋ค, ์ฟผ๋ฆฌ ์ฌ์ฌ์ฉ ๊ฐ๋ฅ
Tutor wonuk = em.createQuery("select t from Tutor t where t.name = โ :name", Tutor.class) .setParameter("name", "wonuk")โ ํ๋ผ๋ฏธํฐ ์ง์ .getSingleResult(); System.out.println("wonuk.getName() = " + wonuk.getName()); System.out.println("wonuk.getAge() = " + wonuk.getAge());
โถ๏ธ JPA์์ ์ฌ๋ฌ ํ๋๋ฅผ ํ๋์ ๊ฐ ๊ฐ์ฒด๋ก ๋ฌถ์ด ์ฌ์ฉํ๋ ์ด๋ ธํ ์ด์
โ
@Embeddable- ๊ฐ์ ์ ์ํ๋ ์ชฝ. ๋ด์ฅ๋ ํด๋์ค (์:Period,Address)
โ@Embedded- ๊ฐ์ ์ฌ์ฉํ๋ ์ชฝ. ์ํฐํฐ์ ํฌํจ์ํค๋ ํ๋ (์:Tutor.period)
โถ๏ธ ์ํฉ ์์
| ์ํฉ | ์ค๋ช |
|---|---|
| ๊ฐ๋ค์ ํ ๋ฉ์ด๋ฆฌ๋ก ํํํ๊ณ ์ถ์ ๋ | ๊ธฐ๊ฐ(์์~์ข ๋ฃ), ์ฃผ์(์/๋/์ฐํธ๋ฒํธ), ์ด๋ฆ(์ฑ/์ด๋ฆ) ๋ฑ |
| ์ฌ๋ฌ ์ํฐํฐ์์ ์ค๋ณต๋ ๊ฐ ๊ตฌ์กฐ๊ฐ ๋์ฌ ๋ | ์: Period๋ฅผ Tutor, Employee์์ ํจ๊ป ์ฐ๊ณ ์ถ์ ๋ |
| ๊ฐ ์์ฒด์ ๋น์ฆ๋์ค ๋ก์ง์ ํฌํจํ ์ ์๊ฒ ํ๊ณ ์ถ์ ๋ | Period.isWork(Date) ์ฒ๋ผ ํด๋น ๊ฐ๋ง์ ๋ฉ์๋ ๋ง๋ค ์ ์์ |
โถ
@Embedded์ฌ์ฉ@Entity public class Tutor { @Id @GeneratedValue private Long id; private String name; @Embedded // โ Embedded ์ฌ์ฉ private Period period; }
โท
@Embeddable์ฌ์ฉ@Embeddable// โ Embedded ์ ์ public class Period { @Temporal(TemporalType.DATE) <--- โ ๋ ์ง ๋งคํ์ ์ฌ์ฉ Date startDate; @Temporal(TemporalType.Date) Date endDate; public boolean isWork (Date date) { <--- โ ์์ฒด ๊ฒ์ฆ ๋ก์ง // startDate <= date <= endDate ํ์ธ return (startDate == null || !date.before(startDate)) && (endDate == null || !date.after(endDate)); } }
โธ ๋ฐ์ดํฐ ์กฐํ
id name start_date end_date ![]()
- tutor ํ ์ด๋ธ์
period๊ฐ ์๋,
period ๋ด ํ๋์ธstartDate,endDate๊ฐ ๋์จ๋ค
โถ๏ธ ์ฟผ๋ฆฌ์์ ์ด๋ค ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ง ์ ํํ๋ ๊ฒ ( select ์ด๋ค๊ฑฐ ํ ์ง? )
โถ๏ธ ์ ์ฌ์ฉํ๋๊ฐ?
- ์ํฐํฐ ์ ์ฒด๋ฅผ ํต์งธ๋ก ์กฐํํ๋ ๋ฐฉ์
- ์กฐํ๋ ๊ฐ์ฒด๋ ์์์ฑ ์ปจํ ์คํธ์ ์ํด ๊ด๋ฆฌ๋๋ค (Dirty Checking ๊ฐ๋ฅ)
Tutor tutor = new Tutor("wonuk", 100); em.persist(tutor); // ์์์ฑ ์ปจํ ์คํธ ์ด๊ธฐํ โ ์กฐํํ tutor๋ ๋ ์ด์ ๊ด๋ฆฌ๋์ง ์์ em.flush(); em.clear(); // Tutor ์ํฐํฐ ์ ์ฒด ์กฐํ List<Tutor> tutorList = em.createQuery("select t from Tutor t", Tutor.class).getResultList(); Tutor wonuk = tutorList.get(0); // ์ํฐํฐ๊ฐ ๋ค์ ์์์ฑ ์ปจํ ์คํธ์ ๋ค์ด์์ ๋ณ๊ฒฝ ๊ฐ์ง ๊ฐ๋ฅ wonuk.setName("wonuk2");
// ๋ฌต์์ JOIN โ JPQL์์๋ ์์ฑํ์ง ์์์ง๋ง ๋ด๋ถ์ ์ผ๋ก JOIN ๋ฐ์ Company company = em.createQuery( "select t.company from Tutor t", Company.class ).getSingleResult(); // ๋ช ์์ JOIN โ ์ฟผ๋ฆฌ์์ JOIN์ ๋ช ํํ ์ ์ธ Company companyV2 = em.createQuery( "select t from Tutor t join t.company", Company.class ).getSingleResult();
- ๐ก ๋ฌต์์ JOIN์ SQL์ ์์ธกํ๊ธฐ ์ด๋ ค์ ์ํํจ
โ ๋ช ์์ ์ผ๋ก JOIN์ ์ ์ธํ๋ ๊ฒ์ด ์ข๋ค.
List<Period> periods = em.createQuery( "select t.period from Tutor t", Period.class).getResultList(); * `select p from Period p` โ โ ๋ถ๊ฐ๋ฅ * Embedded ๊ฐ์ฒด๋ ๋ ๋ฆฝ์ ์ธ ์ํฐํฐ๊ฐ ์๋๊ธฐ ๋๋ฌธ
โ๏ธ ์กฐ๊ฑด ๊ฒ์ ์์
// Embedded ๋ด๋ถ ํ๋ ์ ๊ทผ ๊ฐ๋ฅ "select t from Tutor t where t.period.startDate < :today"โ๏ธ ์์ ๊ตฌ์กฐ โ ์ง๊ด์
"select t from Tutor t where t.startDate < :today" // โ ์ด๊ฑด period ์์ด ๋ฐ๋ก ์ ๊ทผ ๊ฐ๋ฅ (MappedSuperclass์ ๊ฒฝ์ฐ)
- ์ํฐํฐ๊ฐ ์๋ ๋จ์ผ ํ๋(๋ฌธ์์ด, ์ซ์ ๋ฑ) ๋๋ ์ฌ๋ฌ ํ๋๋ง ์กฐํํ ๋
List<Object[]> resultList = em.createQuery( "select t.name, t.age from Tutor t" ).getResultList(); Object[] result = resultList.get(0); System.out.println("์ด๋ฆ: " + result[0]); // name System.out.println("๋์ด: " + result[1]); // age๐ ๊ฒฐ๊ณผ๋
Object[]๋ฐฐ์ด๋ก ๋ฆฌํด๋๋ฏ๋ก, ํ๋ณํ ํ์
// DTO์ ์์ฑ์ ํ์: public TutorDto(String name, int age) List<TutorDto> dtos = em.createQuery( "select new com.example.TutorDto(t.name, t.age) from Tutor t", TutorDto.class ).getResultList();Scalar ํ๋ก์ ์ ์ ๋ ๋ช ํํ๊ฒ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด DTO์ ์ง์ ๋งคํํ๋ ๋ฐฉ๋ฒ์ด ๊ฐ์ฅ ๊น๋ํ๊ณ ์์
โถ๏ธ JPQL์ ๊ธฐ๋ณธ์ ์ผ๋ก LIMIT, OFFSET ๊ฐ์ SQL ๋ฌธ๋ฒ์ ์ง์ ์ฐ์ง ์๊ณ
JPA๊ฐ ์ ๊ณตํ๋ ๋ฉ์๋๋ก ํ์ด์ง ์ฒ๋ฆฌ๋ฅผ ํ๋ค.
โถ๏ธ ์ฌ์ฉ ๋ฉ์๋
๋ฉ์๋ ์ค๋ช setFirstResult(int)์กฐํ ์์ ์์น (OFFSET) setMaxResults(int)๊ฐ์ ธ์ฌ ๋ฐ์ดํฐ ๊ฐ์ (LIMIT)
List<Tutor> tutorList = em.createQuery( "select t from Tutor t order by t.age desc", Tutor.class ) .setFirstResult(5) // 6๋ฒ์งธ ๋ฐ์ดํฐ๋ถํฐ ์กฐํ (OFFSET = 5) .setMaxResults(10) // ์ด 10๊ฐ ์กฐํ (LIMIT = 10) .getResultList(); for (Tutor tutor : tutorList) { System.out.println("tutor = " + tutor.getId() + ", " + tutor.getName() + ", " + tutor.getAge()); }
order by t.age desc
โ ๋์ด ์์๋๋ก ๋ด๋ฆผ์ฐจ์ ์ ๋ ฌ (๊ฐ์ฅ ๋์ด ๋ง์ ํํฐ๋ถํฐ)setFirstResult(5)
โ 6๋ฒ์งธ ๋ฐ์ดํฐ๋ถํฐ ์์setMaxResults(10)
โ 10๊ฐ ๋ฐ์ดํฐ๋ง ์กฐํ
JPA์์์
N+1๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ค
โถ๏ธ ์ง์ฐ๋ก๋ฉ(LAZY) ์ค์ ๋ ์ฐ๊ด ์ํฐํฐ๋ฅผ JOIN๊ณผ ๋์์ ํ ๋ฒ์ ๊ฐ์ ธ์ค๋ ๋ฐฉ๋ฒ
String query = "select t from Tutor t join fetch t.company";โถ Tutor๊ฐ Company๋ฅผ ์ฐธ์กฐํ๊ณ ์๊ณ ,
โทt.getCompany().getName()๋ก ์ ๊ทผํ๋ฉด ์๋๋ ์ฟผ๋ฆฌ ๋ ๋ ์๊ฐ์ผํ๋๋ฐ
โธfetch join์ ์ฐ๋ฉด ๊ทธ ์ ์ ๋ฏธ๋ฆฌ Company๊น์ง ๊ฐ์ด ๊ฐ์ ธ์จ๋ค
โ ์ฟผ๋ฆฌ ํ ๋ฒ์ผ๋ก ๋ ๋ค ๊ฐ์ ธ์ค๋๊น N+1 ๋ฌธ์ ํด๊ฒฐ!
โถ๏ธ ํ ์ํฐํฐ๊ฐ ์ฌ๋ฌ ๊ฐ๋ฅผ ๊ฐ๊ณ ์๋ ์ปฌ๋ ์ (์: ํ์ฌ โ ํํฐ ๋ชฉ๋ก)์ ํ ๋ฒ์ ๊ฐ์ ธ์ค๋ ๋ฐฉ์
String query = "select c from Company c join fetch c.tutorList";โถ Company ํ๋์ Tutor ์ฌ๋ฌ ๋ช ๋ถ์ด์์ผ๋ฉด,
โท SQL์ Tutor ์๋งํผ Company๊ฐ ์ค๋ณต๋ผ์ ๋์จ๋ค
โ ๊ทผ๋ฐ JPA๋ ์์์ ๊ฐ์ Company๋ ํ ๋ฒ๋ง ๋ง๋ค์ด์ค๋ค!
โถ๏ธ ์ง์ฐ๋ก๋ฉ์ ์ ์งํ๋ฉด์, ํ ๋ฒ์ ์ฌ๋ฌ ๊ฐ๋ฅผ IN ์ฟผ๋ฆฌ๋ก ๊ฐ์ ธ์ค๋ ๋ฐฉ์
@BatchSize(size = 100) @OneToMany(mappedBy = "company") private List<Tutor> tutorList;โถ ์๋ฅผ ๋ค์ด Company 2๊ฐ๊ฐ ์์ ๋,
โท ๊ฐ Company์ tutorList๋ฅผ ์กฐํํ๋ ค๊ณ ํ๋ฉด
โธ Tutor๋ฅผ ํ๋์ฉ ์ฟผ๋ฆฌํ๋ ๊ฒ ์๋๋ผ
โ Tutor WHERE company_id IN (1, 2) ์ด๋ฐ ์ฟผ๋ฆฌ๋ก ํ ๋ฒ์!
โ
์ฅ์ : ํ์ด์ง ๊ฐ๋ฅ! ์ฟผ๋ฆฌ๋ ์ค์ด๋ฆ
โ ๏ธ ์ฃผ์: ํ ๋ฒ์ ๋๋ฌด ๋ง์ด ๋ก๋ฉํ๋ฉด ๋ฉ๋ชจ๋ฆฌ ํฐ์ง ์๋ ์์ผ๋ size ์กฐ์ ์ค์
โถ Repository์ ํํฐ๋ณ ํ์ ์ ๋ฉ์๋
public interface TutorRepository extends JpaRepository<Tutor, Long> { @Query("select new com.example.springjpql.dto.TutorStudentCountDto(t.name, count(s)) " + "from Tutor t join t.students s group by t.name") List<TutorStudentCountDto> findTutorStudentCounts(); }โท Repository์ ํ์ ์กฐ๊ฑด๋ถ ๊ฒ์ ๋ฉ์๋
@Query("select new com.example.springjpql.dto.StudentDto(s.name, s.age) " + "from Student s where s.name like %:keyword% " + "and s.age >= :minAge and s.age <= :maxAge") List<StudentDto> findByNameContainingAndAgeBetween( @Param("keyword") String keyword, @Param("minAge") int minAge, @Param("maxAge") int maxAge );
โถ ํ๋กํ ์ค์ ํ๊ธฐ (
application.yml)profiles: active: dev โโท ํ ์คํธ์ฉ ๋ฐ์ดํฐ ์ถ๊ฐ (
configํจํค์ง )
@PostConstruct๋ก Application ์ต์ด ์คํ ์์๋ง ์ด๊ธฐํ ํ๋๋ก ์ค์ @Profile("dev")dev ํ๋กํ์์๋ง ๋์ํ๋๋ก ์ค์ @Slf4j @Component @Profile("dev") โ public class DataInitializer { @Autowired private TutorRepository tutorRepository; @Autowired private StudentRepository studentRepository; @PostConstruct โ public void init() { // Tutor ๋ฐ์ดํฐ ์ด๊ธฐํ Tutor tutor1 = new Tutor("tutor1"); Tutor tutor2 = new Tutor("tutor2"); Tutor tutor3 = new Tutor("tutor3"); tutorRepository.save(tutor1); tutorRepository.save(tutor2); tutorRepository.save(tutor3); // Student ๋ฐ์ดํฐ ์ด๊ธฐํ for (int i = 0; i < 30; i++) { Student student = new Student("student" + i, 20 + i); // ํํฐ๋ฅผ ์์ฐจ์ ์ผ๋ก ํ ๋น if (i % 3 == 0) { student.setTutor(tutor1); } else if (i % 3 == 1) { student.setTutor(tutor2); } else { student.setTutor(tutor3); } studentRepository.save(student); } log.info("===== Test Data Initialized ====="); } }