Bi-Directional

κΈ°μ‘΄ Uni-Directional μ—μ„œλŠ” Instructor λ₯Ό load ν•΄μ•Όλ§Œ 그에 λŒ€ν•œ InstructorDetail 을 κ°€μ Έμ˜¬ 수 μžˆμ—ˆλ‹€.

κ·ΈλŸ¬λ‚˜ Bi-Directional relationship 을 μ‚¬μš©ν•˜λ©΄ InstructorDetail 을 load ν•¨μœΌλ‘œμ¨ 이와 associated 된 Instructor λ₯Ό get ν•  수 μžˆλ‹€.

Uni-Directional μ—μ„œ Bi-Directional 둜 λ°”κΎΈλŠ” 것은 λ‹¨μˆœνžˆ Java code 만 μˆ˜μ •ν•˜λ©΄ λœλ‹€. 기쑴의 DB schema λ₯Ό μˆ˜μ •ν•  ν•„μš”λ„ μ—†λ‹€.

Bi-Directional One-to-One Mapping 의 Development Process λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

  1. Make updates to InstructorDetail class
  2. Add new feild to reference Instructor with @OneToOne annotation
  3. Add getter/setter methods for Instructor
  4. Create Main App
1.1. Add new feild to reference Instructor with @OneToOne annotation
@OneToOne(mappedBy = "instructorDetail", cascade = CascadeType.ALL)  
private Instructor instructor;

@OneToOne annotation 을 ν†΅ν•˜μ—¬ μœ„μ™€ 같이 field λ₯Ό μ„€μ •ν•˜λ©΄ λœλ‹€. λ˜ν•œ mapping 된 λ‹€λ₯Έ entity class 의 μ–΄λ–€ field 와 연관이 λ˜μ–΄μžˆλŠ”μ§€λ₯Ό mappedBy λ₯Ό ν†΅ν•˜μ—¬ μ„€μ •ν•  수 μžˆλ‹€. μ—¬κΈ°μ„œλŠ” Instructor class 의 instructorDetail field 와 μ—°κ΄€λœ 것이닀.

1.2. Add getter/setter methods for Instructor

이후에 instructor 의 getter 와 setter λ₯Ό μΆ”κ°€ν•œλ‹€. μ΄λ•Œ, toString() method μ—λŠ” μΆ”κ°€λœ instructor field 에 λŒ€ν•œ λ‚΄μš©μ„ μΆ”κ°€ν•˜λ©΄ μ•ˆ λœλ‹€.

λ§Œμ•½ λ””λ²„κΉ…μ΄λ‚˜ 기타 좜λ ₯을 μœ„ν•˜μ—¬ entity class object 자체λ₯Ό 좜λ ₯ν•˜λŠ” μƒν™©μ—μ„œ, owning side(FK λ₯Ό λ³΄μœ ν•œ μΈ‘, 주인) 와 inverse side(비주인) λͺ¨λ‘ μ„œλ‘œμ˜ object κ°€ λͺ¨λ‘ 각각의 toString() method 에 ν¬ν•¨λ˜μ–΄ μžˆλ‹€λ©΄ Infinite Recursion(λ¬΄ν•œ μž¬κ·€ 호좜) λ˜λŠ” Circular Reference(μˆœν™˜ μ°Έμ‘°) λ¬Έμ œκ°€ λ°œμƒν•˜κ²Œ λœλ‹€.

λ”°λΌμ„œ μ‹€μ œ FK λ₯Ό λ³΄μœ ν•œ owning side μ—μ„œλ§Œ toString() 에 μΆ”κ°€ν•˜λŠ” 것이 λ°”λžŒμ§ν•œ 방법인 것 κ°™λ‹€.

2. Create Main App
private void findInstructorDetail(AppDAO appDAO) {  
    int theId = 2;  
    InstructorDetail tempInstructorDetail = appDAO.findInstructorDetailById(theId);  
  
    System.out.println("tempInstructorDetail: " + tempInstructorDetail);  
    System.out.println("the associated instructor: " + tempInstructorDetail.getInstructor());  
  
    System.out.println("Done! :)");  
}

μ΄λ²ˆμ—λŠ” Bi-Directional One-To-One mapping 을 test ν•˜κΈ° μœ„ν•˜μ—¬ μœ„μ™€ 같이 InstructorDetail 을 ν†΅ν•˜μ—¬ Instructor data λ₯Ό μ½μ–΄μ˜€λ„λ‘ code λ₯Ό μž‘μ„±ν•œλ‹€.

결과적으둜 μœ„μ™€ 같이 μ„±κ³΅μ μœΌλ‘œ Instructor object λ₯Ό μ„±κ³΅μ μœΌλ‘œ κ°€μ Έμ˜¨ 것을 λ³Ό 수 μžˆλ‹€.

More on mappedBy

mappedBy 의 λ™μž‘ 방식에 λŒ€ν•˜μ—¬ μžμ„Έν•˜κ²Œ μ•Œμ•„λ³΄μž.

mappedBy λ₯Ό μ‚¬μš©ν•˜κ²Œ 되면, Hibernate λŠ” λ‹€μŒκ³Ό 같이 λ™μž‘ν•œλ‹€.

  1. InstructorDetail class κ°€ μ—°κ΄€κ΄€κ³„μ˜ 주인이 μ•„λ‹ˆλΌλŠ” κ±Έ 인식
  2. mappedBy = "instructorDetail" 라고 ν–ˆμœΌλ‹ˆκΉŒ, Instructor class μ•ˆμ—μ„œ instructorDetail λΌλŠ” field λ₯Ό 검색
  3. ν•΄λ‹Ή field μ—λŠ” @JoinColumn(name = "instructor_detail_id") κ°€ λΆ™μ–΄μžˆκΈ° λ•Œλ¬Έμ—, HibernateλŠ” β€œμ•„, μ‹€μ œ FK λŠ” instructor ν…Œμ΄λΈ”μ— μžˆκ΅¬λ‚˜β€λΌκ³  νŒλ‹¨
  4. λ”°λΌμ„œ HibernateλŠ” InstructorDetail ν…Œμ΄λΈ”μ—λŠ” 아무 μ™Έλž˜ 킀도 λ§Œλ“€μ§€ μ•ŠμŒ, λ‹¨μˆœνžˆ mapping 만 μ°Έμ‘°

결과적으둜 mappedBy λŠ” Hibernate μ—κ²Œ β€œFK λŠ” 이μͺ½λ§κ³  μ €μͺ½μ— μžˆμ–΄, λ‚œ κ·Έλƒ₯ 참고만 ν•˜λŠ”κ±°μ•Όβ€λΌκ³  μ•Œλ €μ£ΌλŠ” 역할이고, Hibernate λŠ” 주인 μͺ½ enitity μ—μ„œ FK 정보(@JoinColumn)λ₯Ό μ°Ύμ•„ μ‚¬μš©ν•œλ‹€.

One-to-One Mapping Bi-Directional - Test (Delete)

μ΄λ²ˆμ—λŠ” Delete λ₯Ό ν†΅ν•˜μ—¬ Bi-Directional λ₯Ό test ν•΄λ³΄μž.

@Override  
@Transactional  
public void deleteInstructorDetailById(int theId) {  
    InstructorDetail tempInstructorDetail = entityManager.find(InstructorDetail.class, theId);  
    entityManager.remove(tempInstructorDetail);  
}

μœ„μ™€ 같이 AppDAOImpl 에 method λ₯Ό μΆ”κ°€ν•˜κ³ ,

private void deleteInstrcutorDetail(AppDAO appDAO) {  
    int theId = 2;  
    System.out.println("Deleting instructor detail id: " + theId);   
    appDAO.deleteInstructorDetailById(theId);  
    System.out.println("Done!");  
}

Main App μ—μ„œλ„ method λ₯Ό μΆ”κ°€ν•˜μ—¬ test ν•΄λ³΄μž.

μœ„μ—μ„œ InstructorDetail entity class 의 Instructor field 에도 Cascading option 을 ALL 둜 κ±Έμ–΄μ£Όμ—ˆμœΌλ‹ˆ, InstructorDetail 을 delete ν•˜λ©΄ κ·Έ μž‘μ—…μ΄ Instructor 에도 ν˜λŸ¬κ°€ λͺ¨λ‘ delete 이 될 것이닀. Main App 을 μ‹€ν–‰ν•˜μ—¬ κ²°κ³Όλ₯Ό ν™•μΈν•˜μž.

μ„±κ³΅μ μœΌλ‘œ μ‹€ν–‰λœ 것을 λ³Ό 수 μžˆλ‹€.

One-to-One Mapping Bi-Directional - Test (Only delete Detail)

그럼 One-to-One Mapping Bi-Direction 의 μƒν™©μ—μ„œ, 였직 InstructorDetail 만 μ‚­μ œν•˜κ³  싢을 λ•Œμ—λŠ” μ–΄λ–»κ²Œ ν•΄μ•Ό ν• κΉŒ?

μš°μ„  말둜 μ„€λͺ…해보면 λ‹€μŒκ³Ό κ°™λ‹€. λ¨Όμ € InstructorDetail 의 instructor field 에 μ„€μ •ν•œ Cascading option 을 μ‘°μ •ν•΄μ•Ό ν•œλ‹€. μ•„λž˜μ™€ 같이 ALL μ—μ„œ CascadeType.REMOVE 만 λΉΌκ³  μ „λΆ€ λ‹€ λ„£μ–΄μ£Όλ©΄ 될 것이닀.

@OneToOne(mappedBy = "instructorDetail", cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})  
private Instructor instructor;

κ·ΈλŸ¬λ‚˜ 아직 Instructor object 의 instructorDetail field κ°€ InstructorDetail 의 id field λ₯Ό μ°Έμ‘°ν•˜κ³  있기 λ•Œλ¬Έμ— InstructorDetail object λ₯Ό λ¨Όμ € delete ν•  경우,

μœ„μ™€ 같이 λ‹€λ₯Έ exception 을 λ°œμƒμ‹œν‚€μ§€λŠ” μ•ŠμœΌλ‚˜ Hibernate κ°€ SELECT query 만 μ‹€ν–‰ν•  뿐, DELETE query λŠ” λ‚΄λΆ€μ μœΌλ‘œ μ‹€ν–‰μ‘°μ°¨ ν•˜μ§€ μ•ŠλŠ”λ‹€.

μ΄μœ λŠ” JPA 의 Persistence Context 의 관리 방식과 μ—°κ΄€κ΄€κ³„μ˜ μ†Œμœ κΆŒ κ°œλ… λ•Œλ¬Έμ΄λ‹€.

아무리 Bi-Directional 이라고 ν•˜λ”λΌλ„ mappedBy 둜 κ΄€λ¦¬λ˜λŠ” μͺ½μ€ Non-Owning Side 이며, μ΄λŠ” λ‹¨μˆœνžˆ μ—°κ΄€κ΄€κ³„μ—μ„œ Read-Only View 일 뿐이며, λ³€κ²½ 사항이 DB 에 직접 λ°˜μ˜λ˜μ§€ μ•ŠλŠ”λ‹€.

λ”°λΌμ„œ InstructorDetail 만 delete ν•˜κΈ° μœ„ν•˜μ—¬λŠ” ν•΄λ‹Ή object λ₯Ό μ™„μ „ν•œ 독립적 object 둜 놔두어야 ν•œλ‹€. 즉, λ‹€λ₯Έ object λ‘œλΆ€ν„° μ°Έμ‘°λ₯Ό λ°›λŠ” μƒνƒœμ΄λ©΄ μ•ˆ λœλ‹€.

@Override  
@Transactional  
public void deleteInstructorDetailById(int theId) {
	...
    tempInstructorDetail.getInstructor().setInstructorDetail(null);  
    ...
}

κ·Έλ ‡λ‹€λ©΄ μœ„μ™€ 같이 InstructorDetail 에 associate 된 Instructor 의 μ°Έμ‘°λ₯Ό κ°•μ œλ‘œ null 둜 λ°”κΎΈμ–΄ Bi-Directional 관계λ₯Ό λŠμ–΄μ•Ό ν•œλ‹€. μ΄λ ‡κ²Œ ν•˜κ³  λ‹€μ‹œ test 해보면,

Hibernate κ°€ null 둜 λ°”κΎΈλŠ” query λ₯Ό μ‹€ν–‰ν•˜κ³ , μ•„λž˜μ— DELETE query 도 μ •μƒμ μœΌλ‘œ 보낸 것을 확인할 수 μžˆλ‹€.

μ‹€μ œ DB μ—μ„œλ„ Instructor 은 μœ μ§€λ˜μ–΄ μžˆλŠ” 것을 λ³Ό 수 μžˆλ‹€.