Problems

Service μ—μ„œ μ‚¬μš©λ˜λŠ” DAO 의 κ°œμˆ˜λŠ” μ΄μ „μ²˜λŸΌ ν•œ 개둜만 ꡬ성될 수 μžˆμ§€λ§Œ, μ‹€μ œ Real-Time Service μ—μ„œλŠ” Customer, Student, Product, Book λ“± μ—¬λŸ¬ 개의 DAO λ₯Ό μ‚¬μš©ν•œλ‹€.

κ·Έλ ‡λ‹€λ©΄ λŠ˜μ–΄λ‚˜λŠ” DAO 의 κ°œμˆ˜μ— λ”°λΌμ„œ 맀번 DAO Interface, DAO Implemetation λ₯Ό ν•΄μ£ΌλŠ” 방식은 효율적인 κ²ƒμΌκΉŒ?

기본적인 CRUD methods 듀은 μœ μ§€ν•œ μ±„λ‘œ, Entity 와 Primary Key 만 λ”°λ‘œ μ§€μ •ν•΄μ£ΌλŠ” λ°©μ‹μœΌλ‘œ μ—¬λŸ¬ 개의 DAO λ₯Ό define ν•  수 있으면 맀우 효율적일 것 κ°™λ‹€.

Spring Data JPA

Spring Data JPA κ°€ 이에 λŒ€ν•œ solution 이 될 수 μžˆλ‹€. Spring Data JPA μ—μ„œλŠ” entity type κ³Ό primary key λ₯Ό plug in 만 ν•΄μ£Όλ©΄ CRUD methods λ₯Ό μžλ™μœΌλ‘œ μ œκ³΅ν•΄μ£ΌλŠ” spring project 이닀. μ΄λŠ” boiler-plate DAO code, 즉 반볡적으둜 μž‘μ„±λ˜λŠ” code μž‘μ„±μ„ 보톡 70% λ„˜κ²Œ μ€„μ—¬μ£ΌλŠ” νš¨κ³Όκ°€ μžˆλ‹€κ³  ν•œλ‹€.

Spring Data JPA λŠ” JpaRepository λΌλŠ” Interface λ₯Ό μ œκ³΅ν•˜λŠ”λ°, 기본적인 CRUD method 듀을 μ œκ³΅ν•œλ‹€. μ‚¬μš©ν•˜λŠ” 방법도 맀우 κ°„λ‹¨ν•˜λ‹€. κΈ°μ‘΄μ—λŠ” DAO Interface λ₯Ό λ§Œλ“€κ³ , 이에 λŒ€ν•œ DAO Implementation code λ₯Ό μž‘μ„±ν•΄μ•Ό ν–ˆλ‹€λ©΄, μ•„λž˜μ™€ 같이 λ‹¨μˆœνžˆ JpaRepository interface λ₯Ό extends ν•˜λŠ” κ²ƒλ§ŒμœΌλ‘œλ„ λͺ¨λ“  μž‘μ—…μ€ μ™„λ£Œλœλ‹€.

public interface EmployeeRepository extends JpaRespository<Employee, Integer> {}

μœ„μ™€ 같이 entity type κ³Ό primary key type 만 μ§€μ •ν•΄μ£Όλ©΄ λλ‚œλ‹€. 이후에 Service Impementation code μ—μ„œ ν•΄λ‹Ή repository λ₯Ό injection ν•˜μ—¬ μ‚¬μš©ν•˜λ©΄ λœλ‹€.

Advanced Features

Spring Data JPA 의 advanced features λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

  1. Extending and adding custom queries with JPQL
  2. Query Domain Specific Language (Query DSL)
  3. Defining custom methods(low-level coding)
Using Spring Data JPA

이전에 λ§Œλ“€μ—ˆλ˜ DAO λŒ€μ‹  Spring Data JPA λ₯Ό μ‚¬μš©ν•΄λ³΄μž. μš°μ„  기쑴의 DAO Interface 와 Implementation 은 μ‚­μ œν•˜κ³ , dao package 에 EmployeeRepository λΌλŠ” Interface λ₯Ό μƒˆλ‘œ λ§Œλ“€μž. 그리고 λ‹¨μˆœνžˆ JpaRepository λ₯Ό extends ν•΄μ£Όλ©΄ Repository 에 λŒ€ν•œ μ‚¬μš© μ€€λΉ„λŠ” 벌써 끝났닀.

이후 EmployeeServiceImpl.java μ—μ„œ λ‹€μŒκ³Ό 같이 DAO λŒ€μ‹  Repository λ₯Ό injection ν•΄μ£Όλ©΄ λœλ‹€.

private EmployeeRepository employeeRepository;  
  
@Autowired  
public EmployeeServiceImpl(EmployeeRepository employeeRepository) {  
    this.employeeRepository = employeeRepository;  
}

그리고 DAO λ₯Ό μ‚¬μš©ν–ˆλ˜ code 뢀뢄듀을 employeeRepository 둜 μˆ˜μ •ν•œλ‹€. μ΄λ•Œ, findById() method λŠ” κΈ°μ‘΄ code μ—μ„œ μˆ˜μ •μ΄ ν•„μš”ν•˜λ‹€.

μš°μ„ , JpaRepository μ—μ„œ μ œκ³΅ν•˜λŠ” findById method 의 return type 이 Optional 이기 λ•Œλ¬Έμ— κΈ°μ‘΄ code 에 λŒ€ν•œ μˆ˜μ •μ΄ ν•„μš”ν•˜λ‹€.

Optional 은 null 일 μˆ˜λ„ μžˆλŠ” 값을 κ°μ‹ΈλŠ” Wrapper Class 인데, 이λ₯Ό μ΄μš©ν•˜μ—¬ μ•ˆμ „ν•˜κ²Œ null 값을 λ‹€λ£° 수 μžˆλ‹€. λ˜ν•œ Optional 은 ν•΄λ‹Ή 값이 null 이 아닐 λ•ŒλŠ” Optional.of(value) λ₯Ό ν†΅ν•˜μ—¬ 값을 return ν•˜κ³ , null 이라면 Optional.empty() λ₯Ό ν†΅ν•˜μ—¬ ν•΄λ‹Ή 값이 null μž„μ„ κ°„μ ‘μ μœΌλ‘œ μ•Œλ €μ£ΌλŠ” 것이닀.

μ‹€μ œ Optional.empty() 의 λ‚΄λΆ€ κ΅¬ν˜„μ„ 보면, return ν•˜λ €λŠ” 값이 private static final Optional<?> EMPTY = new Optional<>(null);, 즉 Optional 둜 감싸진 ν˜•νƒœμ˜ null 값이기 λ•Œλ¬Έμ— μ§μ ‘μ μœΌλ‘œ Optional 값을 null κ³Ό λΉ„κ΅ν•˜λŠ” 것은 λ°”λžŒμ§ν•˜μ§€ μ•Šλ‹€.

λ”°λΌμ„œ, Service Class μ—μ„œμ˜ findById method λ₯Ό λ‹€μŒκ³Ό 같이 μˆ˜μ •ν•΄μ•Ό ν•œλ‹€.

@Override  
public Employee findById(int theId) {  
    Optional<Employee> result = employeeRepository.findById(theId);  
    Employee theEmployee = null;  
  
    if (result.isPresent()) {  
        theEmployee = result.get();  
    }  
    else {  
        throw new RuntimeException("Did not find employee id - " + theId);  
    }  
  
    return theEmployee;  
}

μ΄λ ‡κ²Œ Option.isPresent() λ₯Ό ν†΅ν•˜μ—¬ 값이 null 인지 μ•„λ‹Œμ§€λ₯Ό νŒλ‹¨ν•˜κ³ , μ•„λ‹ˆλΌλ©΄ RuntimeException 을 λ°œμƒμ‹œν‚¨λ‹€. 결과적으둜 ν•΄λ‹Ή method λŠ” λ°˜λ“œμ‹œ 값이 μ‘΄μž¬ν•  λ•Œλ§Œ ν•΄λ‹Ή theEmployee object λ₯Ό return ν•˜κ²Œ λœλ‹€.

κ·ΈλŸ¬λ‚˜ code μžμ²΄κ°€ 약간은 κΉ”λ”ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— refactoring 을 μˆ˜ν–‰ν•  수 μžˆμ„ 것 κ°™λ‹€.

@Override
public Employee findById(int theId) { 
	return employeeRepository.findById(theId) 
		.orElseThrow(() -> new RuntimeException("Did not find employee id - " + theId));
}

μœ„μ™€ 같이 Lambda λ₯Ό μ‚¬μš©ν•˜μ—¬ functional programming 을 μ‚¬μš©ν•˜λ©΄ 훨씬 κΉ”λ”ν•˜κ³  가독성이 더 λ‚˜μ•„μ§„ λŠλ‚Œμ„ μ€€λ‹€.

Spring Data JPA - Test

μœ„μ™€ 같은 μƒνƒœμ˜ DB table μ—μ„œ, Service μ—μ„œ mapping ν–ˆλ˜ endpoints 듀을 λͺ¨λ‘ test 해보면 λͺ¨λ‘ μ„±κ³΅μ μœΌλ‘œ κ²°κ³Όκ°€ response 되고, μ•„λž˜μ™€ 같이 μ‹€μ œ DB 에도 잘 적용된 λͺ¨μŠ΅μ„ λ³Ό 수 μžˆλ‹€.