사용자 도구

사이트 도구


java:jpa:one-to-one

문서의 이전 판입니다!


JPA One-To-One

Primary Key 기반 One-To-One JPA 2 방식

* @MapsId를 사용하라.

Primary Key 기반 One-To-One JPA 1 방식

// 부모측
@Id
@Column(name="id")
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
 
@OneToOne(cascade = CascadeType.ALL, mappedBy="parent")
@primaryKeyJoinColumn
private Child child;
// 자식측
@Id
@GenericGenerator(name = "foreign_one_to_one_generator", strategy = "foreign", parameters = @Parameter(name = "property", value = "parent"))
@GeneratedValue(generator = "foreign_one_to_one_generator")
@Column(name = "parent_id")
private Long id;
 
@OneToOne(fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn(name = "parent_id", referencedColumnName = "id")
private Parent parent;
  • 주의! 양뱡향 매핑의 경우, 부모에 자식값을 설정하고, 자식에도 부모 값을 설정해야 부모를 저장하면 정상적으로 자식도 저장된다.

One-To-One 과 Lazy Loading

  • One-To-One 에서는 Lazy Loading이 잘 작동하지 않는다.
    1. 이유는 null 값이 가능한 OneToOne의 경우 프록시 객체로 감쌀 수 없기 때문이다.
    2. 만약 null 값이 가능한 OneToOne 에 프록시 객체를 넣는다면, 이미 그 순간 결코 null 이 아닌 프록시 객체를 리턴하는 상태가 돼 버리기 때문이다.
    3. 따라서 JPA 구현체는 기본적으로 One-To-One 관계에 Lazy 를 허용하지 않고, 즉시 값을 읽어 들인다.
  • 참조
  • 결코 null 일 수 없는 One-To-One 관계에서는 프록시를 설정하고 Lazy 로 작동하게 만드는 것이 가능하다. optional=false 를 지정한다(결코 Null일 수 없다는 뜻).
  • 아래 코드는 잘못되었다. PrimaryKeyJoin 의 경우에는 optional=false 일 경우에 데이터 저장 순서가 꼬여버린다. 정상적인 optional=false가 작동하려면 ForeignKey Join을 해야한다.
  •   @OneToOne(cascade = CascadeType.ALL, mappedBy = "parent", optional = false, fetch=FetchType.LAZY)
      @PrimaryKeyJoinColumn
      private Child child;
  • optional=false 는 Hibernate XML 매핑시 constrained=true 와 같다.

게시판 형태에서 게시글에 대해 One-To-One LazyLoading 구현

  • 게시판처럼 내용이 있지만, 목록에서는 내용을 보여주지 않는 경우 JPA 에서 내용 컬럼에 LazyLoading을 적용해도 현재의 JPA 구현체들이 필드 LazyLoading을 구현하지 못해서 결국 제목의 목록만 필요할 때도 내용까지 읽어들인다.
  • 이 경우 해결책은 내용부분을 다른 테이블로 분리하고 One-To-One 관계로 맺은 뒤에 내용 부분에 LazyLoading을 적용하는 것이다.
  • 단, “PrimaryKeyJoin” 방식은 LazyLoading이 사실상 불가능했다.
    • optional=false를 붙일 경우 부모 부분을 먼저 저장하지 않고 자식(내용부분)을 먼저 저장하려 들기 때문에 부모의 PrimaryKey 가 없어서 오류가 발생한다.
    • optional=true를 할 경우 저장은 잘 되지만 Lazy Loading은 작동하지 않게 된다.
  • 따라서 Foreign Key One-To-One 관계를 사용해야 한다.
  • // Parent.java 에서
    @OneToOne(cascade = CascadeType.ALL, optional = false, fetch = FetchType.LAZY)
    @JoinColumn(name="child_id", nullable = false, unique = true, insertable = true, updatable = false)
    @org.hibernate.annotations.ForeignKey(name="fk_parent_child_id"
    private Child child;
     
    // Child.java 에서
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Lob
    @Column(name="body", nullable=false)
    private String body;
    // 자식측에 Parent 로 가는 매핑을 넣지 말것. 그 경우 Lazy로 지정해도 부모값을 읽는 쿼리가 날라간다.
  • 위와 같이 만들면 꼭 필요할 경우에만 자식의 값을 읽을 수 있다. 단, 매핑은 부모쪽에서 관리하며 부모쪽 테이블에 자식을 가리키는 외래키 컬럼이 추가된다.
  • 값을 저장할 때는 부모 객체에 자식 객체를 설정하여 저장하면 자식까지 저장된다.
java/jpa/one-to-one.1326091434.txt.gz · 마지막으로 수정됨: 2012/01/09 15:43 저자 kwon37xi