01. 기본 엔티티 매핑 관련

기본 엔티티 매핑에 조금 더 디테일한 설정들

@Entity 
@Table (name="USER") 
public class Member { 
	
	@Id 
	@Column (name = "user_id") 
	private String id; 
	 
	private String username; 
	
	private Integer age; 

	@Enumerated (EnumType. STRING) 
	private RoleType userRole;

//	@Enumerated (EnumType. ORDINAL) 
//	private RoleType userRole;

	@Temporal (TemporalType. TIMESTAMP) 
	private Date createdDate;

	@Temporal (TemporalType. TIMESTAMP)  
	private Date modifiedDate;
 
}
어노테이션 추가 설명
@Entity - 기본 생성자는 필수, 매개값이 있는 생성자가 있다면 Java는 기본 생성자를 생성해주지 않기 때문에 기본으로 생성해준다.
- final 클래스, enum, interface, inner class엔 사용할 수 없다
- 저장할 필드라면 final을 사용하면 안된다.
@Table 엔티티와 매핑할 테이블 이름 
생략 가능한 경우 : 엔티티의 이름을 테이블 이름으로 사용한 경우
@Column 객체 필드를 테이블 컬럼에 매핑 (생략 가능)
@Enumerated Java Enum을 테이블에서 사용하는 방식

 


02. 연관 관계 관련 심화

 

  • 단방향 연관관계 @ManyToOne 

다대일(N:1)의 관계 매핑

예시)

@Entity
@Getter
@Setter
public class Member {
	@Id
	@Column(name = "member_id")
	private String id;
	private String username;
	
	@ManyToOne			// Many가 Member, One이 Team
	@JoinColumn(name="team_id")	// 연결할 Entity class의 id이름
	private Team team;              // 연결할 Entity class

	public void setTeam(Team team) {
		this.team = team;
	}
}

@Entity
@Getter
@Setter
public class Team {
	@Id
	@Column (name = "TEAM_ID")
	private String id;
    
	private String name;
}

 

  • 양방향 연관관계 

3가지 Entity를 가지고 관계 보여주는 예시) 

 

Member Entity @OneToMany - 멤버는 여러 주문을 할 수 있다.

더보기
@Getter
@Entity
@NoArgsConstructor
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false)
    private String memberName;

	// One이 Member, Many가 Orders 멤버는 여러 주문을 할 수 있기 때문
    // 
    @OneToMany(mappedBy = "member", fetch = FetchType.EAGER)
    private List<Orders> orders = new ArrayList<>();

    public Member(String memberName) {
        this.memberName = memberName;
    }
}

Food Entity @OneToMany - 음식은 여러 주문에 들어갈 수 있다.

더보기
@Getter
@Entity
@NoArgsConstructor
public class Food {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(nullable = false)
    private String foodName;
    @Column(nullable = false)
    private int price;

    @OneToMany(mappedBy = "food",fetch = FetchType.EAGER)
    private List<Orders> orders = new ArrayList<>();

    public Food(String foodName, int price) {
        this.foodName = foodName;
        this.price = price;
    }
}

Orders Entity @ManyToOne - 주문하나에 음식하나, 멤버하나를 가질 수 있어 각각 ManyToOne

더보기
@Getter
@Entity
@NoArgsConstructor
public class Orders {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "food_id")
    private Food food;

    @ManyToOne
    @JoinColumn(name = "member_id")
    private Member member;

    public Orders(Food food, Member member) {
        this.food = food;
        this.member = member;
    }
}

 

  • 방향의 차이 

단방향이든 양방향이든 두 예시에서 보면  Many쪽은 큰 차이가 없다. 데이터 베이스는 다를까? 데이터 베이스도 차이가 없다. 애초에 데이터베이스는 양방향이 기본이기 때문에 외래키는 양방향에서 조회가 가능하기 때문이다.

(ManyToMany는? 다대다 관계는 애초에 사용을 지양해야한다. 다대다 관계를 정규화를 통해 일대다 혹은 다대일로 중간 테이블을 두어 풀어 사용해야한다. 실무에서는 절대로 사용하면 않는다고 한다.)

// Member의 Team쪽
@ManyToOne
@JoinColumn(name="team_id")
private Team team;
// Order의 Member쪽
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;

 

그렇다면 Many의 반대쪽은 어떨까

@OneToMany(mappedBy = "member", fetch = FetchType.EAGER)
private List<Orders> orders = new ArrayList<>();

형식이 좀 다른데 One mappedBy 속성을 잘 이해해야 한다. mappedBy에는 반대쪽 매핑의 필드 이름을 값으로 주면 되는데 여기서 연관관계의 주인이라는 개념이 나온다.

 

  • 연관관계의 주인? 

원래 객체에서의 양방향관계는 엄밀히 말하자면 서로 다른 단방향 연관관계 2개다.

아래 표와 같이 서로 다른 단방향을 로직으로 잘 묶어 양방향인 것 처럼 보이게 한 것 뿐이다.

 

데이터베이스의 테이블의 외래키는 하나만 있으면 되는 차이가 있다.

때문에 객체에서는 참조가 양쪽에서 각각 하나씩 참조해 2개인데 DB의 테이블의 외래키는 두 테이블 사이에 1개 뿐이면 된다.

객체의 연관관계 테이블의 연관관계
Member → Team Member ↔ Team
Team → Member

이런 차이로 JPA는 테이블과의 연관관계 처럼 두 테이블 중 하나에 외래키 하나를 두는 것 처럼 두 객체 연관관계 중 하나를 정해서 테이블의 외래키를 관리해야하는 데 이를 연관관계의 주인(Owner)라고 한다.

 

연관관계는 둘 사이에 한쪽을 연관관계의 주인으로 정해야한다. 연관관계의 주인만이 데이터 베이스 연관관계와 매핑되고 외래 키를 관리(등록, 수정, 삭제) 할 수 있기 때문이다. 반대로 주인이 아니면 읽기만 할 수 있다. 연관관계의 주인 = 외래키 관리자 인 것이다.

연관관계를 설정할 때 주인은 주인답게 별도의 mappedBy속성을 사용하지 않고 주인이 아닌쪽이 mappedBy속성을 사용해 속성값으로 이 연관관계인 주인님의 필드을 지정한다. (주인님!)

 

 

 

JPA 05.02 - 양방향 연관관계와 연관관계의 주인

양방향 연관관계 이제 팀에서도 회원으로 접근할 수 있도록 양방향 연관관계로 매핑해 보겠습니다. 먼저 객체 연관관계를 살펴보겠습니다. 회원과 팀은 다대일 관계이며 반대로 팀에서 회원은

delusidiot.tistory.com

 

 

  • 양방향 연관 관계의 주의점 

연관관계 주인에 값을 입력하지 않고, 주인이 아닌 entity에 값을 입력하면 문제가 생길 수 있다. 

Order order = new Order ("order", "order”);
em.persist(order);

Order order2 = new Order (”order2", "order2”);
em.persist(order2);

Member member = new Member("member", ”member”);

//여기가 실수 포인트!!! 
member.getOrders().add(order);
member.getOrders().add(order2);
em.persist(member);

 

member.order에만 값을 저장하고 order.member에 아무 값도 입력하지 않았다면 외래키 값도 null로 저장되는 상태가 된다.

이런 문제를 해결하는 방법은 객체 관점에서는 양쪽 방향에서 모두 값을 입력해주는 것이 가장 안전하다. 

하지만 매번 양쪽으로 값을 넣는 건 깜빡할 여지가 많아 이런 식으로 메소드를 만들어 양쪽에 저장해 주는 방식도 있다.

private Order order;
  public void setMember(Member member) {
    this.member = member;               // order.member에 저장
    member.getOrders().add(this);	// member.order에 저장
  }
  ...
}

 

 

'Programming > Spring' 카테고리의 다른 글

스프링 부트(Spring Boot) 다운 그레이드  (0) 2022.12.07
ORM / JPA / Hibernate / Spring Data JPA  (0) 2022.12.05
영속성 컨텍스트  (0) 2022.12.02
Spring Annotation 정리  (0) 2022.11.30
의존성과 라이브러리  (0) 2022.11.28

+ Recent posts