/ SPRING BOOT, SPRING, JPA

JPA 관계, 방향 설정해보기

스프링 목록

스프링 부트 목록


생각보다 까다로운 관계 설정과 방향설정



관계 설정

(@ManyToOne, @ManyToMany, @OneToMany)

  • 게시글은 댓글을 가지고있음, 게시글 하나에 댓글이 여러개 달릴수 있음
    게시글(One) To 댓글(Many)

  • 댓글 입장에선 여러개의 댓글은 하나의 게시글에 달려있음
    댓글(Many) To 게시글(One)

방향설정

  • 방향 설정은 한쪽이 어느쪽을 자주 포함하는지에 따라 설계

    ex) 쇼핑몰 상품결제내역 테이블이 존재
    결제내역에서 어떤 상품을 샀는지 조회하는 경우는 많음
    반대로 상품을 조회하면서 결제내역을 보는 경우는 적음

    결제내역 -> 상품 (단방향)

    반대의 경우를 조회하고 싶을때는 양방향으로 만들지말고 Fetch Join 이용





단방향

설계하기가 생각보다 까다로움, 그러나 설계 후에는 코드관리 용이


1. @Many To One(단방향)

  • 구현 상대적으로 쉬움
@Entity
@Table
public 결제내역{
    //결제에 관한 멤버변수들 .. 날짜, 갯수 등등
    @Id
    Long 결제번호;
    int 갯수.. 
    Date 결제날짜 등등..


	//결제내역이 [Mnay]인 상황

    // 해당 어노테이션과,  One의 자료형만 적어주면 끝
    @ManyToOne
    Product product //결제한 상품
}

결제 내역에서 상품을 보는경우는 많음(단방향)

가끔씩, 상품을 이용해 결제내역 정보를 보고싶으면
(해당 스웨터[상품]의 총 판매 수 )

Product에 @OneToMany 사용하지 말고(양방향으로 바꾸지 말고!)
Fethch Join으로 사용

  • (JPA가 아니였으면 sql사용시 아우터 조인으로 간단히 처리할수 있는 상황)





2 .@One To Many(단방향)

  • 생각보다 까다로움
  • repository 만들때 One 위주로 만들것 (작업단위, 업(JOb) 위주. TDD)
  • cascade로 영속성 관리, 영속성 전이 까지 고려해야함
  • 영속성 전이 할거면 Lazy Loading문제 꼭 고려해야함 아래설명
  • @JoinColumn 쓸지 안쓸지 잘 고려해야함
@Entity
@Table
public 게시판{
    //게시판에 관한 멤버변수들 ..
    @Id
	private int 게시판_번호;
	private String 글제목;
	private String 작성자; //등등..



	//게시판이 [One]인 상황

	//Many를 리스트 형식으로 가지고 있음
	@OneToMany(
				//CascadeType 사용시, 해당 객체 변경시 자동으로 저장해줌(영속성 전이)
				cascade= CascadeType.ALL,

				//영속성 전이 사용시 Lazy Loaind 문제때문에 fetch나 다른 처리 해줘야함
				fetch=FetchType.EAGER  )

	//조인 컬럼 사용할 경우 DB에 BoardFile의 (PK키)를 FK로 참조
	@JoinColumn(name="file_no")
	List<BoardFile> files;

영속성 전파 할경우 Lazy Loading 문제 해결 꼭해야함

  • 해결법 예시 : fetch로 EAGER 주거나, @Transactional 어노테이션 주기
  • 문제는 EAGER 사용시 N+1 문제 발생


@JoinColumn 사용 안하면 JPA가 자동으로 브릿지 테이블 생성해줌







양방향

초기 설계가 단방향에 비해 빠름, 그러나 코드가 복잡해짐


3. @ManyToOne, @OneToMany (양방향)



a)One (의 시점)

mappedBy 써줘야함 ( One (부모) 삭제 시 Many(자식) 문제가됨 )
SELECT 할시에 Lazy Loding 때문에 에러 발생할 수 있음 (영속성 전파 관련문제)


@Entity(name = "order_info")
@Table(name = "jpaorder_info")

@SequenceGenerator(name = "jpaorder_seq_generator",
					sequenceName = "JPAORDER_SEQ",
					initialValue = 1,
					allocationSize = 1)//1씩증가
public class OderInfo {

	@Id
	@GeneratedValue( strategy = GenerationType.SEQUENCE,
					generator = "jpaorder_seq_generator"
			)
	private int order_no;
	
	@CreationTimestamp
	private Date order_dt;
	

// 이 부분은 OrderInfo가 [One]인 상황
    
    //[Many]
	@OneToMany(cascade = CascadeType.ALL,
                //자식들도 인서트 되게할려면all(영속성 전이)
			   fetch = FetchType.EAGER,
			   mappedBy = "order_info")
	private List<OderLine> lines;
	




//추가 예시

// 원래의 예제 코드라면 One의 입장에서만 보여줬겟지만
// 실제의 코드는 이런식으로 Many이면서, One일수도 있음

// 이 부분은 OrderInfo가 [Many]인 상황

    //[One]
	@ManyToOne
	@JoinColumn(name = "order_id") //이거안쓰면 ddl오토 시 이름자동으로 order_)c
	//One
	private Customer order_c;




b) Many (의 시점)

@ManyToOne 어노테이션만 적을시 브릿지 테이블 생성됨(ddl-auto 설정시)

@JoinColumn(FK를 위한 어노테이션) 사용하면One의 @Id가져옴(브릿지 테이블 안생김)

아래 예시는 @Id가 2개인 복합키인 예시! Serializable 구현 해야함


@Entity(name = "orderline")
@Table(name= "jpaorder_line")
public class OderLine implements Serializable{

	//복합키로 아이디 쓸때 시리얼라이저블 인터페이스 구현
	//객체직렬화를 가능하게 구현한다.

// 이 부분은 OrderLine가 [Many]인 상황

    //[One]
	@Id
	@ManyToOne
	@JoinColumn(name = "order_no" )
	private OderInfo order_info;



// 이 부분은 OrderLine가 [Many]인 상황

    //[One]
	@Id
	@ManyToOne
	@JoinColumn(name = "order_prod_no")
	private Pruduct order_p;








Lazy Loding 문제 해결법

  • @Transactional 사용
  • @OneToMany에 fetch=FetchType.EAGER사용(N+1문제 유의)
    성능에 영향을 줌으로 신중히 사용할 것

N+1문제 발생 설명(링크)





@Transactional 쓰면 오류 안나는 이유

  • PC는 트랜잭션과 관련있어서 트랜잭션 시작시 PC할당되고, 완료시 사라짐



@Transactional 없는 경우

  1. 레이지 로딩이 SELECT 먼저 읽어옴
    글번호, 글제목 가져옴 , 댓글은 아직 안가져옴(조인이라)

  2. SELECT 만하고 JOIN구문을 안하고 pc를 닫아버림

  3. o.getReplies 로 댓글가져오고싶어도 pc가 이미 닫혀서 오류


@Transactional 사용시

  1. 레이지 로딩이 SELECT 먼저 읽어옴
    글번호, 글제목 가져옴 , 댓글은 아직 안가져옴(조인이라)

  2. @Transactional 어노테이션이 로직 끝나기전까지 PC닫는거 막아 줌

  3. o.getReplies 구문 사용할 기회가 남아있음 (pc가 열려있음)
  4. o.getReplies select하고 종료





Auto