본문 바로가기
WEB/Spring JPA

Spring JPA 어노테이션 정리 | 종류별 설명과 예제

by 정권이 내 2023. 9. 13.

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 어노테이션을 사용할때는 mappedBy 옵션으로 연관관계의 부모를 지정할수 있고 @ManyToOne 어노테이션을 사용할때는 @JoinColumn 어노테이션으로 FK를 지정합니다.

예시로 주문과 주문 상품의 관계는 하나의 주문에 여러개의 주문상품이 존재할수 있기 때문에 1:N, N:1 관계가 성립되는데 코드로 표현 해보겠습니다.

 

예제 코드

@Entity
@Table(name = "order")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String orderNumber;

@OneToMany(mappedBy = "order")
private List<OrderItem> orderItems;
...
}

@Entity
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String productName;

@ManyToOne
@JoinColumn(name = "order_id")
private Order order;
...
}

Order, OrderItem 의 연관관계에서 Order가 부모가 되어 OrderItem에서는 mappedBy 옵션으로 Order 필드를 지정해줍니다. 그렇게 하는 이유는 Order가 부모가 되어야 OrderItem에서도 Order를 참조할수 있기 때문입니다.

OrderItem에서 Order를 참조할때 @ManyToOne 어노테이션을 사용하고 @JoinColumn 어노테이션의 name 옵션으로 FK를 지정합니다.

 

@NamedQuery

@NamedQuery 어노테이션은 엔티티 클래스에 정적인 이름으로 JPQL 쿼리를 정의할수 있고 애플리케이션 내에서 재사용할 수 있습니다.

 

예제 코드

@Entity
@NamedQuery(
name = "User.findByUsername",
query = "SELECT u FROM User u WHERE u.username = :username"
)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String username;
private String password;
...
}

User.findByUsername은 쿼리의 이름이고, SELECT u FROM User u WHERE u.username = :username 은 JPQL 쿼리입니다. 이 쿼리는 User 엔티티에서 username 필드를 기반으로 사용자를 검색하는 데 사용됩니다.

 

TypedQuery<User> query = entityManager.createNamedQuery("User.findByUsername", User.class);
query.setParameter("username", "john_doe");
List<User> users = query.getResultList();

이렇게 정의된 @NamedQuery 는 엔티티 매니저를 통해 사용할 수 있습니다:

 

@Query

@Query 어노테이션은 @Repository 메서드에서 사용자 정의 쿼리를 정의할때 사용합니다. nativeQuery 옵션에 따라 JPQL 또는 Native SQL 쿼리를 문자열로 지정할수 있습니다.

 

예제 코드

@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private double price;
...
}

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
@Query("SELECT p FROM Product p WHERE p.price < :maxPrice")
List<Product> findProductsCheaperThan(@Param("maxPrice") double maxPrice);

@Query(value = "SELECT * FROM product WHERE price < :maxPrice", nativeQuery = true)
List<Product> findProductsCheaperThanNative(double maxPrice);
}

@Query 어노테이션을 사용하여 findProductsCheaperThan, findProductsCheaperThanNative 메소드를 정의했습니다. 이 메소드들은 각각 JPQL과 NativeQuery 방식을 사용하여 Product 엔티티에서 price 필드를 기반으로 제품을 검색하는 데 사용됩니다.

@Param은 생략이 가능한데 이 경우 메소드의 매개변수 이름이 쿼리의 매개변수 이름으로 사용됩니다.

 

@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;

public List<Product> findProductsCheaperThan(double maxPrice) {
return productRepository.findProductsCheaperThan(maxPrice);
}

public List<Product> findProductsCheaperThanNative(double maxPrice) {
return productRepository.findProductsCheaperThanNative(maxPrice);
}
}

서비스단의 각 메소드는 ProductRepository의 메소드를 호출하여 제품을 검색합니다. findProductsCheaperThan 메소드는 JPQL 쿼리를 사용하고 findProductsCheaperThanNative 메소드는 Native SQL 쿼리를 사용합니다.

반응형

댓글