엔티티 매핑

김강현·2023년 3월 19일
0

ORM-JPA

목록 보기
4/9

여러가지 키 매핑을 객체 변수에 지정할 수 있음.

@Id
@Column(name = "oooo")
@Enumerated(EnumType.STRING) <- number 말고 string 으로 하는게 국룰
@Temporal(TemporalType.TIMESTAMP) <- TIMESTAMP 로 하는 편
@Lob
@Transient

~ key mapping 어노테이션 ~
@GeneratedValue(strategy = GenerationType.AUTO)

id 값을 자동으로 생성.

  1. DB 에 위임하여 직접 하도록.
  • strategy = GenerationType.IDENTITY
  • 영속성 주입을 할때, db에 insert 함수가 실행됨
  1. DB 시퀀스 오브젝트 사용
  • @SequenceGenerator
  • sequence 용 table
  • DB에서 키 50개를 미리 가져오는 설정도 있음 (allocationSize)
  • 추가적으로 더 필요해지면, 그때 또 요청.
  1. DB 키 생성용 table
  • @TableGenerator 필요
  • 데이터 DB 에는 db함수가 들어가지 않고, key만을 위한 함수가 실행.

해당 내용들은 객체와 DB를 매핑하기 위한 annotation 들

우리가 필요한 것은

연관관계 매핑

다대일 관계 (단방향)

[1] 객체를 데이터(Table) 중심으로 설계

@Entity
public class Member {
   @Id @GeneratedValue
   @Column(name = "MEMBER_ID")
   private Long id;
   
   @Column(name = "TEAM_ID")
   private Long teamId;
   
   private String name;
   private String city;
   private String street;
   private String zipcode;
}

[2] 객체 중심으로 설계

@Entity
public class Member {
   @Id @GeneratedValue
   @Column(name = "MEMBER_ID")
   private Long id;
   
   @ManyToOne
   @JoinColumn(name = "TEAM_ID")
   private TeamModel team;
   
   private String name;
   private String city;
   private String street;
   private String zipcode;
}
private Long teamId;
private TeamModel team;

이 두개의 차이임. 객체를 포함하여 객체를 구성하기 위함!

다대일 관계 (양방향)

양방향인 경우에는 주인을 정해주어야함. (여러가지 이유로)
외래키가 있는 곳을 주인(Owner)으로 설정!! (하는 것을 권장하심)

mappedBy 를 통해 주인을 지정해준다.

정확히는 mappedBy가 있으면, 주인의 반대편!

@OneToMany(mappedBy = "team")
-> 나는 team 에 의해 관리되고 있어!

  • 데이터 수정 효율성.
  • jpa server function 실행 명확함 제공.
  • Lazy 기능 용이
(1) 우리 jpa는 이 코드가 생각처럼 작동하도록 하지 못함!

Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");

team.getMembers().add(member);
em.persist(member);
(2) 이렇게 해줘야 우리 생각처럼 작동함 (즉, 주인 객체에서 set 이 일어나야함)

Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");

member.setTeam(team);
em.persist(member);

(1), (2)의 차이에서 보듯이, jpa도 이정도까지는 객체화 지원을 안해주나봄
(어려워서라기 보다는, 아마 내부 로직/설계 상의 단점, 한계가 실제로 있지 않을까...)
아이러니 하게도, flush, clear 를 해주고 나면 원하는 대로 작동.

flush() 가 호출되고 나면 상관이 없겠지만은,
우리가 EntityManager 의 transaction의 commit 혹은 flush 되기 전에,
객체 다운 활용을 하고 싶다면, ((1) 처럼만 하면, getMembers()가 업데이트 안되어 있음)

(3) transaction 동안, 혹은 flush 되기 전에 생각처럼 사용될 수 있도록!!

Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");

team.getMembers().add(member); // 이 부분도 필요할 것이다.
member.setTeam(team);
em.persist(member);
활용!!
>> 실무에서는 두개를 같이 써야하는게 종종 누락이 생기기 마련...
>> 연관관계 편의 메소드 생성

Member.java 파일 setTeam 수정

pulic void setTeam(Team team){
  this.team = team;
  team.getMembers().add(this);
}

관례상 함수명을 setTeam => changeTeam 으로 하는 것을 권장.
member의 메소드에 하던, team 의 메소드로 만들던 그건 선택!!

무한루프 조심 (toString() 이나, lombok, JSON 생성 라이브러리)
김영한 개발자님 강조
엔티티는 절대로 컨트롤러나 api 에 반환하지 마라! 문제가 될 여지가 큼.
DTO 로 변환해서 반환 하는 것을 추천!!

양방향 매핑 정리

  • 단방향 매핑만으로도 이미 연관관계 매핑은 완료
  • 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐
  • jpql 에서 역방향으로 탐색할 일이 많음
  • 단뱡향 매핑을 잘하고 양방향은 필요할 때 추가 해도 됨!! (테이블에 영향 X)

연관관계 주인을 정하는 기준

  • 비즈니스 로직 기준으로 정하는 것이 X
  • 연관관계 주인은 외래 키의 위치를 기준으로 정해야함!!

요약

  • 비즈니스 프로세스상 연관관계를 잘 생각해서 객체 설계를 해야함
  • 만약 멤버를 부를 때마다, 그의 구매목록을 계속 들고오는건 비효율...
  • 추후 비즈니스 유지보수, TDD 등의 이유로 객체화 코드 기반으로 돌리는게 좋음
  • 왠만하면 단방향으로만 설계하고, 거의 공통적으로 쓰이는 경우만 양방향 매핑하기!

연관관계 매핑시 고려사항

  • 다중성
  • 단방향, 양방향
  • 연관관계의 주인

다중성

  • 다대일 @ManyToOne
  • 일대다 @OneToMany
  • 일대일 @OneToOne
  • 다대다 (실무에서는 거의 안씀)

단방향, 양방향

  • 테이블
    -> 테이블에는 방향이라는 개념이 없음. 어차피 Join 이기 때문에

  • 객체
    -> 객체에서는 단방향, 양방향이 있음 (참조 여부)

    테이블은 외래 키 하나로 연관관계를 맺지만,
    객체는 참조가 2군데가 있어서 외래키를 관리할 곳 지정!

    그래서 mappedBy를 추가한다고 해서 테이블 형태가 바뀌는 것은 아님!
    테이블과 객체사이 jpa 에서 인지를 하기 위해 있는 것 (search query 작동)
    사용하다가, search 하는 경우가 너무 많다! 하면 추가하는 거임

    일대다 단방향

일대다 에서 '다' 쪽을 주인이 아닌 '일' 쪽을 주인으로 하게될 경우

-> 의외로 객체에서는 자주 보이는 일
--> 허나 DB 에서 이렇게 관리 되는 건 말이 안됨!
---> 주인은 '일'이 맞으나, 외래키는 '다'에서 관리

query 를 한번 중간에 사용해 줘야하는 단점! '일'쪽에서 어떤 변화가 생겼는지를 detect 하고, 해당 Table 에 id 값이 같은 곳으로 가서, update를 해주어야함

  • (덧) 다 쪽에 해당 함수 추가하여 쓰는 것도 있음
  • @JoinColumn(insertable=false, updatable= false)
  • 마지 다대일 양방향 매핑처럼 됨. 읽기 전용으로 해줌으로서.

실무에서 이런 식의 객체와 테이블의 미스매칭은 유지보수가 상당히 헷갈려질 수 있다. 수십개의 테이블이 존재하는 상황에서, 이걸 전부 외워두고, 실수 안해야하는 것!

실제로 비즈니스 상으로 일대다 에서 일 을 주인으로 해야하는 설계가 필요할 수 있다. 하지만 유지보수 측면에서 다 를 주인으로 해주는 것이 좋아보임. 차라리 다대일 양방향으로 설계하는 것으로 설계 (김영한 개발자님)

일대일 매핑

  • 어디에 외래키를 넣어도 상관 없음

    주 테이블에 외래키 단방향

    Member.java

    @Entity
    public class Member {
    	@Id @GeneratedValue
       @Column(name = "MEMBER_ID")
       private Long id;
       
       @Column(name = "USERNAME")
       private String username;
       
       @OneToOne
       @JoinColumn(name = "LOCKER_ID")
       private Locker locker;
    }

    Locker.java

    @Entity
    public class Locker {
    	@Id @GeneratedValue
       @Column(name = "LOCKER_ID")
       private Long id;
       
       @Column(name = "NAME")
       private String name;
    }

대상 테이블에 외래키 단방향

  • 단방향 관계는 JPA 가 지원하지 x
  • 양방향 관계는 지원

대상 객체에도 양방향으로 넣어서, 대상테이블에서 관리..
사실상 주 테이블 외래키 단방향하고 똑같음!

재밌는 Trade off 를 소개해주심

DB 작업자 의 입장에서

외래키를 어디서 관리할까?
1. Locker 로 관리하면, 추후에 한 회원이 여러 Locker 를 가질 수 있게 되면, 쉽게 변경 가능하겠네
2. 근데, 여러 회원이 하나의 Locker 를 사용하게 된다면? Member에 외래키가 있는게 맞는걸?

객체 개발자의 입장에서

막상 실무를 해보면, Member는 항상 불러오는 값중 하나이고, Member가 외래키를 가지는게!

각자 장단점이 있음... 이건 추후에...!

다대다 매핑

  • 다대다 연관관계가 필요한 경우가 분명 있다.
  • 하지만 여러 단점들 존재... (쓰지 마라! 라는 권유)
  • 일대다, 다대일을 추가하여 똑같은 기능을 하도록 한다!
    (덧) PK (Primary Key, 개인키) , FK (Foreign Key, 외래키)
profile
this too shall pass

0개의 댓글