Spring Data JPA 자주 사용하는 어노테이션 정리 - 1
Spring에서 JPA를 사용하면서 @Entity, @Table, @Column 등등 여러 어노테이션을 접하게 되었는데 이 어노테이션들의 정확한 역할과 추가적으로 설정할수 있는 옵션들에 대해 자세하게 정리할 필요를 느껴서 작성하게 되었습니다.
@Entity
엔티티 클래스와 DB의 테이블을 매핑하기위해 사용하는 어노테이션 입니다. JPA 기능을 사용하기 위해서 필수적으로 들어가야하며 name 옵션으로 매핑할 DB의 테이블명을 지정할수 있습니다.
name 옵션을 사용하지 않을시 클래스 이름으로 테이블과 매핑됩니다.
예제 코드
@Entity(name = "user")
public class User {
...
}
@Table
@Entity 어노테이션과 함께 사용되며 엔티티 클래스와 매핑할 테이블의 정보를 지정할때 사용합니다.
name 옵션으로 매핑할 테이블명을 지정할수 있으며 catalog, schema, uniqueConstraints, indexes 옵션을 사용할수 있습니다.
예제 코드
@Entity
@Table(name = "user",
uniqueConstraints = {
@UniqueConstraint(columnNames = {"col1", "col2"})
},
indexes = {
@Index(columnList = "col3")
},
catalog = "test",
schema = "test")
public class User {
...
}
옵션 설명
- name : 매핑할 테이블명
- catalog : catalog 기능을 사용할때 catalog명
- schema : schema 기능을 사용할때 schema명
- uniqueConstraints : 테이블에 유니크 제약조건을 추가할때 사용합니다. @UniqueConstraint 어노테이션을 사용하며 columnNames 옵션으로 유니크 제약조건을 추가할 컬럼명을 지정합니다.
- indexes : 테이블에 인덱스를 추가할때 사용합니다. @Index 어노테이션을 사용하며 columnList 옵션으로 인덱스를 추가할 컬럼명을 지정합니다.
- uniqueConstraints와 indexes는 DDL을 자동생성할때만 사용되며 DDL을 자동생성하지 않을시에는 무시됩니다.
@Column
엔티티 클래스의 필드와 DB의 컬럼을 매핑할때 사용합니다.
name 옵션으로 매핑할 컬럼명을 지정할수 있으며 nullable, unique, length, columnDefinition, insertable, updatable, precision, scale, table, columnDefinition 등의 옵션을 사용할수 있습니다.
예제 코드
@Entity
@Table(name = "user")
public class User {
@Column(name = "email",
nullable = false,
unique = true,
length = 100,
columnDefinition = "varchar(100) default 'test'",
insertable = true,
updatable = true,
precision = 10,
scale = 2,
table = "user")
private String email;
...
}
옵션 설명
- name : 매핑할 컬럼명
- nullable : null 허용 여부
- unique : 유니크 여부
- length : 문자열 타입의 길이
- columnDefinition : 컬럼의 정보를 직접 지정할때 사용합니다. (DDL 생성시 사용)
- insertable : insert 쿼리에 포함 여부 (defalut = true)
- updatable : update 쿼리에 포함 여부 (defalut = true)
- precision : 숫자 타입의 전체 자릿수
- scale : 숫자 타입의 소수점 자릿수
- table : 컬럼이 속한 테이블명
@Id, @GeneratedValue
엔티티 클래스의 필드를 기본키(Primary Key)로 매핑할때 사용합니다.
@GeneratedValue 어노테이션에서 strategy
옵션으로 기본키 생성 방식을 지정할수 있으며 generator 옵션으로 사용할 식별자 생성기를 지정할수 있습니다.
예제 코드
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
옵션 설명
기본키 생성 전략은 다음과 같습니다.
- AUTO : DB에 맞게 자동으로 생성됩니다.
- IDENTITY : DB의 IDENTITY 컬럼을 이용하여 생성됩니다.
- SEQUENCE : DB의 SEQUENCE를 이용하여 생성됩니다.
- TABLE : 키 생성 전용 테이블을 이용하여 생성됩니다.
@Transient
엔티티 클래스의 필드를 DB의 테이블과 매핑되지 않도록 지정할때 사용합니다.
대표적인 예로 회원가입 과정에서 비밀번호 입력시 재확인용으로 두번 입력받는 경우가 있습니다. 이때 재확인용 패스워드는 실제 회원 정보 관리용도로는 필요하지 않고 일치를 확인하기 위한 용도이므로 DB에 들어갈 필요가 없는 데이터이기 때문에 매핑되지 않도록 하는것입니다.
예제 코드
@Entity
@Table(name = "user")
public class User {
@Column(name = "email")
private String email;
@Column(name = "password")
private String password;
@Transient
private String confirmPassword;
...
}
@OneToMany, @ManyToOne
엔티티 클래스의 필드와 1:N, N:1 관계의 매핑관계 정의시 중요한 역할을 합니다.
- 엔티티 간의 관계를 올바르게 설정하면 데이터베이스 조작 시 데이터 무결성 제약 조건을 준수할 수 있습니다.
- 관계를 정확하게 매핑하면 코드의 가독성을 높일 수 있으며, 엔티티 사이의 관계를 더 쉽게 이해하고 유지할 수 있습니다.
- @OneToMany: 하나의 엔티티가 여러 개의 다른 엔티티와 관계를 맺고 있음을 나타냅니다. 예를 들어, 부서를 나타내는 Department 엔티티가 여러 개의 Employee 엔티티를 가지는 경우입니다.
- @ManyToOne: 여러 개의 엔티티가 하나의 다른 엔티티와 관계를 맺고 있음을 나타냅니다. 예를 들어, 여러 개의 Employee 엔티티가 하나의 Department 엔티티에 속할 수 있습니다.
예시로 주문과 주문 상품의 관계는 하나의 주문에 여러개의 주문상품이 존재할수 있기 때문에 1:N, N:1 관계가 성립되는데 코드로 표현 해보겠습니다.
예제 코드
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Employee> employees = new ArrayList<>();
// getters and setters
}
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "department_id")
private Department department;
// getters and setters
}
- Department 엔티티는 @OneToMany 애노테이션을 사용하여 여러 Employee 엔티티와의 관계를 정의합니다.
- Employee 엔티티는 @ManyToOne 애노테이션을 사용하여 하나의 Department 엔티티와의 관계를 정의합니다.
애노테이션 부가 옵션 설명
- @JoinColumn : 관계의 외래 키를 지정하며, 여기서는 department_id가 외래 키로 사용됩니다.
- mappedBy : 관계의 주인이 아님을 나타내며, Employee 엔티티의 department 필드가 관계의 주인임을 지정합니다.
- cascade : Department 엔티티가 저장되거나 삭제될 때 관련된 Employee 엔티티도 함께 저장되거나 삭제되도록 합니다.
- orphanRemoval : Department와의 관계가 끊어진 Employee 엔티티가 자동으로 삭제되도록 합니다.
@NamedQuery
@NamedQuery 어노테이션은 JPA(Java Persistence API)에서 정적 쿼리를 미리 정의하고 재사용할 수 있게 해주는 기능입니다. 엔티티 클래스에 붙여서, 엔티티 클래스에 대한 정적 쿼리를 이름과 함께 정의할 수 있습니다. 미리 정의된 쿼리를 사용하면 코드의 가독성이 좋아지고, 쿼리 재사용성도 높아집니다.
예제 코드
- @NamedQuery 애노테이션은 엔티티 클래스에 붙입니다.
- @NamedQueries 애노테이션을 사용해서 여러 개의 @NamedQuery를 정의할 수 있습니다.
@Entity
@NamedQueries({
@NamedQuery(
name = "Employee.findAll",
query = "SELECT e FROM Employee e"
),
@NamedQuery(
name = "Employee.findByName",
query = "SELECT e FROM Employee e WHERE e.name = :name"
)
})
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// other fields, getters, and setters
}
- Employee.findAll : 모든 Employee 엔티티를 조회하는 쿼리
- Employee.findByName : 주어진 이름을 가진 Employee 엔티티를 조회하는 쿼리
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
// 메서드 이름을 쿼리 이름과 일치시키면 Spring Data JPA가 자동으로 매핑합니다.
List<Employee> findAll();
List<Employee> findByName(@Param("name") String name);
}
- 리포지토리 인터페이스의 메서드 이름이 @NamedQuery의 이름과 일치하면, Spring Data JPA가 자동으로 매핑합니다.
- @Param : 쿼리에 파라미터를 전달할 때는 @Param 애노테이션을 사용하여 파라미터 이름을 명시합니다.
@NameQuery 장점
- 재사용성: 정적 쿼리를 재사용할 수 있어 코드 중복을 줄일 수 있습니다.
- 유지보수성: 쿼리가 엔티티 클래스에 정의되어 있어 엔티티와 관련된 쿼리를 한 곳에서 관리할 수 있습니다.
- 가독성: 쿼리가 명시적으로 이름으로 정의되어 있어, 쿼리의 의도를 쉽게 파악할 수 있습니다.
@NameQuery 단점
- 유연성 부족: 런타임에 동적으로 변경할 수 없기 때문에, 동적인 조건이 필요한 경우에는 적합하지 않습니다.
- 컴파일 타임 검사 없음: 쿼리 문자열이 컴파일 타임에 검증되지 않기 때문에, 쿼리 문자열의 오류는 런타임에 발견될 수 있습니다.
@Query
- @Query 애노테이션은 Spring Data JPA에서 커스텀 JPQL (Java Persistence Query Language) 또는 SQL 쿼리를 리포지토리 메서드에 직접 정의할 때 사용됩니다. 복잡한 쿼리나 표준 메서드 이름 쿼리로 표현할 수 없는 쿼리를 정의할 수 있습니다.
JPQL 쿼리 사용 예제
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
// JPQL 쿼리 사용 예제
@Query("SELECT e FROM Employee e WHERE e.name = :name")
List<Employee> findByName(@Param("name") String name);
// JPQL 쿼리로 특정 조건을 가진 엔티티를 찾는 예제
@Query("SELECT e FROM Employee e WHERE e.salary > :salary")
List<Employee> findBySalaryGreaterThan(@Param("salary") double salary);
}
네이티브 SQL 쿼리 예제
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
// 네이티브 SQL 쿼리 사용 예제
@Query(value = "SELECT * FROM employees WHERE name = :name", nativeQuery = true)
List<Employee> findByNameNative(@Param("name") String name);
// 네이티브 SQL 쿼리로 특정 조건을 가진 엔티티를 찾는 예제
@Query(value = "SELECT * FROM employees WHERE salary > :salary", nativeQuery = true)
List<Employee> findBySalaryGreaterThanNative(@Param("salary") double salary);
}
파라미터 바인딩
// 이름 기반 파라미터 바인딩
@Query("SELECT e FROM Employee e WHERE e.name = :name")
List<Employee> findByName(@Param("name") String name);
// 위치 기반 파라미터 바인딩
@Query("SELECT e FROM Employee e WHERE e.name = ?1 AND e.department = ?2")
List<Employee> findByNameAndDepartment(String name, String department);
@Query 장점
- 간결함: 복잡한 쿼리를 리포지토리 메서드에 직접 정의할 수 있어 코드가 간결해집니다.
- 유연성: 표준 메서드 이름 쿼리로 표현할 수 없는 복잡한 쿼리를 정의할 수 있습니다.
- 유지보수성: 쿼리가 리포지토리 메서드에 직접 정의되어 있어 유지보수가 용이합니다.
@Query 단점
- 오타 위험: 쿼리를 직접 작성할 때 오타나 문법 오류가 발생할 수 있습니다.
- 테스트 어려움: 쿼리의 복잡성이 증가하면 테스트가 어려워질 수 있습니다.
'WEB > Spring JPA' 카테고리의 다른 글
Spring JPA Criteria API 소개와 예제 (0) | 2024.06.07 |
---|
댓글