연관관계의 주인만 (외래키를 가지는) 대상 엔티티를 바라 볼 수 있음.
객체에서 양방향 관계를 맺고 싶으면 두 엔티티 클래스에 서로의 객체 필드를 삽입해야한다.
- 객체 : 필드 참조를 통해 연관관계 맺음(클래스 Id)
- 테이블 : 외래키를 통해 연관관계를 맺음
객체 참조를 통해 객체 그래프 형태를 가질 수 있다..!
다대일(N:1) 관계 매핑 어노테이션
- optional : false | true , 엔티티 존재 여부 확인
- fetch : 페치 전략 설정 (Lazy/ Eager 로딩)
- cascade : 영속성 설정
- targetEntity : 연관된 엔티티의 타입정보 설정 -> Generic으로 대체 가능
// 둘 다 동등 @OneToMany private List<EntityClass> entities; @OneToMany(targetEntity=EntityClass.class) private List entities;
외래키 매핑 어노테이션 : 참조하는 객체 필드 위에 명시
- name : 매핑할 외래키 이름
- referencedColumnName : 외래키가 참조하는 대상 테이블 칼럼명 (default: 참조하는 테이블 기본키 칼럼 명)
- foreignKey : 외래키 제약 조건 직접 지정 (DDL 조건)
- unique, nullable, insertable, updatable, columnDefinition, table
생략 시 : 필드명_참조 테이블의 칼럼명
@ManyToOne @JoinColumn(name = "참조 객체 필드의 기본키 칼럼 명")
JPA 에서 엔티티를 연관관계를 통해 저장할 시 모든 연관된 엔티티는 영속상태를 유지하여야함.
- JPA가 참조한 객체 필드의 식별자를 외래키로 사용하여 등록 쿼리를 생성함.
String jpql = "select m from Entity m join m.ForeginEntity t where t.id=
:ForeignKey"
EntityManager.createQuery(jpql, Entity.calss).setParameter(
"ForeignKey"
, "값");
영속성 컨텍스트내에 존재하는 엔티티들은 연관관계 설정 시 변경감지를 엔티티 매니저에서 자동으로 해줌.
- 연관관계를 맺는 참조 객체 필드를 null로 변경한 후, 영속성 컨텍스트에서 참조한 외래 엔티티를 제거해줘야함.
- 외래키 제약 조건을 지켜야하기 때문
외래키를 가지는 엔티티 뿐만이 아니라 외래키의 대상이 되는 엔티티도 반대편을 바라볼 수 있는 연관관계
(두 단방향이 서로 바라보는)
테이블은 외래키 하나를 통해 서로 양방향으로 바라 볼 수 있지만, 객체는 그렇지 않다.
@Entity
public class EntityClass{
@Id
private Long id;
@ManyToOne
@JoinColumn(name="foriegn_id") //참조 외래키
private Foreign foreign; // 참조 필드명
}
// JoinColumn 미설정시 : foreign(필드명)_foreign_id(조인 엔티티 대상 Id칼럼명) 형태로 외래키 칼럼명 이용
@Entity
public class Foreign{
@Id
@ColumnName(name="foreign_id") // 참조 외래키
private Long id;
@OneToMany(mappedBy="foreign") // 참조 필드명
private List<EntityClass> entities = new ArrayList<entityClass>();
}
- mappedBy : 양방향 매핑에서 참조 당하는 엔티티 클래스에 명시해 줌(조인을 당하는)
-> 외래키가 하나이지만, 객체 참조가 둘 인 상황을 해결하기 위함.
-> 외래키를 관리하는 주체를 결정하기위해 해당 옵션을 사용.
주체가 아닌 칼럼 @OneToMany에 보통 옵션을 붙여서 주로 사용된다.
- JPA 에서 연관관계를 맺을 떄 외래키를 관리하는 주체는 테이블을 따라가면 된다고 생각하게 된다.
- 외래키를 가지는 테이블은 참조를 하는 테이블이 되니 해당 엔티티는 다(N)이 되고, 참조를 당하는 테이블은 일(1)의 연관관계를 가지면 된다.
@ManyToOne
- 다대일 (FK를 가지는 엔티티)
@OneToMany
- 일대다 (mapped by = "상대 엔티티 클래스 필드") -> 본인의 ID가 다른 엔티티에서 FK로 사용될 때
다 쪽을 맡은 엔티티에 연관관계를 설정해야 영속성 컨텍스트에 반영됨.
연관관계 설정 전 FK를 가지는 대상 엔티티는 영속성 컨텍스트에 반드시 포함되어 있어야한다.
참조를 당하는 엔티티에 아무리 값을 변경하여도 영속성 컨텍스트와 DB에 반영되지 않음.
// 1) 잘못된 경우
EntityClass ec1 = new EntityClass(1);
EntityManager.persist(ec1);
Foreign f1 = new Foreign(1);
f1.getEntities().add(ec1);
EntityManger.persist(f1);
// 영속성 컨텍스트에 아무런 영향도 주지 않음.
// 외래키를 관리하는 주체가 아니기 때문
// 2) 올바른 경우
Foreign f1 = new Foreign(1);
EntityMaanger.persist(f1); // 조인될 (참조될) 엔티티를 먼저 영속성 컨텍스트에 유지
EntityClass ec1 = new EntityClass(1); // 해당 엔티티를 참조하는 다(N) 엔티티를 생성 후
ec1.setForeign(f1); // 연관관계 설정
EntityManager.persist(ec1);
영속성 컨텍스트 뿐만 아니라, 객체 관점에서도 서로 간의 양방향 연관관계를 이어줄 필요가 있다.
- 영속성 컨텍스트에 연관관계를 매핑하여도, 트랜젝션 커밋을 통해 DB에 반영하기 전까지,
- 현재 일(참조되는)인 엔티티는 다(N)인 엔티티를 바라보지 못한다.
- 그래서 일(1)인 엔티티에서 다(N)를 조회해도 결과가 나타나지 않기 때문에, 객체 관점에서는 영속성 컨텍스트에 등록함과 동시에 객체 컬렉션 참조도 같이 진행해주는 것이 올바르다.
Setter 를 통해서 객체 관점에서 연관관계를 만들어주는 메서드 구현, 이때 무한루프가 발생하지 않도록 주의해야함.
나캐싱 기능이 있어도, DB 액세스 접근을 늘어나겠지만 트랜잭션을 미리 한 다음
일대다인 엔티티에서 다(N)를 조회하여 DB에 저장된 연관관계를 통해서 데이터에 접근하는게 더 나을것 같다고 생각이 든다..공부를 하면서 좀 더 구현을 하며 바라봐야겟다.