2016년 12월 21일 수요일

[자바교육,스프링교육,JPA교육학원_탑크리에듀]JPA,영속성전이, 패치전략(EAGER, LAZY),orphanRemoval,CASCADE

JPA,영속성전이, 패치전략(EAGER, LAZY),orphanRemoval,CASCADE 


영속성 전이

n  엔티티를 영속상태로 만들면서 연관된 엔티티도 영속상태로 만들려면 영속성 전이를 사용하면 된다예를들면 영속성 전이 옵션을 사용하면 부모 엔티티를 저장/삭제할 때 자식 엔티티도 함께 저장/삭제할 수 있다.

@Entity
public class Dept {
   @Id  
@GeneratedValue
   private Long deptno;

   @OneToMany(mappedBy = dept)
   private Set<Emp> emps = new HashSet<Emp>();
   ……
}
@Entity
public class Emp {
   @Id
@GeneratedValue
   private Long Id;

   @ManyToOne
   private Dept dept;
   ……
}

private static saveNoCascade(EntityManager em) {
   Dept dept = new Dept();
   em.persist(dept);

   Emp emp = new Emp();
   emp.setDept(dept);        // 부모자식 연관관계 설정
   dept.getEmps().add(emp);

   em.persist(emp);
}

JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속상태여야 한다부모/자식 모두 영속상태로 만들어야 하는데 이럴 경우 영속성 전이를 사용하면 부모가 영속화 될 때 자식도 영속객체로 만들 수 있다.

n  CascadeType은 여러 종류가 있다.
ü  ALL : 부모의 영속성 변화가 자식에게 모두 전이 시킨다. (부모가 영속화되면 자식도 영속화 되고부모가 저장되면 자식도 저장되고부모가 삭제되면 자식도 삭제된다.)
ü  PERSIST : 부모가 영속화 될 때 자식도 영속화 된다.
ü  MERGE : 트랜잭션이 종료되고 detach 상태에서 연관 엔티티를 추가하거나 변경된 이후에 부모 엔티티가 merge()를 수행하게 되면 변경사항이 적용된다연관 엔티티의 추가 및 수정 모두 반영된다.
ü  REMOVE : 부모를 삭제할 때 연관된 자식 엔티티도 삭제된다.
ü  REFRESH : 부모 엔티티가 REFRESH되면 연관도 자식 엔티티도 REFRESH 된다.
ü  DETACH : 부모 엔티티가 detach를 수행하면 연관된 엔티티도 detach 상태가 되어 변경 사항이 반영되지 않는다.


영속성 전이(CASCADE 저장삭제)

n  엔티티를 영속상태로 만들면서 연관된 엔티티도 영속상태로 만들려면 영속성 전이를 사용하면 된다.
n  영속성 전이 기능을 이용하여 부모 엔티티를 저장할 때 자식 엔티티도 같이 저장되게 할 수 있다.

@Entity
public class Dept {
   @Id  
@GeneratedValue
   private Long deptno;

   @OneToMany(mappedBy = dept, cascade = CascadeType.PERSIST)
   private Set<Emp> emps = new HashSet<Emp>();
   ……
}
@Entity
public class Emp {
   @Id
@GeneratedValue
   private Long Id;

   @ManyToOne
   private Dept dept;
   ……
}

private static saveNoCascade(EntityManager em) {
   Emp emp1 = new Emp();
   Emp emp1 = new Emp();
   Dept dept = new Dept();
   emp1.setDept(dept);   // 부모자식 연관관계 설정
   emp2.setDept(dept);   // 부모자식 연관관계 설정
   dept.getEmps().add(emp1);
   dept.getEmps().add(emp2);
  
   em.persist(dept);  //부모저장
}

EMP 테이블을 SELECT 해보면 두건이 입력되었음을 확인 할 수 있다.
.
n  이전에 저장한 부모/자식을 모두 삭제하려면 아래 왼쪽처럼 해야 한다.
n  영속성 전이는 엔티티를 삭제할 때도 가능하다.(CascadeType.REMOVE)

Dept dept = em.find(Dept.class, 1L);
Emp emp1 = em.find(Emp.class, 1L);
Emp emp2 = em.find(Emp.class, 2L);
em.remove(emp1);
em.remove(emp2);
em.remove(dept);

è

CasCadeType.REMOVE를 지정한 경우
private static deleteCascade(EntityManager em) {
   Dept dept = em.find(Dept.class, 1L);
   em.remove(dept);
}

실행하면 DELETE SQL문을 세번 실행해서 레코드를 삭제한다.  1 DEPT와 연관된 EMP들은 모두 삭제되며 참조무결성 때문에 자식부터 먼저 삭제되고 부모를 삭제한다.
만약 부모쪽에 CascadeType.REMOVE를 지정하지 않고 실행하면 DEPT만 삭제되므로 외래키 때문에 부모를 삭제할 수 없다는 오류가 발생한다..


영속성 전이(orphanRemoval 속성)

n  JPA에서는 부모엔티티와 연관관계가 끊어진 자식을 자동으로 삭제할 수 있는 기능이 있는데 @OneToOne 또는 @OneToMany의 속성에 orphanRemoval 속성을 “true”로 주면 된다엔티티에서 삭제가 일어난 경우 연관관계에 있는 엔티티도 같이 삭제할지의 여부를 결정한다. Cascade JPA 레이어의 정의이고 이 속성은 DB레이어 에서 직접 처리한다기본은 false.

@Entity
public class Dept {
   @Id   @GeneratedValue
   private Long deptno;

   @OneToMany(mappedBy = dept, orphanRemoval = true)
   private Set<Emp> emps = new HashSet<Emp>();
   ……
}
@Entity
public class Emp {
   @Id @GeneratedValue
   private Long Id;
   @ManyToOne
   private Dept dept;
   ……
}

Dept dept = em.find(Dept.class, 1L);
Dept.getEmps().remove();

컬렉션에서 제거된 Emp객체가 실제 DB에서도 삭제된다.

orphanRemoval = true vs DDL On Delete Cascade vs CascadeType.REMOVE

먼저 예문을 보자.

@Entity
class Emp {
@OneToOne(orphanRemoval=true)
    private Addr addr;
}
Emp 엔티티가 삭제될 때 참조가 끊어진 연관된 Addr 엔티티도 삭제하라는 의미이며 DB에서도 삭제되는데참조(연결)가 끊어진 Addr 객체는 DB에서도 삭제된다는 뜻이다.

@Entity
class Emp {
@OneToOne(cascade=CascadeType.REMOVE)
    private Addr addr;
}
Emp 엔티티가 삭제될 때 연관된 Addr 엔티티도 삭제하라는 의미이며 DB에서도 삭제된다.

@Entity
class Emp {
@OneToMany(orphanRemoval=true)
    private List<Addr> addr;
}
addr 컬렉션에서 Addr 객체가 제거되는 경우 DB에서도 삭제하라는 의미 즉 참조가 끊어진 자식 객체를 제거하라는 뜻.

orphanRemoval JPA2.0 이상에서 지원하는 것으로 ORM 스펙, JPA 레벨에서의 정의이고 On Delete Cascade DBMS 레벨에서 작동되며 하는 일은 같다. orphanRemoval @OneToMany 연관에서 부모 엔티티의 컬렉션 등에서 자식 엔티티가 삭제될 때 참조가 끊어지므로 DB 레벨에서도 삭제되고, @OneToOne 연관에서는 부모 엔티티가 삭제될 때 연관된 엔티티가 참조가 끊어지므로 DB에서 삭제된다즉 참조연결이 끊어진(Disconnected엔티티를 같이 삭제하라는 의미로 참조가 끊어진 객체들을 정리할 때 유용하다.

cascade=CascadeType.REMOVE는 참조가 끊어진다고 해서 자동 삭제되는 것은 아니고 명시적으로 연관 엔티티가 삭제될 때 같이 삭제하라는 영속성 전이와 관련된 옵션이다.

반면 On Delete Cascade DB레벨에서(DDL) 부모 테이블의 레코드가 삭제될 때 자식레코드도 같이 삭제하라는 의미이다.




n  프록시 객체는 주로 연관된 데이터를 지연로딩(Lazy Loading)할 때 사용한다.

     Emp emp = em.find(Emp.class, 1L);     // 1. 1번 사원 검색
     Dept dept = emp.getDept();           // 2. 객체 그래프 탐색
     String dname = dept.getName();       // 3. Dept 엔티티 사용


n  Emp 엔티티를 조회하는 1번에서 연관된 Dept의 엔티티를 즉시 또는 지연로딩 할 수 있는데 즉시로딩은 Emp를 조회할 때 연관된 Dept 엔티티도 같이 조회하는 것이며 Emp dept 속성에@ManytoOne(fetch=FetchType.EAGER) 라고 하면 된다반대로 연관된 엔티티를 실제 사용될 때 조회될 수 있도록 할 수 있는데 지연로딩 이라 하고 FetchType.LAZY 라고 설정하면 된다.

class Emp {
     @Id
     @GeneratedValue
     Long empno;

     @ManyToOne(cascade=CasecadeType.ALL,
                      fetch=FetchType.LAZY)
     Set<Dept> depts = new HashSet<Dept>();
}

class Dept {
      @Id @GeneratedValue  Long deptno;
      ……
}

void findEmp() {
   Dept dept = newDept();
   dept.setDname(“총무부”);
   deptRepository..save();
   Emp emp1 = empRepository.findOne(iL);
   emp1.getDept().setDname(“영업부”);
   …
}

위의 경우 LazyInitializationException이 발생한다지연로딩(LAZY)이라면 findEmp() 메소드위에 반드시 @Transactional을 지정하거나 FetchType.EAGER로 해야 한다.



 패치전략(EAGER, LAZY) – 프록시

n  JPA에서 EntityManager find()로 엔티티를 조회하는 경우 영속성 컨텍스트에 엔티티가 없으면 DB에서 조회를 하는데 이 엔티티를 사용하든 안하든 DB에서 조회하므로 부하가 있을 수 있다이러한 경우는 getReference()로 조회를 하면 되고 실제 엔티티가 사용하는 시점에 조회를 한다.

n  최초 getReference()는 실제 DB를 조회하지 않고 엔티티 객체도 생성하지 않으며 나중에 DB접근을 위한 프록시 객체를 리턴하고 실제 엔티티가 존재한다면 실제 엔티티를 리턴한다.

n  프록시는 실제 객체의 참조를 보관하는데실제 클래스를 기반으로 만들어 지므로 실제 객체와 유사하다클라이언트 입장에서는 실제 그 객체로 인식하고 메소드를 호출하면 프록시가 원래 객체의 메소드를 호출해 준다.
    
     Emp e = em.getReference(Emp.class, 1L);       //ID 1인 사원조회
     String name = e.getEname();                   //프록시가 실제 DB에서 조회한다.

n  프록시는 영속성 컨텍스트에 엔티티가 생성되어 있지 않으면 영속성 컨텍스트에 객체 생성을 요청하는데  이를 프록시 초기화라 하고 처음 사용될 때 한번만 초기화 된다초기화되면 프록시를 통해 실제 엔티티에 접근 가능하다.

     1. 영속성 컨텍스트는 실제 DB를 검색해서 엔티티를 생성한다.
     2. 프록시는 생성된 Entity의 참조를 e에 보관한다.
     3. 프록시가 실제 엔티티의 getName() 메소드를 호출하고 결과를 반환한다.

n  준영속 상태와 초기화 : 준영속 상태의 엔티티를 초기화 하면 LazyInitializationException이 발생한다.

     Emp e = em.getReference(Emp.class, 1L);     //ID 1인 사원조회
     ……
     transaction.commit();     em.close();         //close로 인해 준영속 상태가 된다
     e.getName();                              //준영속 상태 초기화이므로 오류발생

n  JPA의 기본 Fetch 전략은 연관된 엔티티가 하나면 즉시로딩(FetchType.EAGER), 컬렉션이면 지연로딩(FetchType.LAZY)을 사용한다가능하면 모든 연관에 지연로딩을 사용하는 것이 좋다.

첨부파일 URL참조 - http://ojc.asia/bbs/board.php?bo_table=LecJpa&wr_id=265

댓글 없음:

댓글 쓰기