Spring
[Springboot] JPA 주요 어노테이션
얼복무
2024. 12. 16. 19:03
어노테이션이란?
어노테이션(Annotation)은 클래스, 메서드, 필드 등에 부가적인 메타데이터를 제공하는 특별한 형태의 마커입니다.
어노테이션을 사용하면 코드의 구조나 동작을 설명하거나 특정 기능을 지정할 수 있습니다. Spring Framework와 Spring Boot에서는 어노테이션을 통해 많은 기능을 설정하고 관리할 수 있으며, 이는 코드의 가독성을 높이고 복잡성을 줄이는 데 큰 도움이 됩니다.
어노테이션을 사용하는 이유
- 설정의 간소화: XML 설정 파일을 대신하여, 어노테이션만으로 복잡한 설정을 쉽게 처리할 수 있습니다.
- 코드의 명확성: 어노테이션을 통해 클래스나 메서드의 역할과 의도를 명확히 할 수 있습니다.
- 자동화된 동작: Spring Boot가 어노테이션을 기반으로 자동 설정(Auto Configuration)을 제공하여, 개발자가 수동으로 설정할 필요를 줄여줍니다.
JPA(Java Persistence API)에서 사용되는 어노테이션은 객체와 관계형 데이터베이스 테이블 간의 매핑을 정의하는 데 사용됩니다. JPA는 객체지향 프로그래밍에서 데이터를 저장하거나 조회할 때 필요한 많은 어노테이션을 제공합니다. 아래는 주요 JPA 어노테이션에 대한 설명입니다.
1. @Entity
- 목적: 해당 클래스가 데이터베이스의 테이블과 매핑될 엔티티 클래스임을 나타냅니다.
- 사용 예:
@Entity public class Person { @Id private Long id; private String name; }
- 설명: 이 어노테이션을 사용하면 JPA가 해당 클래스를 테이블로 매핑하고, 해당 클래스의 인스턴스를 데이터베이스에 저장하거나 조회할 수 있습니다.
2. @Id
- 목적: 엔티티의 기본 키(primary key)를 지정하는 어노테이션입니다.
- 사용 예:
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
- 설명: 이 어노테이션은 엔티티에서 기본 키 역할을 하는 필드에 적용됩니다. 기본 키의 값은 데이터베이스에 의해 자동 생성되기도 합니다.
3. @GeneratedValue
- 목적: 기본 키의 값을 자동으로 생성하는 방법을 지정합니다.
- 사용 예:
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
- 설명: strategy 속성으로 자동 생성 전략을 지정할 수 있습니다.
- GenerationType.IDENTITY: 데이터베이스가 기본 키를 자동 생성.
- GenerationType.AUTO: JPA 제공자가 데이터베이스에 맞는 전략을 자동으로 선택.
- GenerationType.SEQUENCE: 시퀀스를 사용하여 기본 키를 생성.
- GenerationType.TABLE: 테이블을 사용하여 기본 키를 생성.
4. @Column
- 목적: 엔티티의 필드를 데이터베이스의 열(column)에 매핑합니다.
- 사용 예:
@Column(name = "username", nullable = false, length = 50) private String username;
- 설명: 이 어노테이션을 사용하여 컬럼의 이름, null 허용 여부, 길이 등의 속성을 지정할 수 있습니다.
5. @Table
- 목적: 엔티티가 매핑될 데이터베이스의 테이블을 지정합니다.
- 사용 예:
@Entity @Table(name = "users") public class User { @Id private Long id; }
- 설명: 이 어노테이션을 사용하면 엔티티 클래스가 매핑되는 테이블 이름을 지정할 수 있습니다. 기본적으로 클래스 이름이 테이블 이름으로 사용됩니다.
6. @OneToMany
- 목적: 1:N 관계를 정의할 때 사용됩니다.
- 사용 예:
@OneToMany(mappedBy = "user") private List<Post> posts;
- 설명: mappedBy 속성은 관계의 주체가 어떤 엔티티의 필드인지를 지정합니다.
- 한 명의 user는 여러 개의 post를 쓸 수 있기 때문에 1:N 관계.
7. @ManyToOne
- 목적: N:1 관계를 정의할 때 사용됩니다.
- 사용 예:
@ManyToOne @JoinColumn(name = "department_id") // 외래 키 설정 private Department department;
- 설명: @ManyToOne은 여러 개의 자식 엔티티가 하나의 부모 엔티티를 참조할 때 사용합니다. @JoinColumn으로 외래 키(foreign key)를 정의할 수 있습니다.
- Employee라는 테이블이 있을 때, ManyToOne 관계로 Department와 연결됩니다. 즉, 여러 직원이 하나의 부서에 속할 수 있습니다.
8. @OneToOne
- 목적: 1:1 관계를 정의할 때 사용됩니다.
- 사용 예:
@OneToOne @JoinColumn(name = "profile_id") private Profile profile;
- 설명: 두 엔티티 간의 1:1 관계를 정의하며, @JoinColumn으로 외래 키를 지정합니다.
9. @ManyToMany
- 목적: N:N 관계를 정의할 때 사용됩니다.
- 사용 예:
@Entity public class Student { @Id private Long id; private String name; // Many students can enroll in many courses @ManyToMany @JoinTable( name = "student_course", joinColumns = @JoinColumn(name = "student_id"), inverseJoinColumns = @JoinColumn(name = "course_id") ) private List<Course> courses; } @Entity public class Course { @Id private Long id; private String name; // Many courses can have many students @ManyToMany(mappedBy = "courses") private List<Student> students; }
- @JoinTable 애너테이션은 중간 테이블을 명시적으로 설정할 때 사용됩니다. @JoinTable에는 두 가지 중요한 속성이 있습니다
- joinColumns: 현재 엔티티에서 중간 테이블에 연결될 외래 키 컬럼을 지정합니다.
- inverseJoinColumns: 반대 엔티티에서 중간 테이블에 연결될 외래 키 컬럼을 지정합니다.
10. @JoinColumn
- 목적: 엔티티 간 관계에서 외래 키를 정의합니다.
- 사용 예:
@ManyToOne @JoinColumn(name = "department_id") private Department department;
- 설명: @JoinColumn을 사용하여 외래 키를 정의할 수 있습니다. 이 어노테이션은 관계형 데이터베이스에서 테이블을 연결하는 데 필요합니다.
- 주로 단방향 관계에서 사용되며, 연관된 엔티티와 연결하는 컬럼을 설정합니다.
JPA에서 단방향 관계와 양방향 관계의 차이는 관계가 정의된 방향과 연관된 엔티티들이 서로를 어떻게 참조하는지에 있습니다.
1. 단방향 관계 (Unidirectional Relationship)
단방향 관계는 한 엔티티가 다른 엔티티를 참조하는 관계입니다. 즉, 한쪽에서만 연관된 엔티티를 알고 있고, 다른 엔티티는 이 관계를 모릅니다. 한쪽 엔티티만 참조를 하며, 다른 쪽 엔티티는 관계를 알지 못합니다. 양쪽 엔티티에서 관계를 설정하지 않고, 한쪽에서만 관계를 설정합니다.
2. 양방향 관계 (Bidirectional Relationship)
양방향 관계는 두 엔티티가 서로를 참조하는 관계입니다. 즉, 두 엔티티 모두가 서로를 알고 있으며, 관계를 양방향으로 탐색할 수 있습니다. 양방향 관계를 정의할 때는 양쪽 엔티티에서 관계를 설정해야 합니다. 양방향 관계를 설정하려면 반대쪽 엔티티에서 mappedBy 속성을 사용하여 관계의 주체를 지정해야 합니다.
4. 양방향 관계에서 mappedBy의 역할
mappedBy는 반대 방향의 관계에서 관계의 주체를 지정합니다. 양방향 관계에서 중복된 외래 키가 생성되지 않도록 하고, 양쪽 엔티티에서 한쪽만 외래 키를 관리하도록 합니다. 예를 들어, @ManyToOne은 반대 방향에서 외래 키를 관리하므로, @OneToMany에서는 mappedBy로 관계를 관리하는 필드를 지정합니다.
5. 양방향 관계의 단점
데이터베이스 설계에서는 양방향 관계가 복잡성을 증가시킬 수 있습니다. 양쪽에서 연관된 데이터를 처리할 때 양방향 참조로 인해 예상치 못한 무한 루프나 오류가 발생할 수 있습니다. 예를 들어, 두 엔티티 간에 무한히 서로를 참조할 수 있는 상황을 주의해야 합니다. 단방향 관계는 데이터베이스 설계를 단순화할 수 있습니다. 관계가 한쪽에서만 정의되므로, 데이터가 불필요하게 중복될 위험이 적습니다.
11. @Embedded
- 목적: 다른 클래스를 엔티티 클래스의 일부로 포함시키는 데 사용됩니다.
- 사용 예:
@Embedded private Address address;
- 설명: @Embeddable 클래스를 엔티티에 포함시킬 때 사용됩니다. @Embeddable 클래스의 필드는 엔티티의 컬럼으로 매핑됩니다.
12. @Embeddable
- 목적: @Embedded로 포함될 수 있는 클래스를 정의하는 어노테이션입니다.
- 사용 예:
@Embeddable public class Address { private String street; private String city; }
- 설명: 이 어노테이션을 사용하여 복합 속성(예: 주소)을 엔티티에 포함시킬 수 있습니다.
13. @Transient
- 목적: 해당 필드를 데이터베이스에 매핑하지 않도록 지정합니다.
- 사용 예:
@Transient private String temporaryData;
- 설명: @Transient가 적용된 필드는 데이터베이스에 저장되지 않습니다. 보통 일시적인 데이터나 계산된 값에 사용됩니다.
- 참고: https://gmoon92.github.io/jpa/2019/09/29/what-is-the-transient-annotation-used-for-in-jpa.html
14. @Version
- 목적: 낙관적 잠금(Optimistic Lock)을 구현할 때 사용됩니다.
- 사용 예:
@Entity public class Product { @Id private Long id; private String name; @Version // 버전 필드를 사용하여 충돌을 감지 private Integer version; // Getters and Setters }
- 설명: @Version은 엔티티의 버전을 관리하여 여러 사용자가 동시에 데이터를 수정하는 경우 충돌을 방지하는 데 사용됩니다.
낙관적 락과 비관적 락
데이터베이스에서 동시성 문제를 해결하는 두 가지 주요 방식입니다. 이 두 방법은 동시에 여러 사용자가 동일한 데이터를 변경하려고 할 때 발생할 수 있는 문제를 처리하는 방법에 차이가 있습니다.
1. 낙관적 락 (Optimistic Locking)
충돌이 일어나지 않을 것이라고 가정하고, 데이터를 변경할 때까지 잠금을 사용하지 않는 방식입니다. 데이터 변경을 시도할 때 실제로 다른 트랜잭션이 데이터를 수정했는지 확인하고, 수정이 있었다면 충돌을 처리합니다.
변경 전, 데이터를 잠그지 않습니다. 대신, 데이터를 수정할 때 "변경된 상태인지"를 확인하는 방법을 사용합니다.보통 버전 번호(versioning) 또는 타임스탬프를 활용하여 데이터가 수정되었는지 확인합니다.데이터가 변경되었다면, 충돌을 감지하고 해당 트랜잭션을 롤백하거나 에러를 발생시킵니다.
낙관적이기 때문에 충돌이 거의 일어나지 않을 것이라는 가정 하에 동작합니다.충돌이 발생할 경우, 트랜잭션을 다시 시도해야 할 수 있습니다.주로 읽기가 많은 시스템에서 유용합니다.
2. 비관적 락 (Pessimistic Locking)
비관적 락은 충돌이 발생할 것이라고 가정하고, 데이터를 변경하기 전에 데이터를 잠그는 방식입니다. 즉, 한 트랜잭션이 데이터를 수정하는 동안 다른 트랜잭션은 해당 데이터를 수정할 수 없도록 합니다.
데이터가 변경될 때, 즉시 락을 걸어 다른 트랜잭션이 해당 데이터를 수정하지 못하도록 합니다.락을 해제하기 전까지는 다른 트랜잭션이 해당 데이터를 읽거나 수정할 수 없습니다.락의 종류에는 공유 락(읽기 락)과 배타 락(쓰기 락)이 있습니다.
비관적이기 때문에 충돌을 방지하기 위해 미리 데이터를 잠급니다.주로 쓰기 작업이 많거나 동시성 문제가 자주 발생하는 시스템에서 사용됩니다.
15. @Enumerated
- 목적: Enum 타입을 데이터베이스에 매핑할 때 사용됩니다.
- 사용 예:
@Entity public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Enumerated(EnumType.STRING) private Status status; // "PENDING", "APPROVED", "REJECTED" // Getters and Setters } @Entity public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Enumerated(EnumType.ORDINAL) private Status status; // 0, 1, 2 (PENDING, APPROVED, REJECTED) // Getters and Setters }
- 설명:
- EnumType.STRING: enum 값의 이름(name)을 데이터베이스에 저장합니다.
- EnumType.ORDINAL: enum 값의 순서(ordinal, 0부터 시작하는 정수값)를 데이터베이스에 저장합니다