실무에서는 getter는 대부분 열어두고 setter는 꼭 필요한 경우에만 사용하기
사실 getter setter대신 필요한 별도의 메서드를 제공하는 것이 가장 이상적이지만
데이터를 조회할 일이 너무 많으므로 getter는 모두 열아두는것이 편함(getter는 아무리 호출해도 큰 문제는 발ㅇ생하지 않음)
하지만 setter는 엔티티의 값이 어디서 변했는지 추적하기 힘들어짐 → 변경지점이 명확하도록 변경을 위한 비즈니스 메서드를 별도로 제공해야함
@Entity @Getter @Setter
public class Member {
@Id
@Column(name = "member_id")
@GeneratedValue
private Long id;
...
}
예제에서는 엔티티의 식별자는 id 를 해놓고 pk 컬럼명은 member_id를 사용함
→ FK랑 이름도 맞추고, 컬럼명을 그냥 id로 해놓으면 나중에 찾기 힘들고, DB설계하는 분들이 관례적으로 이렇게 많이함
실무에서는 @ManyToMany 사용X
왜냐면 ManyToMany는 중간테이블에 다른 컬럼을 못넣음(두 테이블의 PK만 들어감)
→ 중간 엔티티를 만들고 @ManyToOne, @OneToMany로 매핑해서 사용하기
@Embeddable // Jpa 내장타입
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Address {
private String city;
private String street;
private String zipcode;
}
위와 같은 값 타입(Value Type)은 변경이 되선 안됨
→ Setter 는 막고 생성자에서 값을 모두 초기화하여 변경 불가능하게 만들기
모든 연관관계는 지연로딩으로 설정!(중요)
즉시 로딩(EAGER)이란 엔티티 하나를 로딩하는 시점에 연관된 다른 엔티티들을 한번에 로딩 하는 것이다.
예를 들면, member를 조회할 때 연관된 order와 다른 엔티티를 한번에 조회해버림
즉시 로딩은 예측이 어렵고, 어떤 SQL이 실행될지 모르고 JPQL 실행 시 N+1문제 자주 발생
@Entity
@Table(name = "orders")
@Getter @Setter
public class Order {
@Id
@GeneratedValue
@Column(name = "order_id")
private Long id;
@ManyToOne(fetch = FatchType.EAGER)
@JoinColumn(name = "member_id")
private Member member;
...
}
order를 조회할떄 member를 join해서 한방에 같이 가져옴
But JPQL 사용시 select o from order o; → SQL로 그대로 번역이됨
→ order가 100개가 있고 Member가 EAGER로 되어있으면 쿼리가 100번 날라감
→ order에 연관된 Member를 가져오기 위해 쿼리가 100번 날라감
→ N+1 문제 발생
→ 실무에서 모든 연관관계는 지연로딩(LAZY)으로 설정
연관된 엔티티를 함께 DB에서 조회해야 하면 fetch join, 또는 엔티티 그래프 기능을 사용한다.
컬렉션은 필드에서 초기화하기
Member member = new Member
System.out.println(member.getOrders().getClass());
em.persist(team);
System.out.println(member.getOrders().getClass());
// out put
class java.util.ArrayList
class org.hibernate.collection.internal.PersistentBag
위와 같이 영속화 시키면 Hibernate의 내장 컬렉션으로 변경됨 변경된 상태에서 getTeam() 같은 메서드를 호출하며 new ArrayList<>() 이런식으로 생성해 버리면 문제가 발생할 수 있음 그러므로 컬렉션은 필드에서 바로 초기화하자!14분 까지 완료
Index of /hibernate/orm/5.4/userguide/html_single
엔티티의 필드명을 그대로 테이블 명으로 사용
현재 스프링 부트 신규 설정(엔티티(필드)→테이블(컬럼))
카멜 케이스 → 언더스코어(userId → user_id)
대문자 → 소문자
. → _
적용 2단계
논리명 적용: 테이블이나 컬럼명을 명시하지 않을 때 논리명 적용
물리명적용 : 모든 논리명에 적용, 실제 테이블에 적용
cascade 옵션
...
public class Order {
@Id
@GeneratedValue
@Column(name = "order_id")
private Long id;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
...
}
위의 코드에서는 orderItems를 생성할때
부모 엔티티인 Order가 영속 상태로 만들 때 연관된 엔티티도(여기선 orderItems의 OrderItem 엔티티들) 함께 영속 상태로 전이시킴
즉 cascade = CascadeType.ALL 를 적용하면 특정 엔티티에 대해 특정한 작업을 수행하면 관련된 엔티티에도 동일한 작업을 수행한다는 의미
연관관계 메소드
ex) Member가 주문을하면 orders에 order 추가(연관관계의 주인이 아니더라도 값은 참조하므로 set을 해줘야함)