[JPA] 단방향 @OneToOne , @JoinColumn

2021. 11. 11. 20:19개발/JPA

 

물리테이블 설계


 예시로 자산 포트폴리오 관리를 위한 서비스를 구현하고자 한다. 이상하긴 하지만 "사용자는 하나의 포트폴리오만 만들수 있다." 라는 요구사항이 있다고 했을때 아래와 같은 물리테이블을 생성할것이다.

테이블간 관계

 

 위같은 테이블간의 관계는 소스코드상에서 어떻게 코딩할까?

 

 

테이블의 컬럼과 같도록 Entity 필드 맞추기


첫번째 방법으로는 테이블과 똑같이 필드를 구성하는것이다. 

User Entity
Portfolio Entity

 

 위 방법은 Entity의 필드와 Table의 컬럼이 동일하기때문에 이해하기 쉽다는 장점이 있다. 하지만 User별 Portfolio 정보를 가져오는 경우처럼 테이블간의 연관관계를 통한 데이터 접근을 수행하려고 할때 다소 번거로운 코드를 작성해야하는 단점이 있다. ( User Entity 정보를 가져와서 Portfolio Entity를 다시 조회한다. 아래 코드의 Line 38,39 )

사용자의 포트폴리오 정보 조회

Join문으로 한번에 유저와 포트폴리오 정보를 가져오지 않고 별개의 Select문 2개를 수행한다.

 

 

테이블의 컬럼을 Entity로 매핑하기


 이번 방법은 좀 더 객체지향적 방식이다. 이전과 달리 User Entity에 String 타입의 portfolioId가 아닌 클래스의 Portfolio를 작성했다. 여기서부터 자바소스의 Entity와 관계형 DB간의 괴리가 발생한다.

Portfolio 타입의 필드 작성

 

 Table에선 portfolio_id라는 문자값이지만 JPA에선 Portfolio라는 클래스(데이터 묶음)로 표기한다. 그래서 Table과 Java 코드의 간극을 해소하기 위한 Portfolio 클래스 필드에 추가적인 코드 작성이 필요하다.

 

@JoinColumn

 아래 Line 16의 @JoinColumn을 해석하면 "portfolio 필드를 TABLE_PORTFOLIO와 Join을 통해 채울건데 Join key(on 절)는 TABLE_USER의 portfolio_id 컬럼으로 해줘."라는 뜻이다. 즉, TABLLE_USER와 TABLE_PORTFOLIO를 Join할때 on 절에 TABLE_USER 테이블의 portfolio_id를 사용하도록 해준다. 만약 @JoinColumn(name = "userName")이라고 작성하면 on TABLE_USER.username = TABLE_PORTFOLIO.portfolio_id와 같이 on 절이 작성된다

 

잘되는지 확인해보기 위해 main 코드를 새로 작성한다.

아래 코드의 Line 31과 Line 40을 보자.

Line 31에선 portfoli_id가 아닌 portfolio 객체를 전달한다.

Line 40에선 portfolio_id를 통해서 TABLE_PORTFOLIO의 데이터를 조회하는 불필요한 로직이 사라졌다.

 

하지만 위 코드를 실행하면 에러가 발생한다. 

 Could not determine type for: domain.Portfolio, at table: TABLE_USER, for columns: [org.hibernate.mapping.Column(portfolio)]

 에러 로그를 보면 portfolio라는 컬럼을 TABLE_USER에서 찾을수 없다고 한다. 이는 User Entity의 portfolio 필드에 명확한 컬럼 타입이 지정되어 있지 않아서 발생한 문제다. Entity의 모든 필드에는 기본적으로 @Column 붙는다고 보면되는데(작성하지 않으면 필드명이 컬럼명이 됨) @Column 어노테이션으론 클래스 타입을 해석할 수 없다. 따라서 해석을 위한 추가적인 코드를 작성한다.

 

@OneToOne

 앞서 작성한 물리 테이블 설계를 보면 TABLE_USER와 TABLE_PORTFOLIO는 1대1 관계다. 이뜻은 TABLE_USER의 외래키인 portfolio_id를 TABLE_PORTFOLIO에서 조회하면 단 1개의 row만 조회된다는 뜻이다. 그러므로 @OneToOne 어노테이션은 추가해준다. ( 복수개의 데이터가 조회되도록 설계했다면 @OneToMany를 쓰면된다. )

 

아래 코드의 Line 16,17을 해석하면...

"TABLE_USER 테이블의 portfolio_id(FK)를 사용해서 TABLE_PORTFOLIO 테이블과 Join을 수행하고 그 결과로 나온값은 1건이며 이 데이터를 User Entity의 Portfolio에 매핑한다."

@OneToOne 추가

 

다시 아래의 코드를 실행해본다. em.find를 한번만 작성해서 User에 매핑되어 있는 포트폴리오 정보를 조회할 수 있다.

Hibernate가 Join쿼리를 작성해서 결과를 가져온다.

쿼리문 및 결과 출력

 

 


참고


 

Flowchart Maker & Online Diagram Software

Flowchart Maker and Online Diagram Software diagrams.net (formerly draw.io) is free online diagram software. You can use it as a flowchart maker, network diagram software, to create UML online, as an ER diagram tool, to design database schema, to build BPM

app.diagrams.net

 

 

JPA inheritance Could not determine type for

I have a simple JPA mapping but I keep getting a Could not determine type for exception. Setters and getters are omitted. @Entity @Inheritance(strategy = InheritanceType.JOINED) public class

stackoverflow.com

 

'개발 > JPA' 카테고리의 다른 글

[JPA] @ManyToOne, @OneToMany 이해하기  (0) 2021.11.29
[JPA] mappedBy 이해하기  (0) 2021.11.16