본문 바로가기

Java/JPA

[JPA] Querydsl 벌크삭제 (2)

 

테이블의 데이터들이 다대다 관계로 구성되어 있고 
하나의 데이터를 삭제할 때 관련된 모든 테이블의 데이터를 삭제하기 위해 QueryDSL 공통로직 생성.

 


 

(이전방식)
서비스 로직에서 엔티티별 Q클래스 생성 & 쿼리작성

https://phyho.tistory.com/315

 

[JPA] Querydsl 벌크삭제

Querydsl 을 사용해서 벌크삭제를 해봤다. 아래와 같은 세개의 엔티티 클래스가 있다면, ' User ' , ' Order ', ' Post '=> 각 User는 여러 Order를 가질 수 있고, 또한 여러 Post를 가질 수 있음. User 엔티티 삭

phyho.tistory.com

 


 

CommonRepository 인터페이스 생성

import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.dsl.BooleanExpression;

public interface CommonRepository{
    long commonDeleteAll(EntityPath<?> entityPath, BooleanBuilder builder);
}

 

CommonRepositoryImpl 클래스 생성
 - 실제 구현 로직으로 CommonRepository 상속 

import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class CommonRepositoryImpl implements CommonRepository{

    private final JPAQueryFactory jpaQueryFactory;

    public long commonDeleteAll(EntityPath<?> entity, BooleanBuilder builder){
        return jpaQueryFactory.delete(entity).where(builder).execute();
    }

}

아래의 두개를 파라미터로 받아서 delete쿼리 실행.

 - EntityPath<?> entity : 엔티티 Q클래스.
 - BooleanBuilder bilder : delete쿼리의 where 조건.

 


 

CommonDeleteService 클래스 생성

import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.SimpleExpression;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Service
@Slf4j
@RequiredArgsConstructor
public class CommonDeleteService {

    @Autowired
    private final CommonRepository CommonRepository;

    @Transactional
    public long commonDelete(EntityPath<?> entity, Map<String, Object> conditions, BooleanExpression additional) {

        try {
            BooleanBuilder booleanBuilder = new BooleanBuilder();
            if (conditions != null && !conditions.isEmpty()) {
                for (Map.Entry<String, Object> condition : conditions.entrySet()) {
                    String key = condition.getKey();
                    String value = String.valueOf(condition.getValue());

                    // key와 일치하는 Q클래스 필드 가져오기
                    Field field = entity.getClass().getField(key);
                    SimpleExpression<Object> simpleExpression = (SimpleExpression<Object>) field.get(entity);
                    // value값 비교
                    BooleanExpression b = simpleExpression.eq(value);
                    booleanBuilder.and(b);
                }
            }

            if(additional != null){
                booleanBuilder.and(additional);
            }

            if (booleanBuilder.hasValue()) {
                return commonRepository.commonDeleteAll(entity, booleanBuilder);
            }

        } catch (NoSuchFieldException e) { // 일치하는 필드가 없을때
            log.error("Field not found in Q class: " + e.getMessage());
        } catch (IllegalArgumentException e) { // 조건이 없을때
            log.error("No conditions : " + e.getMessage());
        } catch (Exception e){
            log.error("delete error");
        }
        return 0;
    }

}

아래의 세개를 인자로 받아 최종적으로 CommonRepository 의 delete메소드 실행.

 - EntityPath<?> entity : 엔티티 Q클래스.
 - Map<String, Object> conditions : where절에서 eq조건으로 비교할 엔티티 필드값
 - BooleanExpression additional : where절에서 eq조건(conditions)에 and로 연결하여 추가로 비교할 조건값

1) Q클래스에서 conditions의 key값과 동일한 필드를 가져와
   필드의 value값과 conditions의 value가 동일한지 eq메소드로 비교.

2) eq조건이 아닌 다른 조건이 있는 경우 and연산자 결합. (선택조건)

3) 모든 조건이 BooleanExpression 타입으로 and로 연결되어 엔티티별 delete메소드 실행. 

 


 

서비스 로직에서 CommonDeleteService 의 공통삭제 메서드 활용.

 

private final CommonDeleteService commonDeleteService;

// 생략

Map<String, Object> conditions = new HashMap<>();
conditions.put("seq", seq);

eq조건으로 비교할 필드값(seq)을 conditions에 담기.
** 전달하는 Q클래스의 필드명과 conditions의 key값이 일치해야 한다.

 

1. eq 조건만 있는 경우.

QOrder order = QOrder.order;
commonDeleteService.commonDelete(order, conditions, null);

// DELETE FROM order WHERE seq = 'seq_value값';

where조건 : seq 필드값 일치여부(eq)

 

2. eq조건 없이 추가조건만 있는 경우.

QPost post = QPost.post;
BooleanExpression seqMatch = post.seq.in(seqList);
commonDeleteService.commonDelete(post, null, seqMatch);

// DELETE FROM post WHERE seq IN (값1, 값2, ..., 값N);

where조건 : seq 필드값 포함여부(in)

 

 

3. eq 조건 + 추가조건 있는 경우.

QUser user = QUser.user;
BooleanExpression userMatch = user.userCode.in(userCdList);
commonDeleteService.commonDelete(user, conditions, userMatch);

// DELETE FROM user WHERE seq = 'seq_value값' AND user_code IN (값1, 값2, ..., 값N);

where조건 : seq 필드값 일치여부(eq) and user 필드값 포함여부(in)

 

위처럼 Q클래스와 조건들을 파라미터로 전달해주면 delete쿼리가 실행된다.