4. JPA 연관관계 처리

JPA 연관관계 처리의 순서

1) 필요한 각각의 클래스를 정의 2) 각 클래스의 연관관계에 대해 설정을 추가한다. A. '일대일','다대다' 등 연관관계 B. 단방향,양방향 설정 3) 데이터베이스상 원하는 형태의 테이블이 만들어지는지 확인 4) 테스트 코드를 통해서 정상작동 여부 확인

순수 데이터와 동작 데이터에 대한 설명 ( 본인의 생각 )

순수 데이터 : 아무런 사전 조건이 없는 순수 데이터를 의미한다.

  • 독립적인 라이프 사이클을 유지

  • 고객의 요구사항에 대한 모든 행위의 주어나 목적어

ex) 회원 테이블

User{
    private String name;
    private String email;
}

동작 데이터 : 요구사항에 대한 '동작'을 담당하는 데이터를 의미한다.

  • 두개의 순수 데이터 사이의 관계를 정의한다고 본다

ex) 메일을 보내는 동작여부 확인 DB

Send_to_Destination {
    private String sender;
    private String sender_email;
    private String receiver;
    private String receiver_email;
    private Date sending_time;
}

연관관계 설정

  • 일대일(One to One)

  • 일대다(One to Many)

  • 다대다(Many to Many)

이러한 설계과정은 ERD(Entity Relation Diagram)을 통해서 확인하고 구성해야된다.

  • JPA를 이용할 경우 반드시 "방향성"에 대한 설정이 필요하다!

SpringBoot 2.0 이상의 findOne()

스프링부트 2.0 에서 findOne 메소드는 findById()로 변경되었다. 또한 return 타입은 Optional 인데 Optional 타입은 null을 대신한다고 생각하는게 좋다. Optional의 말은 그대로 존재할 수 있고 존재하지 않을 수 도 있다는 의미이다. 과거 코드에서는 일일이 Null을 체크했지만 optional 부터는 null에 대해 신경쓰지 않아도 된다.

Optional을 쓸때는 주로 get() 이나 ifPresent()를 사용하여 결과를 반환받거나 처리하게 된다.

spring.jpa.database-platform을 지정해야 되는 이유!

이거에 대해 지정하지 않으면 MyISAM으로 지정되는데 이는 데이터 무결성을 제대로 체크하지 않는다. 또한 Auto로 지정되어있기 때문에 Mysql을 사용할 때 아래와 같이 지정하자.

spring.jpa.database-platform = org.hibernate.dialect.MySQL5InnoDBDialect

@Id의 GenerationType.AUTO 와 GenerationType.IDENTITY

GenerationType.AUTO 타입을 사용하면 DB에 hibernate_sequence라는 테이블이 생기게 되어 이 번호를 유지하는 방식으로 진행된다. 이렇게 되면 IDENTITY 타입으로 변경하더라도 auto_increment로 처리되지 않기때문에 테이블을 삭제후 새로 만들어주어야 한다.

따라서 IDENTITY 타입을 쓸때는 hibernate_sequence 에 대해 확인하고 사용하자.

JPA join 처리

@Query에서 사용하는 JPQL 자체가 테이블을 보는 것이 아니라, 클래스를 보고 작성하기 때문에 hibernate 5.0.X 경우 참조관계가 없는 다른 엔티티를 사용하는 것이 불가능하다. 하지만 SpringBoot 2.0이상에선 hibernate 5.2.X에서는 참조관계가 없어도 'ON'을 이용해서 Left OUTER JOIN을 처리할 수 있다.

  • SpringBoot 2.0 이상일 때,

  • Member.java

    ~~~ @Getter @Setter @Entity @ToString @Table(name="tbl_members") @EqualsAndHashCode(of="uid") public class Member {

    @Id private String uid; private String upw; private String uname;

}

* Profile.java

@Getter @Setter @ToString(exclude = "member") @Entity @Table(name="tbl_profile") @EqualsAndHashCode(of="fname") public class profile {

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

private String fname;
private boolean current;

@ManyToOne
private Member member;

}

* MemberRepository

@Query("Select m.uid, count(p) from Member m LEFT OUTER JOIN profile p "+" ON m.uid = p.member where m.uid = ?1 GROUP BY m") public List getMemberWithProfileCount(String uid);

* Test case

@Test public void testFetchJoin1() { List result = memberRepo.getMemberWithProfileCount("user1");

    result.forEach(arr-> System.out.println(Arrays.toString(arr)));
}
- SpringBoot 2.0 이하일 때,

두 객체 클래스간에 서로의 객체를 만들고 참조를 해주어야 한다.

즉, Member.java 에 profile class에 대한 참조가 필요하다는 점이다. 아래 코드 참조

@OneToMany private Profile profile;

## Cascading 필요성

영속성 전이를 통해서 연관관계가 있는 정보를 같이 삭제하게 하기 위함이다.

예를 들어, 유저가 회원가입해서 출석부에 출석을 했다. 해당 유저가 만약 회원탈퇴를 한다면 출석부에 역시 유저가 사라져야 된다. 그렇기에 유저의 정보를 지우기 위해서 영속성 전이에 대해 처리가 필요하다.

@OneToMany(cascade = CascadeType.ALL) @JoinColumn(name="pdsno") private List files;

## @Modifying , @Transaction

@Modifying의 경우 기본 @Query에서는 Select 만 지원을 한다 그렇기 때문에 @Modyfying이 Insert,Update,Delete 작업을 처리하게 지원을 해준다.
이와같은 Update나 Delete 작업을 하기위해서는 코드에는 @Transaction로 트렌젝션 처리가 필요하다. 하지만 처리되지 않을경우가 있는데 이는

Rolled Back transaction for ...

~~~ 롤백처리가 되었기 때문이다. 이러한 롤백처리를 막기위해 위에 @Commit을 필요로한다.

Last updated