이번에는 설정을 했던 이전 개시물에 이어 일대다, 다대일 양방향 매핑을 해보고 직접 DB에 넣어보겠습니다.


고객이 포트폴리오를 만들 수 있고, 또한 관심종목을 여러개 입력할 수 있다고 해봅시다. 그렇다면 1:N 관계가 되겠죠.


일단 DB 테이블입니다.


tbl_port는 포트폴리오 테이블, tbl_interest는 관심종목 테이블입니다.


그냥 예시로 보여드리는 것이니 테이블 이름과 기본키, 외래키 정도만 확인하도록 합니다.


tbl_port의 기본키를 tbl_interest가 외래키로 가지고 있는 구조입니다.




다음에는 VO를 준비해봅시다. 코드의 주석을 참조하시면 되겠습니다.






여기서 핵심은 @OneToMany와 @ManyToOne 입니다.


만약 포트폴리오를 조회했다면, 포함된 관심종목도 따라서 불러와지도록 해야 할 것입니다. 그렇지만 관심종목들의 내용을 확인할 필요 없이 일단 포트폴리오 내용만 본다고 한다면, 관심종목은 당장 불러올 필요는 없겠죠. 


위에 @ManyToOne의 의 fetch=FetchType.LAZY 설정이 이에 해당합니다. LAZY, EAGER 등을 설정할 수 있는데 자세한 내용은 검색해봅니다. 일단 잘 모르겠으면 LAZY로 해두고 한꺼번에 불러올 부분만 

EAGER로 해 두시면 되겠습니다.



또한 @ManyToOne를 PortVo에 걸어둔 점을 유의하시기 바랍니다. PortVo의 @Id로 지정된 변수를 외래키로 쓰겠다는 의미인 것이죠. 지금 하는 실습은 1:N, N:1 양방향 입니다.


PortVo에는 InterestVo가 배열로 지정되어있습니다. 또한 여기에 @OneToMany(mappedBy="portVo", cascade={CascadeType.ALL})가 걸려있죠.

mappedBy="portVo"는 외래키가 InterestVo, 그러니까 tbl_interest에 걸려있다는 뜻이라고 이해하시면 되겠습니다. 여기서 portVo는 InterestVo의 portVo를 뜻합니다.


cascade={CascadeType.ALL}은 모든 경우에 영속성 전이를 설정하겠다는 것을 뜻합니다. 예를 들자면, 만약 포트폴리오를 입력할 때 관심종목도 한꺼번에 입력하고 싶다고 합니다.


그렇다면 PortVo의 List<InterestVo> interestVos에 각각의 관심종목 Vo를 넣어두고, repository.save(portVo) 와 같은 방식으로 한꺼번에 넣는게 편할 것입니다.


CascadeType.ALL은 이런 식으로 부모 객체를 입력하면 안에 들어 있는 자식 객체되 자동 적용되는 걸로 해둔 것입니다. 잘 모르겠으면 일단 CascadeType.ALL로 해두고 레퍼런스를 참조합니다. 단, CascadeType.ALL로 걸어두고 삭제를 하면 자식 객체도 DB에서 지워진다는 점을 유의합시다.


이번에는 입력을 담당하는 Repository Interface 입니다. 마이바티스에 익숙하시다면 Mapper Interface에 해당한다고 보시면 되겠습니다.




이렇게만 해 놓으면 왠만한 것은 다 할 수 있습니다.


이번에는 Junit 테스트 코드입니다. 일부 코드가 삭제된 부분이 있으니 주석 처리된 부분이 중요하므로 이 부분만 집중적으로 봅시다.




포트폴리오 객체를 하나 생성하고 안에 관심종목 객체 두 개를 넣어 놓습니다.


여기서는 interestVo.setPortVo(portVo); 이 부분과 portVo.getInterestVos().add(interestVo); 이 부분을 눈여겨 봅시다. 이렇게 양방향으로 넣어놓아야 DB에 올바르게 입력이 됩니다.

또한 entityManager.clear();와 같이 엔티티매니저를 초기화하는 부분이 있습니다. 이것을 하는 이유는 저 코드에 도달할 때 까지 자료가 DB에 저장된게 아니라 엔티티매니저에만 저장돼있기 때문입니다. 

하이버네이트는 설정에 따라 바꿀 수 있지만, 일반적으로 최종 커밋이 될 때 실제 DB에 저장이 됩니다. 그 전에는 자료가 엔티티매니저에 들어가있다가, 호출되면 다시 불러와지는 식이죠.

entityManager.clear();는 아직 커밋이 안된 영속성 객체를 커밋시키고 엔티티매니저를 비우는 행위를 합니다. 자료를 새로 불러와서 제대로 DB에 저장됐나 확인하기 위함이죠.

또한 유의할 점이, 디버깅을 하겠다고 println(portVo)와 같이 객체 전체를 찍으면 안됩니다. portVo -> InterestVo -> portVo -> InterestVo -> portVo -> InterestVo -> .... 이런식으로 Recursion이 발생해 StackOverFlowError가 발생하기 때문입니다.

이 아티클은 이것으로 마치고, 다음에는 Ajax로 객체를 보내서 Hibernate로 DB에 저장하는 것을 해보겠습니다.




Posted by 타다키치
,