엔티티
@Entity가 붙은 클래스는 JPA가 관리하고, DB에서 테이블과 맵핑이 될 클래스
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public Member(String name) {
this.name = name;
}
}
JPA가 엔티티 생성 시에 기본생성자를 사용하기에 필수
지연로딩을 위한 프록시 객체 생성 시에 상속을 통해 만들어서 final Class, private 생성자는 사용 불가
영속성 컨텍스트
영속성 컨텍스트는 엔티티를 영구 저장하는 환경이라는 뜻이다. 엔티티를 관리하는 1차 캐시와 쓰기지연 SQL 저장소로 이루어져있고 DB와 애플리케이션 사이에서 위치해 여러가지 장점을 갖고 있다.
엔티티의 생명주기
비영속
엔티티 객체를 생성했지만, 영속성 컨텍스트와 관계 없는 상태
Member a = new Member();
영속
엔티티 매니저를 통해 엔티티를 영속성 컨텍스트에서 관리하는 상태
em.persist(a);
준영속
영속성 컨텍스트에 저장되었다 분리된 상태
1차 캐시, 쓰기지연 SQL 저장소, 변경감지, 지연로딩을 포함한 영속성 컨텍스트가 제공하는 모든 기능 동작 하지 않음
em.detach(a);
삭제
엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제
em.remove(a);
영속성 컨텍스트의 이점
1차 캐시
영속성 컨텍스트 내부의 캐시를 통해 영속 상태의 엔티티를 저장. 1차 캐시의 키는 엔티티의 식별자(@Id)이고 값은 해당 식별자의 인스턴스이다. 엔티티를 조회할 때 1차 캐시를 먼저 살펴보고 없으면 DB에서 조회 후 1차 캐시에 저장한다.
동일성 보장
영속성 컨텍스트는 엔티티의 식별자로 동일성을 보장한다.
트랜잭션을 지원하는 쓰기 지연
트랜잭션이 커밋되기 전까지 해당 트랜잭션 내의 쿼리문을 쓰기 지연 SQL 저장소에 저장해두다 커밋 될때 쿼리문이 발생한다.
변경 감지
JPA로 엔티티 수정 시에는트랜잭션만 걸면 조회 후 수정 후 저장을 안해도 변경을 감지해서 수정된다.
1. 트랜잭션을 커밋하면 엔티티 매니저 내부에서 먼저 플러쉬 호출
2. 수정된 엔티티와 영속성 컨텍스트의 수정 전 스냅샷을 비교해 변경된 엔티티를 찾는다.
3. 변경된 엔티티가 있으면 수정 쿼리를 쓰기지연 SQL 저장소에 저장한다.
4. 쓰기 지연 SQL 저장소의 쿼리 커밋
5. 데이터베이스 커밋
지연 로딩
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne(fetch=FecthType.LAZY)
@JoinColumn(name = "team_id")
@Setter
private Team team;
public Member(String name) {
this.name = name;
}
public void changeName(String name){
this.name = name;
}
}
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
public Team(String name) {
this.name = name;
}
public void addMember(Member member){
member.setTeam(this);
members.add(member);
}
}
위의 코드에서 멤버를 가져올 때 팀의 정보가 필요한 서비스도, 필요없는 서비스도 있을 것이다.
만약 멤버의 정보가 필요한 서비스 위주라면 멤버를 가져올 때 팀의 정보를 가져오지 않고, 팀의 정보가 필요할 때 가져오게 끔 멤버에 연관관계를 맺은 팀의 필드에 fetch를 LAZY로 바꿀 수 있는데 이를 지연 로딩이라고 한다.