[TIL] JPA - 연관관계

phdljr·2023년 11월 13일
0

TIL

목록 보기
29/70
post-custom-banner

Entity의 연관관계

  • DB Table 간에는 방향이란 개념이 없음
  • Entity간에는 방향이 존재함
    • 단방향, 양방향
    • 엔티티에서는 필드에 해당되지 않는 객체가 없으면 참조하질 못함

1대 1 관계

  • 엔티티에선 외래 키의 주인은 일반적으로 다의 관계인 엔티티이다.
  • 1대1 관계에선 키의 주인을 직접 지정해야 한다.
  • @OneToOne 을 사용한다.

외래 키 주인만이 외래 키를 등록, 수정, 삭제할 수 있다.
주인이 아닌 쪽은 오직 외래 키를 읽기만 가능하다.

  • @JoinColumn 은 외래 키의 주인이 활용하는 에너테이션

    • 외래 키의 컬럼 명을 정해주는 역할
      • 기본 값이 설정되어 있어서 생략 가능
        • (비추천 → 1대 다 관계에선 JPA가 매핑 테이블을 만들 수가 있음)
    • 주인 엔티티만 @JoinColumn 이 있으면 된다.
  • 단방향일 경우

    • 음식 엔티티가 외래 키의 주인인 경우

      @Entity
      @Table(name = "food")
      public class Food {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
          private String name;
          private double price;
      
          @OneToOne
          @JoinColumn(name = "user_id")
          private User user;
      }
      
      @Entity
      @Table(name = "users")
      public class User {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
          private String name;
      }
    • 고객 엔티티가 외래 키의 주인인 경우

      @Entity
      @Table(name = "food")
      public class Food {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
          private String name;
          private double price;
      }
      
      @Entity
      @Table(name = "users")
      public class User {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
          private String name;
      
          @OneToOne
          @JoinColumn(name = "food_id")
          private Food food;
      }
  • 양방향일 경우

    • @OneToOne(mappedBy = {외래 키의 주인이 갖고 있는 외래 키의 필드 명})

      • 클래스를 의미하는게 아닌, 필드 명을 적어야 한다.
    • 이게 있으면 주인이 아니다.

      @Entity
      @Table(name = "food")
      public class Food {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
          private String name;
          private double price;
      
          @OneToOne
          @JoinColumn(name = "user_id")
          private User user;
      }
      
      @Entity
      @Table(name = "users")
      public class User {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
          private String name;
      
          @OneToOne(mappedBy = "user")
          private Food food;
      }
  • 외래 키의 주인이 아닌 쪽에서 연관 관계를 설정하는 방법

    • 다음과 같이, 주인 엔티티에 setter로 설정하면 된다.
    public class User {
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Long id;
      private String name;
    
      @OneToOne(mappedBy = "user")
      private Food food;
    
      public void addFood(Food food) {
          this.food = food;
          food.setUser(this);
      }
    }

N대 1 관계

  • @ManyToOne 을 사용한다.

  • 단방향 관계

    @Entity
    @Table(name = "food")
    public class Food {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
        private double price;
    
        @ManyToOne
        @JoinColumn(name = "user_id")
        private User user;
    }
    
    @Entity
    @Table(name = "users")
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
    }
  • 양방향 관계

    @Entity
    @Table(name = "food")
    public class Food {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
        private double price;
    
        @ManyToOne
        @JoinColumn(name = "user_id")
        private User user;
    }
    @Entity
    @Table(name = "users")
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
    
        @OneToMany(mappedBy = "user")
        private List<Food> foodList = new ArrayList<>();
    }
    • 단, User의 foodList에 food를 단순히 추가하면, 데이터베이스엔 영향이 가진 않는다.
      • 영향이 가게 하려면, 해당 객체에 주인 객체를 설정하고 추가해야된다.
    • 또한, List에 있는 데이터를 수정해도 DB에는 변함없다.
      • 트랜잭션 내의 범위에 있다면 영향이 간다.

1대 N 관계

  • 주인은 음식이지만, 실제 외래키는 고객이 들고있음
    • DB에선 하나의 음식이 유저들의 외래키를 여러 개 들고 있을 순 없음

    • 그래서 주인은 음식이지만, 고객한테 외래키를 넘겨줌

      @Entity
      @Table(name = "food")
      public class Food {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
          private String name;
          private double price;
      
          @OneToMany
          @JoinColumn(name = "food_id") // users 테이블에 food_id 컬럼
          private List<User> userList = new ArrayList<>();
      }
      
      @Entity
      @Table(name = "users")
      public class User {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
          private String name;
      }
    • 외래키가 고객에 존재하기 때문에, 추가적인 UPDATE가 발생된다는 단점이 존재함

  • @OneToMany 는 양방향 관계가 없음
    • @ManyToOne 에 mappedBy 옵션이 존재하지 않음
    • 단, 억지로 표현해줄 수는 있음
      • @JoinColumn 으로 생성과 수정을 못하도록 설정해두면 됨

        @Entity
        @Table(name = "users")
        public class User {
            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            private Long id;
            private String name;
        
        		@ManyToOne
        		@JoinColumn(name = "food_id", insertable = false, updatable = false)
        		private Food food;
        }

N대 M 관계

  • 다대다 관계는 중간 테이블을 생성해서 사용

  • 중간 테이블을 자동으로 생성해 주지만, 이를 사용할 경우 테이블의 변경이 일어나면 문제가 발생할 가능성이 존재 ⇒ 비추천

    • 개발자가 따로 중간 테이블을 만들어서 @ManyToOne 을 사용하는 것을 추천
  • 단방향 관계

    @Entity
    @Table(name = "food")
    public class Food {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
        private double price;
    
        @ManyToMany
        @JoinTable(name = "orders", // 중간 테이블 생성
        joinColumns = @JoinColumn(name = "food_id"), // 현재 위치인 Food Entity 에서 중간 테이블로 조인할 컬럼 설정
        inverseJoinColumns = @JoinColumn(name = "user_id")) // 반대 위치인 User Entity 에서 중간 테이블로 조인할 컬럼 설정
        private List<User> userList = new ArrayList<>();
    }
    
    @Entity
    @Table(name = "users")
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
    }
  • 양방향 관계

    @Entity
    @Table(name = "food")
    public class Food {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
        private double price;
    
        @ManyToMany
        @JoinTable(name = "orders", // 중간 테이블 생성
        joinColumns = @JoinColumn(name = "food_id"), // 현재 위치인 Food Entity 에서 중간 테이블로 조인할 컬럼 설정
        inverseJoinColumns = @JoinColumn(name = "user_id")) // 반대 위치인 User Entity 에서 중간 테이블로 조인할 컬럼 설정
        private List<User> userList = new ArrayList<>();
    }
    
    @Entity
    @Table(name = "users")
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
    
        @ManyToMany(mappedBy = "userList")
        private List<Food> foodList = new ArrayList<>();
    }
  • 중간 테이블 직접 생성하는 방식

    • 이렇게 하면 관리하거나 컨트롤하기 쉬워서 확장성에 좋음

    • 양방향으로 할지, 단방향으로 할지는 선택(양방향이 필수는 아님)

      @Entity
      @Table(name = "food")
      public class Food {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
          private String name;
          private double price;
      
          @OneToMany(mappedBy = "food")
          private List<Order> orderList = new ArrayList<>();
      }
      
      @Entity
      @Table(name = "users")
      public class User {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
          private String name;
      
          @OneToMany(mappedBy = "user")
          private List<Order> orderList = new ArrayList<>();
      }
      
      @Entity
      @Table(name = "orders")
      public class Order {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
      
          @ManyToOne
          @JoinColumn(name = "food_id")
          private Food food;
      
          @ManyToOne
          @JoinColumn(name = "user_id")
          private User user;
      }
profile
난 Java도 좋고, 다른 것들도 좋아
post-custom-banner

0개의 댓글