Spring boot + JPA로 개발하던 중 약 100건 ~ 200건의 데이터를 한 번에 insert 하는 로직을 개발했는데 속도가 너무 느려서 개선을 해보았습니다.
우선 지금 내가 저장하고자 하는 테이블의 PK는 복합키입니다. 두 개의 컬럼을 복합키로 가지고 있는데, 하나는 자동 증가하는 sequence 컬럼이고 다른 하나는 다른 테이블의 PK입니다. 즉 복합키 중 하나의 키는 외래키입니다.
그래서 insert를 하기 전에 entity에 해당 외래키를 set해두고 save를 해야합니다.
예를들어서 아래코드와 같습니다.
ProductEntity productEntity = new ProductEntity();
productEntity.setProductMasterEntity(productMasterEntity);
productRepository.save(productEntity);
예시코드입니다. 실제로는 entity에 new를 쓰지도 set을 쓰지도 않습니다...
여튼.
JPA고수라면 다들 알겠지만
JPA는 save()를 하기 전에 Entity의 식별자값인 Id에 값이 있으면 SELECT 쿼리가 먼저 나갑니다. 그리고 그 SELECT 쿼리의 결과를 바탕으로 값이 있으면 UPDATE 쿼리를, 값이 없으면 INSERT 쿼리를 진행하게 됩니다.
근데 저의 경우에는 위에 작성했다싶이
Id, 즉 pk가 복합키 입니다. 복합키중 한 컬럼은 외래키라 save이전에 지정을 해주기 때문에 값이 무조건 있는 상황입니다!! 그래서 제 의도는 insert이지만 자꾸만 save하기 전에 select가 나가서 속도가 느려졌던 것입니다.
이런경우... 여러가지 해결방법이 있을 수 있습니다만 저는 엔티티에 Persistable 인터페이스를 구현해 해결했습니다.
@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Table(name = "TB_PRODUCT")
@TableGenerator(
name = "generator",
table = "sequences",
pkColumnName = "seq",
initialValue = 1,
allocationSize = 100
)
@IdClass(ProductEntityId.class)
public class ProducEntity implements Persistable<ProductEntityId> {
@Id
@ManyToOne(fetch = FetchType.LAZY)
@OnDelete(action = OnDeleteAction.CASCADE)
@JoinColumn(name = "PROD_MST_CD", nullable = false)
@Comment("상품마스터")
private ProductMasterEntity productMaster;
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "generator")
@Column(name = "PROD_SEQ")
@Comment("상품시퀀스")
private Integer productSequence;
/**
* 컬럼 생략
*/
@Override
public ProductEntityId getId() {
return new ProductEntityId(this.productMaster.getProductMasterSequence(), this.productSequence);
}
@Override
public boolean isNew() {
return this.productSequence == null;
}
}
Persistable을 implements해서 getId(), isNew() 메서드를 구현하면 됩니다!!
getId()는 제 entity의 Id를 return 하도록 했고 isNew() 메서드는 자동 증가되는 시퀀스 컬럼값만 바라보게 설정 해주었습니다.
이렇게 설정하고 나니 제 의도대로 바로 insert 쿼리가 나가면서 속도가 많이 빨라졌습니다!
그럼 언젠가 JPA의 고수가 되는 그날까지!! 안녕~!!!