바로 앞에서 다대일 관계에서 단반향으로써 학생이 반을 접근하는 방식으로 진행했으나 이번에는 반에서 학생들을 접근하는 방식을 사용해보자.
그렇게 되면 학생 -> 반에서 반 -> 학생이 추가되어 결국 반 <-> 학생 이런 양방향 연관관계가 형성된다.
하나의 반에는 여러 학생이 포함되어 있다. 그렇기 때문에 반 클래스에 List<Student> 객체를 추가한다.
1 2 |
@OneToMany(mappedBy = "classes") private List<Student> students; |
@OneToMany(mappedBy = "classes")
- 일대다 매핑을 정보를 추가하고 학생쪽에서 사용되는 반 필드명을 mappedBy에 값으로 추가해준다.
조회
반에 포함되어 있는 학생들을 조회한다.
1 2 3 4 5 6 7 8 9 10 |
@Override @Transactional public void selectClasses() { Classes classes1_1 = entityManager.find(Classes.class, "1-1"); List<Student> students = classes1_1.getStudents();
for (Student student : students) { print(student); } } |
연관관계 주인 지정
테이블은 외래키 하나로 두 테이블의 연관관계를 관리 할 수 있다. 예를 들면 반 이름이 외래키라고 했을 때 학생 테이블에서 외래키 반 이름을 추가할 수도 있고, 반 테이블에서 반 이름을 관리할 수 있다. 하지만 엔티티에서는 외래키를 관리(추가, 수정, 삭제)할 수 있는 것은 두 개의 엔티티의 연관관계의 주인이 되는 엔티티만이 가능하다. 나머지 다른 엔티티는 조회만 가능하다.
예를 들어 저번 시간에 공부 했었던 Student 엔티티 클래스는 Classes 외래키의 주인으로써 외래키를 추가, 수정, 삭제 할 수 있다. Classes 엔티티 클래스는 외래키의 주인이 아니므로 조회만 가능하다.
@ManyToOne 설정이 있는 곳이 무조건 주인이다. 그리고 양방향 설정된 엔티티에서 조회가 가능하도록 하기 위해서 다른 엔티티에 @OneToMany(mappedby ="classes")를 지정해 주면 주인 설정이 끝난다.
그럼 진짜 주인이 아닌 엔티티 Classes에서는 외래키 관리가 안되는지 확인해보자.
1 2 3 4 5 6 7 |
@Override @Transactional public void saveClasses() { Student wedul = entityManager.find(Student.class, "1-1-01"); Classes classes2_1 = new Classes("2-2", "2학년2반", Arrays.asList(wedul)); entityManager.persist(classes2_1); } |
-> 처음 생각대로라면 id : "2-2", name : "2학년2반"인 반이 classes테이블에 추가되고 학생 테이블에 wedul 학생의 반이 2-2로 될 것 같지만 그렇지 않다. 왜냐하면 Classes 엔티티는 주인이 아니기 때문이다. 그래서 반 테이블에 값만 추가된다.
ㅁ주의사항
만약 단방향 그러니까 Student 엔티티에만 @ManyToOne을 해줄 경우 Classes 엔티티를 통해 반에 등록된 학생을 조회 하려 할 때 빈 값을 받게 된다. 왜냐하면 연관관계가 맺어지지 않았기 때문이다. 그래서 무조건 이럴 경우 양방향 연관관계를 맺어 주는것이 좋다.(@OneToMany)
아래 코드를 보면 wedul학생에 classes2_1 반을 추가해줬지만 classes2_1에서 학생을 조회하면 wedul 학생이 없다.
1 2 3 4 5 6 |
Student wedul = entityManager.find(Student.class, "1-1-01"); Classes classes2_1 = new Classes("2-2", "2학년2반", Collections.emptyList()); wedul.setClasses(classes2_1);
// 반영 되어 있지 않아서 wedul이 출력되지 않음. classes2_1.getStudents(); |
그래서 이런 문제로 버그가 발생할 수 있기 때문에 좋은 방법으로 setClasses() 메소드를 다음과 같이 변경해주면 좋다.
1 2 3 4 |
public void setClasses(Classes classes) { this.classes = classes; classes.getStudents().add(this); } |
그렇지만 이렇게만 해주고 나면 또다른 버그가 발생할 수 있다. 다음과 같은 상황을 가정해보자.
1 2 3 4 5 6 7 8 9 |
Student wedul = entityManager.find(Student.class, "1-1-01"); Classes classes2_1 = new Classes("2-2", "2학년2반", Collections.emptyList()); wedul.setClasses(classes2_1);
classes2_1.getStudents();
// 학생의 반을 다른 반으로 변경할 경우 기존의 반에 들어있는 getStudents List안에서 학생을 지워줘야한다. Classes classes3_1 = new Classes("3-1", "3학년1반", Collections.emptyList()); wedul.setClasses(classes3_1); |
이 상황에서는 위에 변경해주었던 방식대로 진행하면 기존에 반이었던 classes2_1에도 wedul이 있고 classes3_1에도 wedul이 있는 문제가 발생한다. 그래서 다음과 같이 바꿔주면 해결된다.
1 2 3 4 5 6 7 8 |
public void setClasses(Classes classes) { // 먼저 지워준다. classes.getStudents().remove(this);
// 그리고 반을 바꾸고 학생추가 this.classes = classes; classes.getStudents().add(this); } |
단방향 매핑만으로도 테이블과 객체의 연관관계 매핑이 되었지만 양방향 매핑을 통해서 더욱 편리하게 객체의 탐색이 가능하게 할 수 있다. 하지만 위에 보았듯이 양방향 매핑에서는 주의해서 관리 해줘야 할 포인트가 많다.
'web > JPA' 카테고리의 다른 글
JPA 상속관계 매핑 전략 (0) | 2018.10.31 |
---|---|
JPA 관계 유형별 엔티티 설정 방법 (0) | 2018.10.31 |
연관관계 매핑 - 다대일 매핑 (단반향) (0) | 2018.10.23 |
JPA 매핑 어노테이션 - DDL 2 (0) | 2018.10.13 |
JPA 매핑 어노테이션 - DDL (0) | 2018.10.12 |