임도현의 성장
[Spring-Boot] 스프링 데이터 JPA 본문
👻 JPA 란?
JPA(Java Persistence API)는 자바 진영에서 ORM(Object-Relational Mapping)을 통해 관계형 데이터베이스(RDBMS)와 객체(Entity)를 쉽게 다룰 수 있도록 도와주는 표준 API이다.
🍄 ORM(객체 관계 매핑) 란?
- 객체(Entity)와 데이터베이스 테이블을 자동으로 매핑하는 기술
- SQL을 직접 작성하지 않아도 객체를 저장, 조회, 수정, 삭제 가능
👉 Entity 란?
- 데이터베이스의 테이블과 매핑되는 자바 객체
- @Entity 어노테이션을 사용해 JPA가 관리하도록 설정
😮 JPQL 란?
- JPQL은 SQL과 비슷하지만 객체를 대상으로 하는 쿼리 언어이다.
- JPA에서 SQL이 제공하지 않는 기능을 필요로 할 때 주로 사용한다.
- @Query 어노테이션을 붙히고 JPA 엔티티 객체를 대상으로 쿼리를 작성 해야합니다.
🦖 JPA를 사용하는 이유?
- SQL을 직접 작성할 필요 없이 객체 지향적으로 데이터 관리 가능
- 변경 감지, 지연 로딩 같은 기능 지원
- DB 변경 시 코드 수정 최소화 (유지보수가 좋음)
🥥 JPA의 동작
JPA는 어플리케이션과 JDBC API 사이에서 동작하는 API로 어플리케이션의 객체를 관계형 데이터베이스의 테이블에 매핑하여 JPA 프로바이더(Hibernate)가 내부적으로 JDBC를 사용해 데이터베이스와 데이터를 주고받습니다.

😎 JPA ORM 매핑
@Entity : JPA가 사용하는 객체라는 뜻이다. 이 에노테이션이 있어야 JPA가 인식할 수 있다.
@Id : 테이블의 PK와 해당 필드를 매핑한다.
@GeneratedValue(strategy = GenerationType.IDENTITY) : PK 생성 값을 데이터베이스에서 생 성하는 IDENTITY 방식을 사용한다.
@Column : 객체의 필드를 테이블의 컬럼과 매핑한다.
@OneToMany : 하나의 엔티티와 여러개의 엔티티와 1 : N 관계를 맺을 때 사용하는 JPA 어노테이션이다.
@ManyToOne : 반대로 N : 1 관계를 맺을 때 사용하는 JPA 어노테이션이다.
@JoinColumn : 외래 키로 사용될 컬럼을 지정하는 JPA 어노테이션이다.
@NoArgsConstructor @AllArgsConstructor
@Builder @Getter @Setter
@Entity
@Table(name = "school")
public class School {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "school_id")
private Long id;
@Column(name = "school_name")
private String name;
@OneToMany(mappedBy = "school")
private List<Student> studentList = new ArrayList<>();
}
@NoArgsConstructor @AllArgsConstructor
@Builder @Getter @Setter
@Entity
@Table(name = "student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "student_id")
private Long id;
@Column(name = "student_name")
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "school_id") // 외래 키 컬럼 이름
private School school;
}
👩💻 Service에서 JPA호출
studentRepository.findById(id) : id를 기준으로 Student 엔티티를 데이터베이스에서 조회합니다. 만약 해당 id에 맞는 학생이 없다면 RuntimeException 을 던져 오류를 처리합니다.
반환 할 때는 조회된 Student 엔티티를 StudentDto로 변환하여 리턴합니다.
@Service
@RequiredArgsConstructor
public class StudentService {
private final StudentRepository studentRepository;
public StudentDto findStudent(Long id) {
long count = studentRepository.countStudent();
Student student = studentRepository.findById(id).orElseThrow(() -> new RuntimeException("Member not found!"));
return new StudentDto(student);
}
}
@ToString
@Getter
public class StudentDto {
private Long id;
private String name;
private String schoolName;
public StudentDto(Student student) {
this.id = student.getId();
this.name = student.getName();
this.schoolName = student.getSchool().getName();
}
}
🙊 JpaRepository 상속 받기
JpaRepository<T, ID>를 상속받으면 기본적인 데이터 처리 메서드(CRUD, 페이징, 정렬 등)를 자동으로 사용할 수 있어즉, SQL 쿼리를 직접 작성하지 않아도 기본적인 DB 작업이 가능하다는 장점이 있습니다.
countStudent 인테페이스는 SQL이 제공하지 않는 기능이여서 JPQL로 작성하였습니다. @Query 어노테이션을 붙히고 JPA 엔티티 객체를 대상으로 쿼리를 작성 해야합니다.
@Repository
public interface StudentRepository extends JpaRepository<Student, Long> {
@Query("SELECT COUNT(m) FROM Student m")
long countStudent();
}
💩 내 생각 정리
JPA를 사용하면 CRUD, 페이징 처리 같은 기능들도 제공을해주어 증복되는 코드도 개선해주며 객체 지향적인 접근 방식으로 데이터를 다룰 수 있어 유연한 설계가 가능해 졌다. 하지만 JPA는 공부해야 할게 많은거 같다. ORM에 대해서 확실하게 알아야 하며 엔티티 또한 잘 설정을 해주어야 데이터베이스 변경에 대한 유연한 대응이 가능하게 되어 엔티티를 최소한의 수정을 통해 유지보수가 좋아지는거 같다.
'Spring Boot' 카테고리의 다른 글
[Spring-Boot] QueryDsl 적용해보기 (0) | 2025.02.17 |
---|---|
[Spring-Boot] Prometheus + Grafana 모니터링 구축 (1) | 2025.02.09 |
[Spring-Boot] Exception 예외처리 (0) | 2025.01.14 |
[Spring-Boot] Redis에 저장 조회 해보기 (1) | 2025.01.11 |
[Spring-Boot] Cross-Origin Resource Sharing (0) | 2024.12.25 |