안녕하세요. 오늘은 jpa를 사용하여 엔티티와 테이블을 매핑하기 위한 어노테이션에 대해 알아보려합니다.
김영한님의 자바 ORM 표준 JPA 프로그래밍을 읽고 정리했습니다.
@Entity
jpa를 사용하여 테이블과 매핑하기 위해서는 @Entity 어노테이션을 붙여야한다. 이 어노테이션이 붙은 테이블은 jpa가 관리하며 엔티티라고 부른다.
엔티티 클래스를 사용할 때는 세 가지의 주의사항이 있다.
1. 기본 생성자는 필수
jpa가 엔티티 객체를 생성할 때 기본 생성자를 이용하므로 이 생성자는 반드시 있어야 한다.
자바는 생성자가 없으면 기본 생성자를 자동으로 생성한다. 하지만 임의의 생성자를 만들었다면 기본 생성자를 만들어야 한다.
public Member() {} // 기본 생성자
public Member(String name) { // 임의로 만든 생성자
this.name = name;
}
2. final 클래스, enum, interface, inner 클래스에는 사용 불가
3. 저장할 필드에 final을 사용하면 안됨
@Table
@Table은 엔티티와 매핑할 테이블을 지정하는 어노테이션이다.
속성 | 기능 | 기본값 |
name | 매핑할 테이블 이름 | 엔티티 이름을 사용 |
catalog | catalog 기능이 있는 데이터베이스에서 catalog를 매핑 | |
schema | scheme 기능이 있는 데이터베이스에서 schema 매핑 | |
uniqueConstraints | DDL 생성 시 유니크 제약조건을 만든다. 이 기능은 스키마 자동 생성 기능을 사용해서 만들 때만 사용된다. |
데이터베이스 스키마 자동 생성
jpa는 스키마 자동 생성 기능을 지원한다.
.yml 파일에 아래와 같이 추가하면 콘솔에 실행되는 sql을 출력할 수 있다.
spring:
jpa:
show-sql: true
.yml 파일에 아래와 같이 설정하면 스키마 자동 생성 기능을 이용할 수 있다.
spring:
jpa:
hibernate:
ddl-auto: create
아래는 ddl-auto 속성이다.
create : 기존 테이블 삭제하고 새로 생성
create-drop : 생성 후 종료할 때 ddl 제거
update : 변경 사항만 수정
validate : 데이터베이스 테이블과 엔티티 매핑 정보가 다르면 어플리케이션 실행하지 않음
none : 자동 생성 기능을 사용하지 않음
DDL 생성 기능
DDL에 제약 조건을 추가해보려고 한다.
컬럼이 필수로 입력되어야 하고, 10자를 초과하지 않는 제약 조건을 실행하려면 어떻게 해야할까?
@Column(name = "NAME", nullable = false, length = 10)
private String username;
nullable을 false로 두면 not null 조건을 추가할 수 있다. 그리고 length로 문자의 크기를 지정할 수 있다.
유니크 제약 조건을 만들어 주는 @Table의 uniqueConstraints 속성을 알아보자.
@Entity(name="MEMBER")
@Table(name = "MEMBER", uniqueConstraints = {@UniqueConstraint(
name = "NAME_AGE_UNIQUE",
columnNames = {"NAME", "AGE"})})
public class Member {
}
유니크 제약 조건으로 실행된 DDL 쿼리이다.
ALTER TABLE MEMBER
ADD CONSTRAINTS NAME_AGE_UNIQUE UNIQUE(NAME, AGE)
이런 기능들은 DDL 생성을 자동으로 생성할 때만 사용되고 JPA 실행 로직에는 영향을 주지 않는다.
기본 키 매핑
@Id 어노테이션을 사용하여 기본 키를 어플리케이션에 직접 할당할 수 있다.
직접 할당하는 경우에 기본 키 없이 영속성 컨텍스트에 저장하려고 하면 예외가 발생한다.
@Id(name = "ID")
private String id;
그러나 기본 키를 데이터베이스에서 생성해주는 값을 사용하려면 어떻게 해야할까?
IDENTITY 전략
IDENTITY는 기본 키 생성을 데이터베이스에 위임하는 전략이다. 주로, MySQL, PostgreSQL, SQL Server 등에서 사용한다. (MySQL의 auto_increment와 같은 기능)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
jpa에서 기본 키 전략을 identity로 사용하게 되면 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.
왜냐하면 엔티티가 영속 상태가 되려면 식별자 값이 있어야하는데 엔티티를 데이터베이스에 저장해야 식별자 값을 알 수 있기 때문이다.
따라서 em.persist() 를 호출하는 즉시 insert 쿼리가 데이터베이스로 날아간다.
private static void logic(EntityManager em) {
Board board = new Board();
em.persist(board);
System.out.println("board.id = " + board.getId());
}
identity 전략에서 Statement.getGeneratedKeys()를 사용하면 데이터를 저장하면서 동시에 생성된 기본 키 값을 가져올 수 있어서 최적화 할 수 있다.
SEQUENCE 전략
데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트다. 이 전략은 시퀀스를 지원하는 오라클, PostgreSQL, H2 데이터베이스에서 사용할 수 있다.
@Entity
@SequenceGenerator(
name = "BOARD_SEQ_GENERATOR",
sequenceName = "BOARD_SEQ",
initialValue = 1, allocationSize = 1)
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "BORAD_SEQ_GENERATOR")
private Long id;
}
@SequenceGenerator을 사용해서 시퀀스 생성기를 등록하고, 키 생성 전략을 Sequence로 설정한다. 이 때, generator를 방금 생성한 시퀀스로 선택했다.
시퀀스를 사용하는 코드는 IDENTITY와 동일하지만 내부 동작은 다르다.
private static void logic(EntityManager em) {
Board board = new Board();
em.persist(board);
System.out.println("board.id = " + board.getId());
}
em.persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용하여 식별자를 조회한다. 그리고 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장한다.
이후 트랜잭션을 커밋하여 플러시가 일어나면 데이터베이스에 sql을 보낸다.
TABLE 전략
TABLE 전략은 키 생성 전용 테이블을 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략이다.
이 전략은 모든 데이터베이스에서 적용할 수 있다.
@Entity
@TableGenerator(
name = "BOARD_SEQ_GENERATOR",
table = "MY_SEQUENCES",
pkColumnValue = "BOARD_SEQ", allocationSize = 1)
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "BORAD_SEQ_GENERATOR")
private Long id;
}
사용하는 코드 또한 동일하며 테이블을 사용했다는 것을 제외하면 내부 동작 방법은 시퀀스와 동일하다.
private static void logic(EntityManager em) {
Board board = new Board();
em.persist(board);
System.out.println("board.id = " + board.getId());
}
AUTO 전략
AUTO 전략은 선택한 데이터베이스마다 알아서 테이블 전략을 선택한다.
예를 들면 MySQL은 IDENTITY를 오라클은 SEQUENCE를 사용한다.
필드와 컬럼 매핑 : 레퍼런스
필드는 사용할 일이 있을 때 자세히 읽어보는 게 좋다.
@Column
@Column은 필드를 테이블 컬럼에 매핑한다. 속성 중에 name, nullable이 가장 많이 사용된다.
@Column을 생략하게 되면 속성의 기본값이 적용된다.
int 타입에는 null 값을 입력할 수 없다. 따라서 int 필드에 @Column을 생략했을 때, not null 제약 조건을 알아서 추가했다.
하지만 필드를 int로 지정했으나 @Column 에 nullable을 지정하지 않으면 데이터베이스에서 만들어진 컬럼은 null이 허용된다.
따라서 자바 기본 타입을 사용하면 nullable = false를 지정하는 것이 안전하다.
int data1; // 자바 기본 타입
data1 integer not null // 생성된 ddl
Integer data2; // 자바 객체 타입
data2 integer // 생성된 ddl
@Column // 생략되지 않은 @Column 어노테이션
int data3; // 자바 기본 타입
data3 integer // 생성된 ddl - null이 허용됨
@Enumerated
자바 enum 타입을 매핑할 때 사용된다.
enum RoleType {
ADMIN, USER
}
EnumType.ORDINAL로 저장할 경우 enum 순서에 맞게 0,1 숫자로 저장된다.
EnumType.STRING으로 지정할 경우 이름 그대로의 값을 저장한다.
@Enumerated(EnumType.STRING)
private RoleType roleType;
enum은 아래와 같이 사용되며 데이터베이스에는 ADMIN으로 저장된다.
member.setRoleType(RoleType.ADMIN);
@Temporal
날짜 타입을 매핑할 때 사용된다.
@Temporal(TemporalType.DATE)
private Date date; // 날짜
@Temporal(TemporalType.TIME)
private Date time; // 시간
@Temporal(TemporalType.TIMESTAMP)
private Date timestamp; // 날짜와 시간
@Lob
데이터베이스의 BLOB, CLOB 타입과 매핑한다.
@Transient
이 필드는 매핑하지 않는다. 데이터베이스에 저장하지 않고 조회하지도 않는다. 객체에 임시로 어떤 값을 보관하고 싶을 때 사용한다.
@Access
jpa가 엔티티 데이터에 접근하는 방식을 지정한다.
AccessType.FIELD : 필드에 직접 접근한다.
AccessType.PROPERTY : 접근자를 사용한다.
@Access를 지정하지 않으면 @Id 위치를 기준으로 접근 방식이 설정된다.
@Entity
@Access(AccessType.FIELD)
public class Member {
@Id
private String id;
}
@Entity
@Access(AccessType.PROPERTY)
public class Member {
private String id;
@Id
public String getId() {
return id;
}
}
두 코드 모두 Access가 생략 가능하다.
스터디 할 때 만든 피피티 자료입니다.
'🍀spring > 스프링 jpa' 카테고리의 다른 글
[Spring JPA] 프록시와 연관관계 관리 (0) | 2024.03.03 |
---|---|
[Spring JPA] 고급 매핑 (상속, 복합 키 매핑) (0) | 2024.02.29 |
[Spring JPA] 다양한 연관관계 매핑 (1) | 2024.02.29 |
[Spring JPA] 연관관계 매핑 기초 (1) | 2024.02.28 |
[Spring JPA] 영속성 관리 (2) | 2024.02.25 |