ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TIL 자바 스프링부트 서버개발 올인원 : 33-34강
    Java 2024. 5. 10. 00:01

    @ManyToOne / @OneToMany

    bookService 대출 기능

    이제까지의 대출기능은 절차지향적으로 진행이 되고 있었다.

    1. Request로 들어오는 bookName을 통해 Book이 있는지 확인

    2. 있으면, UserLoanHistoryRepository에서 빌릴 수 있는지 확인

    3. 빌릴 수 있다면, User가 있는지 확인

    4. 유저가 있다면, UserLoanHistory객체로 만들어서 저장

     

    이 경우 User와 UserLoanHistory는 밀접한 연관이 있기 때문에 두 객체를 협업하게 할 수 있다.

    요런식으로 생략할 수 있게 된다.

     

    단 선행 조건으로 User와 UserLoanHistory가 서로 알아야한다.

    @Entity
    public class UserLoanHistory {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id = null;
    	
    //  @Column(nullable = false, name = "user_id")
    //  private long userId;
        @ManyToOne
        private User user;
    
        @Column(nullable = false, name = "book_name")
        private String bookName;
    
        @Column(nullable = false, name = "is_return")
        private boolean isReturn;
    
        protected UserLoanHistory() {
        }

    위와 같이 UserId에 해당하는 부분은 @ManyToOne을 사용해서 User객체로 대체해주면 된다.

    UserLoanHistory의 경우 User객체는 하나고 UserLoanHistory가 여러개 생길 수 있기 때문에 ManyToOne이 된다.

    즉, N : 1 관계가 되는 것이다.

     

    UserLoanHistory객체를 위와 같이 바꿨다면, User객체도 수정한다.

    @OneToMany(mappedBy = "user")
    List<UserLoanHistory> userLoanHistories = new ArrayList<>();

    List로 UserLoanHistory를 받아주고(유저객체는 여러개의 UserLoanHistory를 가질 수 있으니까)

    반대로 @OneToMany를 달아준다.

     

    그리고 연관관계의 주인이 아닌쪽에 mappedBy옵션을 달아줘야 하기 때문에 User Entity에 달아준다.


    연관관계의 주인

    table을 보았을 때 누가 관계의 주도권을 가지고 있는가

    출처 : 인프런 - 자바 스프링부트 서버개발 올인원

    UserLoanHistory테이블이 User테이블의 id인 User_id를 가지고 있기 때문에 주인이 된다고 할 수 있다

    의존한다고 생각하면 반대로 생각하기 쉽지만 가지고 있다고 생각하면 주인이 되는게 맞다.

     

    mappedBy 옵션을 달아서 연관관계의 주인의 값을 설정해야만 진정한 데이터가 저장된다.

    (주인이 아니기 때문에 낙인?찍는것)

     

    연관관계의 주인의 효과는 객체가 연결되는 기준이 된다.

    Person 객체는 Address_id를 가지고 있기 때문에 주인인 상황이고

    Address 객체는 MappedBy로 Person객체에 묶여있는 상황이다(주인이 아니기 때문에).


    해당 코드를 보면, person테이블과 address테이블에 각각 객체를 저장해서 DB에 넣어주고 있다.

    그리고 person객체에서 setAddress를 통해 address_id 변수를  바꿔주게 되면 DB에도 똑같이 반영이 된다.

    하지만 address객체에서 setPerson을 통해 person_id변수를 바꾼다면 DB에 반영이 되지 않는다.

     

    그렇기 때문에 연관관계의 주인은 객체가 연결되는 기준이 되고, 진정한 데이터가 저장된다는 것이다.

     


    연관관계의 사용시 주의점

    주인인 person객체가 setAddress하면 테이블끼리 연결이 되었기 때문에 주인이 아닌 Address객체의 Person도 변경 되었다고 생각할 수 있다. 하지만 트랜잭션이 끝나지 않았을 때, 한쪽만 연결해 둔다면 반대쪽은 null값이 나온다.

    이를 해결하기 위해서 setter로 Person의 Address와 Address의 Person을 한번에 이어줄 수있다. 

    Person객체의 setAddress 메서드

    Person객체의 setAddress 메서드로 address를 설정해주면서, address객체의 person까지 설정해준다.


    @ManyToOne은 단방향으로만 사용할 수도 있다.

    주인이 아닌 쪽이 같는 @OneToMany를 생략할 수 있다.

     

    위에서 User객체를 수정할 때 아래와 같은 코드로 List를 만들어줬었는데

    @OneToMany(mappedBy = "user")
    List<UserLoanHistory> userLoanHistories = new ArrayList<>();

    사실 @OneToMany와 List객체는 생략할 수 있는 부분이다. 


    @JoinColumn

    연관관계의 주인이 활용할 수 있는 어노테이션이다.

    필드의 이름이나 null여부, 유일성 여부, 업데이트 여부 등을 지정한다.


    N : M 관계 - @ManyToMany

    학생이 여러 동아리를 가질 수 있고, 동아리도 여러 학생을 받는 경우가 다대다 관계이다.

    그러나 구조가 복잡하고, 테이블이 직관적으로 매핑되지 않아서 사용하지 않는 것을 추천한다.

    --> @ManyToMany는 @ManyToOne로 바꿔줄 수 있기 때문이다.


    Cascade 옵션

    한 객체가 저장되거나 삭제될 때, 그 변경이 폭포처럼 흘러 연결되어 있는 객체도 함꼐 저장되거나 삭제되는 기능

     

    예를 들어 1번 유저가 A책을 빌렸는데, 1번 유저가 탈퇴하면??

    유저 테이블에 1번 유저는 없지만, 대출내역에는 1번 유저가 A책을 빌린것으로 나오게 된다. 이를 해결하기 위해서 생긴 것이 1번 유저가 삭제되면, 1번 유저와 관련된 데이터들을 수정하거나 삭제할 수 있도록 할 수 있는 Cascade옵션이다.

     

    이런식으로 옵션을 넣어주면 된다.


    OrphanRemoval 옵션

    객체간의 관계가 끊어진 데이터를 자동으로 제거하는 옵션이다.

    주인 객체가 아닌 User객체에서 @OneToMany/ List<UserLoanHistory> userLoanHistories 중 책1이 삭제된 경우는 DB는 어떻게 될까?

    --> 아무런 변화가 없다.

    --> 그러나 OrphanRemoval 옵션을 true로 설정한다면 DB를 변경할 수 있다.

     

    즉, UserLoanHistoryRepository를 직접 수행하지 않고 연결이 끊어진것만으로도 DB에서 데이터를 지우고 싶은 경우에 사용한다.

     

    강의 : https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%84%9C%EB%B2%84%EA%B0%9C%EB%B0%9C-%EC%98%AC%EC%9D%B8%EC%9B%90/dashboard

Designed by Tistory.