JPA는 자바 표준 ORM(Object Relational Mapping)이다.
JPA를 통해 쿼리문 대신 자바 코드를 통해 DB의 데이터를 다룰 수 있다.
엔티티는 데이터베이스 테이블과 매핑되는 자바 클래스를 말한다.
ERD를 통해 구성한 DB의 구조대로 엔티티를 작성하면 된다.
엔티티로 만들 클래스에 @Entity
어노테이션을 붙이면 그 클래스를 엔티티로 사용할 수 있다.
@Setter | business logic | 특징 | |
---|---|---|---|
entity | X | O | DB 테이블 매칭 |
dto | O | X | 계층간 데이터 전달 (ex. view와 controller 사이) |
엔티티의 경우 값을 함부러 변경하여 일관성과 안전성을 해치면 안되기 때문에 @Setter
어노테이션 사용을 지양한다. 따라서 대신 사용하는 것이 Lombok의 @Builder
어노테이션이다. @Builder
를 사용할 경우 자동으로 builder 패턴을 생성해준다.
리포지터리는 DB의 데이터를 처리하기 위한 인터페이스다. 엔티티는 단순히 DB와 매핑이 되는 클래스이기 때문에 실질적으로 데이터를 가져오고 처리하는 동작들은 리포지터리에서 이루어진다.
// QuestionRepository.java
public interface QuestionRepository extends JpaRepository<Question, Integer> {
}
interface QuestionRepository extends JpaRepository
QuestionRepository 인터페이스가 리포지터리가 되기 위하여 JpaRepository 인터페이스를 상속받아야 한다.
<Question, Long>
JpaRepository를 생성하기 위하여 <엔티티 타입, 해당 엔티티의 PK 속성의 타입>
을 지정해준다. 따라서 이 경우 "Question 테이블" 과 Question 테이블의 PK인 id의 타입인 "Long" 를 적어준다.
// SbbApplicationTests.java
@SpringBootTest
class SbbApplicationTests {
@Autowired // questionRepository 객체를 자동 생성
private QuestionRepository questionRepository;
@Test
void testJpa() {
Question q1 = new Question();
q1.setSubject("제목 설정");
q1.setContent("내용 설정");
q1.setCreateDate(LocalDateTime.now()); // 현재 시간 저장
this.questionRepository.save(q1); // 저장
Question q2 = new Question();
q2.setSubject("제목 설정2");
q2.setContent("내용 설정2");
q2.setCreateDate(LocalDateTime.now());
this.questionRepository.save(q2);
}
}
// findAll
List<Question> all = this.questionRepository.findAll();
assertEquals(2, all.size());
// findBy + ()
// QuestionRepository.java 에 Question findBySubject(String subject); 추가
Question q = this.questionRepository.findBySubject("sbb가 무엇인가요?");
assertEquals(1, q.getId());
Optional<Question> oq = this.questionRepository.findById(1);
assertTrue(oq.isPresent());
Question q = oq.get();
q.setSubject("수정된 제목");
this.questionRepository.save(q);
이미 저장되어 있는 값을 get으로 가져와 set으로 값을 변경하고 save를 하면 Update 쿼리가 실행된다.
( Optional : null 처리를 유연하게 처리하기 위함 )
assertEquals(2, this.questionRepository.count());
Optional<Question> oq = this.questionRepository.findById(1);
assertTrue(oq.isPresent());
Question q = oq.get();
this.questionRepository.delete(q);
assertEquals(1, this.questionRepository.count());
delete()를 통해 데이터를 삭제할 수 있다.
앞뒤에 count()를 통해 데이터 수의 변화를 확인할 수 있다.
테스트 코드에서는 DB 연결이 끊어지는 문제가 발생한다. 따라서 메서드가 종료될 때까지 DB 세션이 유지하기 위하여 @Transactional
어노테이션을 사용한다. (실제 환경에서는 DB 연결이 끊기지 않아 사용하지 않아도 된다.)
참고
점프 투 스트링부트 : https://wikidocs.net/161165
점프 투 스프링부트 : https://wikidocs.net/160890
빌더 패턴 : https://velog.io/@park2348190/Lombok-Builder의-동작-원리