티스토리 뷰

728x90

1. 코스 테이블과 리뷰 테이블 두개의 관계를 가지고 설명한다.

2. 코스와 리뷰는 One To Many의 관계를 가진다.

3. 코스가 삭제되면 리뷰 역시 삭제되어야 한다.

4. 아래는 데이터베이스 구조 도식이다.

  4-1 review 테이블이 추가되었고

  4-2 review 테이블의 course_id가 foreign key로 설정되어 있다.

2. One to Many uni-directional은 전의 one-to-one과 one-to-many와는 좀 다르다.

  2-1 uni-directional의 방향이 foreign key가 있는 테이블에서 시작하는 경우는 설정이 동일하다.

    2-1-1 해당 Entity에 관계하는 Entity의 속성을 추가하고 @Many-To-One과 @JoinColumn을 붙이면 된다.

    2-1-2 결국 One-To-One의 uni-directional과 동일한데

      2-1-2-1 이것은 테이블 설정이 동일하고 필요에 따라 many, one이 결정되기 때문이다.

 

  2-2 uni-direction의 방향이 반대인 경우는 좀 달라진다. (과정 Entity만 리뷰 정보를 가지는 경우)

    2-2-0 이 포스팅이 이 부분에 관한 내용이다.

    2-2-1 달라봤자 거기서 거기다.

 

    2-2-2 중요한 것은 @JoinColumn이라는 것의 의미인데

      2-2-2-1 관계가 있는 테이블과 join에 필요한 foreign key column을 명시하는 것이다.

      2-2-2-2 그러니 A테이블에 있던 B테이블에 있던 상관이 없다.

      2-2-2-3 사실 @JoinColumn이 없어도 동작한다. 하지만 명시하는 게 명확하다.

 

    2-2-3 @JoinColumn을 foreign key가 없는 Entity에 붙여준다. 여기서는 Course Entity의 reviews 속성이다.

      2-2-3-1 이게 가능한 이유는 속성에 이미 Entity type이 지정되어 있기 때문이다.

        2-2-3-1-1 지정된 entity와 매핑되어 있는 테이블의 foreign key를 찾아가게 된다.

 

    2-2-4 간단하게 말하면 direction설정에 foreign key가 있는 Entity가 참여하는 경우는 그 Entity에

    2-2-5 참여하지 않는 경우에 상대 Entity에 @JoinColumn을 붙인다.

    2-2-6 아래 Review Entity는 uni-direction이기 때문에 lombok으로 인한 recursive가 없으므로 @Data를 사용했다.

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "review")
@Data
@NoArgsConstructor
public class Review {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Column
  private String comment;

  public Review(String comment) {
    this.comment = comment;
  }
}


// 수정된 Course Entity class
@Entity
@Table(name = "course")
@Getter
@Setter
@NoArgsConstructor
public class Course {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  
  @Column
  private String title;
  
  @ManyToOne(cascade = { CascadeType.DETACH, 
      CascadeType.MERGE, CascadeType.PERSIST,CascadeType.REFRESH })
  @JoinColumn(name = "instructor_id")
  private Instructor instructor;

  @OneToMany(cascade = CascadeType.ALL)
  @JoinColumn(name = "course_id")
  private List<Review> reviews;
  
  
  public Course(String title) {
    this.title = title;
  }

  @Override
  public String toString() {
    return "Course [id=" + id + ", title=" + title + "]";
  }
  
  public void addReview(Review review) {
    if (this.reviews == null) {
      this.reviews = new ArrayList<>();
    }
    
    this.reviews.add(review);
  }
}

3. 데이터를 저장하는 코드이다.

  3-1 @JoinTable이 지정된 course를 저장하는 것이므로 자동으로 foreign키를 참조하고 저장한다.

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import pe.pilseong.hibernate_mapping.entities.Course;
import pe.pilseong.hibernate_mapping.entities.Instructor;
import pe.pilseong.hibernate_mapping.entities.InstructorDetail;
import pe.pilseong.hibernate_mapping.entities.Review;

public class SaveEntity {
  public static void main(String[] args) {
    SessionFactory factory = new Configuration().configure()
        .addAnnotatedClass(Instructor.class)
        .addAnnotatedClass(InstructorDetail.class)
        .addAnnotatedClass(Course.class)
        .addAnnotatedClass(Review.class)
        .buildSessionFactory();
    
   
    Review review1 = new Review("Best course ever!!!"); 
    Review review2 = new Review("Mediocre course ever!!!"); 
    Review review3 = new Review("Worst course ever!!!"); 
    
    Session session = factory.getCurrentSession();
    try {
      session.beginTransaction();
      
      Course course = session.get(Course.class, 2L);
      course.addReview(review1);
      course.addReview(review2);
      course.addReview(review3);
      
      session.save(course);
           
      session.getTransaction().commit();
      
      session = factory.getCurrentSession();
      session.beginTransaction();
      
      course = session.get(Course.class, 2L);
      System.out.println("Course :: " + course.toString());
      course.getReviews().stream().forEach(System.out::println);
      
      session.getTransaction().commit();
      
    } catch (Exception e) {
      e.printStackTrace();
      
    } finally {
      session.close();
      factory.close();
    }
  }
}

4. Course를 가지고 오는 예제

public class FetchEntity {
  public static void main(String[] args) {
    SessionFactory factory = new Configuration().configure()
        .addAnnotatedClass(Instructor.class)
        .addAnnotatedClass(InstructorDetail.class)
        .addAnnotatedClass(Course.class)
        .addAnnotatedClass(Review.class)
        .buildSessionFactory();
    
    Session session = factory.getCurrentSession();
    
    try {
      session.beginTransaction();
      
      Course course = session.get(Course.class, 2L);
      System.out.println("Fetchd course :: " + course.toString());
      course.getReviews().stream().forEach(System.out::println);      
      
      session.getTransaction().commit();
    } catch (Exception e) {
      e.printStackTrace();
      
    } finally {
      session.close();
      factory.close();
    }
  }
}

// 결과
Fetchd course :: Course [id=2, title=Spring Boot intermediate]
Review(id=1, comment=Best course ever!!!)
Review(id=2, comment=Mediocre course ever!!!)
Review(id=3, comment=Worst course ever!!!)

5. 삭제 예제 이다.

  5-1 첫번 째는 하나의 Review만 삭제한 것이고 comment 달아 놨다.

  5-2 두번 째는 course를 지우니 관련된 review가 다 지워지는지 테스트 한다.

public class DeleteEntity {
  public static void main(String[] args) {
    SessionFactory factory = new Configuration().configure()
        .addAnnotatedClass(Instructor.class)
        .addAnnotatedClass(InstructorDetail.class)
        .addAnnotatedClass(Course.class)
        .addAnnotatedClass(Review.class)
        .buildSessionFactory();
    
    Session session = factory.getCurrentSession();
    
    try {
      session.beginTransaction();
      
//      Review review = session.get(Review.class, 1L);
//      System.out.println("Fetched Review :: " + review.toString());
//      
//      session.delete(review);      
      
      Course course = session.get(Course.class, 2L);
      System.out.println("Fetchd course :: " + course.toString());
      course.getReviews().stream().forEach(System.out::println);
      
      session.delete(course);
      
      session.getTransaction().commit();
    } catch (Exception e) {
      e.printStackTrace();
      
    } finally {
      session.close();
      factory.close();
    }
  }
}

// 결과 - 첫번째
Fetched Review :: Review(id=1, comment=Best course ever!!!)
Hibernate: delete from review where id=?


// 결과 - 두번째
Fetchd course :: Course [id=2, title=Spring Boot intermediate]
Review(id=2, comment=Mediocre course ever!!!)
Review(id=3, comment=Worst course ever!!!)
Hibernate: update review set course_id=null where course_id=?
Hibernate: delete from review where id=?
Hibernate: delete from review where id=?
Hibernate: delete from course where id=?

 

6. 솔직히 존나 복잡해 보인다.

  6-1 필요하면 공식 doc을 참조하길 바란다.

 

JoinColumn (Java(TM) EE 7 Specification APIs)

(Optional) The name of the foreign key column. The table in which it is found depends upon the context. If the join is for a OneToOne or ManyToOne mapping using a foreign key mapping strategy, the foreign key column is in the table of the source entity or

docs.oracle.com

  6-2 요약하면 아래와 같다. 위의 doc에 있는 그대로 영어로 복사했다.

(Optional) The name of the foreign key column. The table in which it is found depends upon the context.

  • If the join is for a OneToOne or ManyToOne mapping using a foreign key mapping strategy, the foreign key column is in the table of the source entity or embeddable.
  • If the join is for a unidirectional OneToMany mapping using a foreign key mapping strategy, the foreign key is in the table of the target entity.
  • If the join is for a ManyToMany mapping or for a OneToOne or bidirectional ManyToOne/OneToMany mapping using a join table, the foreign key is in a join table.
  • If the join is for an element collection, the foreign key is in a collection table.
728x90
댓글