Web/JPA 2019. 4. 29. 14:52

 

오늘 포스팅할 내용은 간단히 JPA의 cascade 기능이다. 이전 포스팅 중에 해당 내용에 대해 포스팅한적이 있지만 조금 부족한 것같아서 다시 한번 정리할겸 글을 남긴다.

 

영속성 전이(cascade)란 쉽게 말해 부모 엔티티가 영속화될때, 자식 엔티티도 같이 영속화되고 부모 엔티티가 삭제 될때, 자식 엔티티도 삭제되는 등 부모의 영속성 상태가 전이되는 것을 이야기한다. 영속성전이의 종류로는 ALL, PERSIST, DETACH, REFRESH, MERGE, REMOVE등이 있다. 이름만 봐도 어디까지 영속성이 전이되는지 확 눈에 보일 것이다. 여기서는 별도로 각각을 설명하지는 않는다.

 

오늘의 상황 : A와 B라는 엔티티가 존재하고, 두 엔티티의 관계는 @ManyToMany 관계이다. 이 관계는 중간에 Bridge 테이블을 두어 @OneToMany<->@ManyToOne  @OneToMany<->@ManyToOne 관계로 매핑하였다. 그리고 C라는 엔티티는 A엔티티와 @ManyToOne 관계이다. 이렇게 여러개가 조인이 걸려있는 상황에서 cascade를 이용한 PERSIST 예제를 한번 짜보았다.

 

<AEntity Class>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Entity
@Table(name = "TB_A")
@Getter
@Setter
@ToString
public class AEntity {
    
    @Id
    @Column(name = "A_ID")
    @GeneratedValue(strategy=GenerationType.TABLE, generator = "SEQ_GENERATOR")
    @TableGenerator(
            name="SEQ_GENERATOR",
            table="MY_SEQUENCE",
            pkColumnName="SEQ_NAME"//MY_SEQUENCE 테이블에 생성할 필드이름(시퀀스네임)
            pkColumnValue="A_SEQ"//SEQ_NAME이라고 지은 칼럼명에 들어가는 값.(키로 사용할 값)
            allocationSize=1
    )
    private Long id;
    
    private String name;
    
    @OneToMany(mappedBy="a",cascade=CascadeType.ALL)
    private List<CEntity> cList = new ArrayList<>();
    
    
    @OneToMany(mappedBy = "aEntity",fetch=FetchType.EAGER,cascade=CascadeType.ALL)
    private List<BridgeEntity> bridges;
}
 
cs

 

<BEntity class>

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Entity
@Table(name = "TB_B")
@Getter
@Setter
@ToString
public class BEntity {
    @Id
    @Column(name = "B_ID")
    @GeneratedValue(strategy=GenerationType.TABLE, generator = "SEQ_GENERATOR")
    @TableGenerator(
            name="SEQ_GENERATOR",
            table="MY_SEQUENCE",
            pkColumnName="SEQ_NAME"//MY_SEQUENCE 테이블에 생성할 필드이름(시퀀스네임)
            pkColumnValue="B_SEQ"//SEQ_NAME이라고 지은 칼럼명에 들어가는 값.(키로 사용할 값)
            allocationSize=1
    )
    private Long id;
    
    private String name;
    
    @OneToMany(mappedBy = "bEntity")
    private List<BridgeEntity> bridges;
    
}
 
cs

 

<CEntity class>

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Entity
@Table(name = "TB_C")
@Getter
@Setter
@ToString
public class CEntity {
    
    @Id
    @Column(name = "C_ID")
    @GeneratedValue(strategy=GenerationType.TABLE, generator = "SEQ_GENERATOR")
    @TableGenerator(
            name="SEQ_GENERATOR",
            table="MY_SEQUENCE",
            pkColumnName="SEQ_NAME"//MY_SEQUENCE 테이블에 생성할 필드이름(시퀀스네임)
            pkColumnValue="C_SEQ"//SEQ_NAME이라고 지은 칼럼명에 들어가는 값.(키로 사용할 값)
            allocationSize=1
    )
    private Long id;
    
    private String name;
    
    @ManyToOne
    @JoinColumn(name="A_ID", nullable=false)
    private AEntity a;
}
 
cs

 

<BridgeEntity class>

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Entity
@Table(name = "TB_BRIDGE")
@Getter
@Setter
@ToString
public class BridgeEntity {
    
    @Id
    @Column(name = "BRIDGE_ID")
    @GeneratedValue(strategy=GenerationType.TABLE, generator = "SEQ_GENERATOR")
    @TableGenerator(
            name="SEQ_GENERATOR",
            table="MY_SEQUENCE",
            pkColumnName="SEQ_NAME"//MY_SEQUENCE 테이블에 생성할 필드이름(시퀀스네임)
            pkColumnValue="BRIDGE_SEQ"//SEQ_NAME이라고 지은 칼럼명에 들어가는 값.(키로 사용할 값)
            allocationSize=1
    )
    private Long id;
    
    @ManyToOne
    @JoinColumn(name = "A_ID")
    private AEntity aEntity;
    
    @ManyToOne
    @JoinColumn(name = "B_ID")
    private BEntity bEntity;
}
 
cs

 

<A,B Entity Repository class>

1
2
3
4
5
6
7
public interface AEntityRepository extends JpaRepository<AEntity, Long>{
 
}
 
public interface BEntityRepository extends JpaRepository<BEntity, Long>{
 
}
cs

 

테스트 상황 : A,B,C,BridgeEntity 모두 데이터를 persist 하는 상황이다. 그럼 여기서 생각해야 할것이 있다. 우선 C의 부모는 A이다. 즉, A 엔티티가 persist될때 C 자식 엔티티까지 persist되도록 영속성 전이기능을 이용하면 된다. 그렇다면 A,B,Bridge 세개의 관계는 어떻게 될까? 우선 조건이 있다. A,B,Bridge 관계에서 Bridge엔티티가 외래키의 주인이다. 이 말은 즉슨, Bridge 엔티티가 persist 되기 전에 A,B모두가 영속화된 상태여야하는 것이다. 예제는 아래와 같은 시나리오로 테스트했다.

 

1)B 엔티티 영속화 ->2)A 엔티티의 영속화 + Bridge 엔티티 영속성전이

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringDataJpaTestApplicationTests {
    
    
    @Autowired AEntityRepository aRepository;
    @Autowired BEntityRepository bRepository;
    
    @Test
    public void contextLoads() {
        
        AEntity a = new AEntity();
        a.setName("A");
        
        BEntity b = new BEntity();
        b.setName("B");
        
        IntStream.rangeClosed(09).forEach((i)->{
            CEntity c = new CEntity();
            c.setName(i+"");
            a.getCList().add(c);
        });
        
        a.getCList().stream().forEach((c)->{
            c.setA(a);
        });
        
        
        BridgeEntity bridge = new BridgeEntity();
        bridge.setBEntity(bRepository.save(b));
        bridge.setAEntity(a);
 
        a.setBridges(Arrays.asList(bridge));
        
        aRepository.save(a);
    }
 
}
cs

중요한 것이 있다. 부모 엔티티가 영속화되는 동시에 자식 엔티티가 영속화되게 영속성 전이를 이용하기 위해서는 반드시 자식 객체에 부모객체를 set해주어야 하는 점이다. 만약 부모엔티티를 자식 엔티티에 set해주지 않으면 외래키에 null값이 채워질 것이다.

 

결과

=>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
A_ID NAME
25     A
 
B_ID NAME
21     B
 
C_ID NAME A_ID
63     c-0  25
64     c-1  25
65     c-2  25
66     c-3  25
67     c-4  25
68     c-5  25
69     c-6  25
70     c-7  25
71     c-8  25
72     c-9  25
 
BRIDGE_ID A_ID B_ID
21          25   21
cs
posted by 여성게
: