영속성 전이
JPA에서 처리하는 Entity의 상태에 따라 종속적인 객체들의 영속성도 함께 처리되는 것이며 총 5개의 옵션이 있다.
이번 게시글에서는 PERSIST와 REMOVE에 대해 테스트해보려 한다. 테스트에 사용할 Entity들은 Team과 Member이며 OneToMany로 양방향 매핑되어있다.
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "team_id")
private Long id;
private String name;
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<>();
...
}
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
private String username;
private int age;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;
...
}
PERSIST
Persist옵션은 Entity가 persist될 때, 즉 save될 때 동작한다. 먼저 Casecade 옵션 없이 테스트해보겠다.
@SpringBootTest
@Transactional
class CascadeTestApplicationTests {
@Autowired
EntityManager em;
@Test
public void testEntity() {
Team teamA = new Team("teamA");
Team teamB = new Team("teamB");
Member member1 = new Member("member1", 10, teamA);
Member member2 = new Member("member2", 20, teamA);
Member member3 = new Member("member3", 30, teamB);
Member member4 = new Member("member4", 40, teamB);
teamA.getMembers().addAll(List.of(member1, member2));
teamB.getMembers().addAll(List.of(member3, member4));
em.persist(teamA);
em.persist(teamB);
//초기화
em.flush();
em.clear();
//확인
System.out.println("===========================================================================================================");
List<Team> teams = em.createQuery("select t from Team t", Team.class).getResultList();
teams.forEach(System.out::println);
List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
members.forEach(System.out::println);
System.out.println("===========================================================================================================");
}
}
두개의 Team에 각각 2명의 Member를 매핑해 주었다. 그리고 persist는 Team에만 해준다. 이렇게 된 경우 Member들은 저장되지 않는다.
그래서 Member는 조회되지 않았고 Team에 매핑한 Member들도 전혀 저장되지 않은 상태이다.
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "team_id")
private Long id;
private String name;
@OneToMany(mappedBy = "team", cascade = CascadeType.PERSIST)
List<Member> members = new ArrayList<>();
...
}
반대로 Team의 members에 Persist 옵션을 넣어서 테스트 해보자. 테스트코드는 동일하며 결과를 확인해보면 Member들이 저장된 것을 확인할 수 있다.
Persist 옵션으로 Team에 연관된 Member들도 연속성 전이를 일으키며 같이 저장된 상태이다.
REMOVE
Remove 옵션은 Entity가 삭제될 때 작동한다.
@SpringBootTest
@Transactional
class CascadeTestApplicationTests {
@Autowired
EntityManager em;
@Test
public void testEntity() {
Team teamA = new Team("teamA");
Team teamB = new Team("teamB");
Member member1 = new Member("member1", 10, teamA);
Member member2 = new Member("member2", 20, teamA);
Member member3 = new Member("member3", 30, teamB);
Member member4 = new Member("member4", 40, teamB);
teamA.getMembers().addAll(List.of(member1, member2));
teamB.getMembers().addAll(List.of(member3, member4));
em.persist(teamA);
em.persist(teamB);
//초기화
em.flush();
em.clear();
//확인
System.out.println("===========================================================================================================");
List<Team> teams = em.createQuery("select t from Team t", Team.class).getResultList();
teams.forEach(System.out::println);
List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
members.forEach(System.out::println);
System.out.println("===========================================================================================================");
em.remove(teams.get(0));
em.flush();
em.clear();
List<Team> teams2 = em.createQuery("select t from Team t", Team.class).getResultList();
teams2.forEach(System.out::println);
List<Member> members2 = em.createQuery("select m from Member m", Member.class).getResultList();
members2.forEach(System.out::println);
System.out.println("===========================================================================================================");
}
}
위에서 사용한 테스트 코드에 가져온 팀중 첫번 째 팀을 삭제하고 다시 조회를 하였다. 현재는 cascade 옵션이 없는 상태기 때문에 이 코드를 실행하면 flush때 예외가 발생한다.
Team은 삭제되었지만 연관되있는 Member에 대한 처리가 없기 때문이다. 이 코드가 동작하게 만들려면 Team에 연관된 Member들도 같이 삭제해줘야 한다.
teams.get(0).getMembers().forEach(member -> em.remove(member));
반대로 Cascade 옵션을 REMOVE로 설정하고 테스트 해보자. REMOVE 옵션을 준 경우 별도로 Team에 연관된 Member들을 명시적으로 삭제하지 않더라도 자동으로 삭제가 가능하다.
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "team_id")
private Long id;
private String name;
@OneToMany(mappedBy = "team", cascade = CascadeType.REMOVE)
List<Member> members = new ArrayList<>();
...
}
OrphanRemoval
Cascade 옵션에 REMOVE를 넣지 않아도 연관된 객체를 삭제할 수 있는 방법중에 하나다. 이 옵션의 경우 Team을 삭제하게 되면 Member에 연관된 Team이 없어짐으로 Member는 주인 없는 고아객체가 되버린다. 이 옵션을 활성화 하면 고아객체가 된 객체들을 자동으로 삭제해준다. 기본값은 false로 되어있으며 true로 바꿔서 활성화 할 수 있다.
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "team_id")
private Long id;
private String name;
@OneToMany(mappedBy = "team", orphanRemoval = true)
List<Member> members = new ArrayList<>();
}
마찬가지로 정상적으로 삭제되는 것을 확인할 수 있다.
'spring > jpa' 카테고리의 다른 글
자바 ORM 표준 JPA 프로그래밍 - 3장 JPA 영속성 컨텍스트 (0) | 2021.07.05 |
---|---|
자바 ORM 표준 JPA 프로그래밍 - 2장 JPA시작 (0) | 2021.07.05 |
자바 ORM 표준 JPA 프로그래밍 - 1장 JPA 소개 (0) | 2021.07.05 |
자바 ORM 표준 JPA 프로그래밍 - 4장 엔티티 매핑 (0) | 2021.06.24 |
toMany 관계에서 필터링 걸기 (0) | 2021.05.13 |
댓글