Entity
엔티티(Entity)는 데이터베이스의 특정 테이블을 자바 클래스 형태로 표현한 것
create table boards(
board_id bigint not null primary key auto_increment,
name varchar(20) not null
);
위의 sql문을 java의 Spring으로 표현하면 다음과 같다
@Getter
@Setter
@Entity
@Table(name = "boards")
public class Board {
private Long boardId;
private String name;
}
@Id
기본키를 정의하는 필드를 사용할 때 선언.
기본 키 필드는 보통 @GeneratedValue을 통해 자동으로 생성.
@Getter
@Setter
@Entity
@Table(name = "boards")
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.사용할생성전략)
private Long boardId;
private String name;
생성 전략
@GeneratedValue에도 종류가 있는데 일반적으로 auto_increment와 비슷한 @GeneratedValue의 IDENTITY 전략을 사용한다.
- 자동 증가 전략 (jakarta.persistence.GenerationType.AUTO) 데이터베이스의 기본키 생성 규칙을 자동으로 결정하며, 데이터베이스가 기본키를 생성하는 방식을 따르게 된다.
- 시퀀스 전략 (jakarta.persistence.GenerationType.SEQUENCE) 기본키 값을 데이터베이스 시퀀스 객체를 사용하여 자동으로 생성하는 전략 @SequenceGenerator를 통하여 시퀀스의 이름과 시작 값 등을 자세히 정의하는 것도 가능 생성된 시퀀스를 사용하여 각 엔티티의 기본키 값을 생성
- 식별자 전략 (jakarta.persistence.GenerationType.IDENTITY) 기본키 필드에 대한 자동 증가 속성(auto increment)과 같음
기타 전략들
- 테이블 전략 (jakarta.persistence.GenerationType.TABLE) 시퀀스 전략과 유사하게 @TableGenerator를 통하여 기본키 값을 생성할 테이블과 관련된 설정을 직접 정의할 수 있다. 테이블 전략은 DBMS 제품군과는 상관 없이 개발 전략에 맞추어 취하는 방법
- UUID 전략 (jakarta.persistence.GenerationType.UUID) 기본키 값을 UUID로서 생성하여 할당하는 전략
@Column
@Column은 테이블 컬럼의 데이터 타입과 제약 조건을 정의할 때에도 사용
nullable, columnDefinition, unique와 같은 제약조건 속성
@Getter
@Setter
@Entity
@Table(name = "boards")
public class Board {
private Long boardId;
@Column(nullable = false, length = 50)
private String name;
}
EntityManager
- 엔티티 객체의 생명주기를 관리하고 레코드와 객체 간 매핑 및 조작을 쉽게 수행할 수 있도록 도와준다.
- **영속성 컨텍스트(Persistence Context)**를 통해 엔티티 객체의 상태를 관리를 한다.
- 1차 캐시 역할을 하여 db의 불필요한 트랜잭션을 줄이고 성능을 향상시킨다
- 트랜잭션 관리를 담당하며 트랜잭션의 **시작(BEGIN), 커밋(COMMIT), 롤백(ROLLBACK)**을 제어한다.
영속성 컨텍스트(Persistence Context)란?
엔티티 매니져가 관리하는 객체이다.
- 엔티티 객체의 상태를 추적하고, 해당 엔티티가 언제, 어떻게 데이터베이스와 상호작용 하는지를 결정한다.
- 데이터베이스에 불필요한 접근을 줄이고, 성능을 최적화하며, 데이터의 일관성을 보장하는 역할을 수행한다.
- 영속성 컨텍스트는 기본적으로 세션(Session)이라고도 불리며, 이 세션은 엔티티 객체를 캐싱하는 역할을 수행한다.
- 트랜잭션이 시작되면 영속성 컨텍스트가 생성되며, 트랜잭션이 종료될 때까지 유지된다.
- 1차 캐시로 작동되어 동일한 컨텍스트 내에서 여러번 조회할 때 캐시된 엔티티를 반환하여 성능을 최적화한다.
영속성 컨텍스트의 엔티티 관리
엔티티는 컨텍스트 내에서 크게 4가지 상태로 관리된다.
- 비영속 상태(transient) 엔티티 객체가 아직 영속성 컨텍스트에서 관리하지 않는 상태. 데이터 베이스에 저장이 되거나 하지 않는다.
- 영속(관리)(managed) 영속성 컨텍스트에 의해 엔티티 객체가 관리되고 있는 상태. 엔티티의 변경사항을 추적(변경감지)하며, 트랜잭션이 커밋 될 때 변경사항을 자동 반영한다.
- 준영속(detached) 엔티티가 영속성 컨텍스트의 생명주기를 벗어나거나 명시적으로 분리 된 경우. 필드 변화가 일어난 후 커밋이 발생하더라도 관리를 받지 않고있기 때문에 변경사항이 반영 x 다시 반영해야 한다면 준영속 상태에서 병합(merge)를 통해 영속상태로 변경해야 한다.
- 삭제(removed) 영속성 컨텍스트 뿐 아니라 db에서도 삭제하기 위한 상태. 제 상태에서 엔티티는 트랜잭션이 커밋될 때 데이터베이스에서 삭제된다.
변경 감지
영속성 컨텍스트에 의해 관리되는 엔티티의 상태변화를 영속성 컨텍스트가 자동으로 감지하고 db에 반영하는 것을 의미한다.
- 엔티티 객체의 상태를 추적하기 위해 모든 필드 값들이 스냅샷으로 저장된다.
- 스냅샷
- 엔티티 초기 상태를 복사한 것
- 영속성 컨텍스트가 가장 최신의 스냅샷과 비교하여 어떤 필드가 변경되었는지 검사하고 컴밋할 때 db에 반영된다.
연관관계
db에서 테이블간의 관계(외래키와 같은)를 엔티티간의 관계로 표현한 개념이다.
일대일 연관관계(One to One)
@OneToOne 락커와 회원의 연관관계
@Entity
public class Locker {
...
}
@Entity
public class Member {
...
private Grade memberType;
@OneToOne
private Locker locker;
}
일대다, 다대일 (One to Many, Many to One)
하나의 팀에는 많은 선수들이 있고, 많은 선수들은 하나의 팀에 속해있다.
@Entity
public class BaseBallPlayer {
...
@ManyToOne
private BaseBallTeam team;
...
}
@Entity
public class BaseBallTeam {
...
@OneToMany
private List<BaseBallPlayer> players;
...
}
일대다로 매핑할 경우에는 연관관계의 대상이 되는 객체를 List<> 타입의 필드로서 선언한다는것
꼭 List<>일 필요는 없지만 반드시 Collection타입이어야 한다.
다대다 @ManyToMany는 자바는 쉽게 표현 가능한 상황이지만 db에서는 어렵기 때문에 잘 사용하지 않고 만약 필요하다면 다대다 관계를 풀어서 일대다 + 다대일 관계로 처리한다. -> 중간 테이블을 만들어서 관리한다
연관관계 소유자
연관관계를 맺는 두 엔티티 객체 사이에 어느 엔티티가 외래키를 갖고 있는지를 결정하는 주체이다.
@OneToOne, @OneToMany, @ManyToOne, @ManyToMany을 통해서 엔티티 간의 관계를 정의하였는데 여기서 속성 mappedBy 속성을 통해 연관관계의 외래키 소유자를 명시할 수 있다.
다음 예시들은 모두 양방향 매핑을 기준으로 표현한 것 이다.
양방향 매핑 : 서로가 서로의 참조를 볼 수 있어 team에서도 선수를 가져와 사용할 수 있고 선수도 팀의 정보를 가져와 사용할 수 있는 관계로 보면 된다.
일대일 관계
mappedBy 속성을 통해 Locker는 OneToOne으로 연결된 필드의 이름 locker의 이름을 통해 연결하고 JoinColumn을 통해 외래키 필드의 이름을 locker_id로 설정한다.
@Entity
public class Locker {
...
@OneToOne(mappedBy = "locker") // OneToOne으로 연결된 Locker필드의 이름
private Member member;
...
}
@Entity
public class Member {
...
private Grade memberType;
@OneToOne
@JoinColumn(name = "locker_id") // 외래키의 이름 설정
private Locker locker;
...
}
다대일, 일대다 관계 @ManyToOne , @OneToMany
다음과 같이 Post테이블은 Board테이블의 번호를 참조하여 외래키로 가지고 있다.
< 표 8 > 게시판(Board) 테이블
번호 이름
| 1 | 공지 사항 |
| 2 | 자유 게시판 |
< 표 9 > 게시글(Post) 테이블
번호 제목 내용 게시판 번호
| 1 | 첫 번째 공지사항 | 첫 공지입니다. 안녕하… | 1 |
| 2 | Spring은 즐거워요 | Spring이 이렇게… | 2 |
위의 테이블의 외래키 설정을 나타내면 다음과 같다.
@Entity
public class Board {
private int id;
private String name;
@OneToMany(mappedBy = "board")
private List<Post> post;
}
@Entity
public class Post() {
private int id;
private String title;
private String conetent;
@ManyToOne
@JoinColumn(name = "board_id", nullable = false) // 외래키 설정
private Board board;
}
공통속성 정의(@MappedSuperclass)
@MappedSuperclass은 상속 구조에서 공통 속성을 정의할 때 사용하는 방법이다.
클래스가 직접적으로 테이블과 매핑되지 않으면서도 자식 클래스에 공통 속성을 제공할 수 있도록 한다.
즉 @MappedSuperclass가 적용된 클래스는 엔티티(Entity)가 아니며, 데이터베이스 테이블로 매핑되지 않지만 이를 상속받는 자식 클래스는 부모 클래스의 필드를 자신의 테이블에 포함시키게 되는 것이다.
다음 예제와 같이 공통된 속성 createAt, id 등을 정의하여 베이스 테이블을 만들어 사용하면 속성은 공통되지만 독립적으로 각각 테이블에 적용되어 사용할 수 있다.
@MappedSuperclass
public abstract class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}
@Entity
@Table(name = "board")
public class Board extends BaseEntity {
@Column(nullable = false, length = 100)
private String name;
}
@Entity
@Table(name = "post")
public class Post extends BaseEntity {
@Column(nullable = false, length = 100)
private String title;
@Column(length = 1000)
private String contents;
}
'Spring' 카테고리의 다른 글
| [오르미 백엔드7기] (Spring Security , Cors,CSRF) (1) | 2024.11.29 |
|---|---|
| [오르미 백엔드7기](Spring 프록시객체, 지연로딩 ,즉시로딩, 영속성전이, N+1문제) (1) | 2024.11.25 |
| [오르미 백엔드7기] (Spring JPA 기본,ORM이란? , JDBC) (0) | 2024.11.22 |
| [오르미 백엔드 7기] (Spring Controller, thymeleaf 실습) (1) | 2024.11.19 |
| [오르미 백엔드 7기](POJO, SPRING @Controller, Thymeleaf + CS지식(CSR, SSR)) (1) | 2024.11.18 |