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 λ λ€μκ³Ό κ°λ€.
- Extending and adding custom queries with JPQL
- Query Domain Specific Language (Query DSL)
- 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 μλ μ μ μ©λ λͺ¨μ΅μ λ³Ό μ μλ€.
