헤르메스 LIFE

[JPA] update (Persistence Context) 본문

Spring Boot Framework

[JPA] update (Persistence Context)

헤르메스의날개 2023. 3. 18. 01:26
728x90

요즘 JPA를 공부하고 있습니다. MyBatis 만 하다가, 개인적으로 공부를 진행하고 있습니다. 하다보니 막히고, 이해가 안되는 부분이 많네요. 그때마다 검색과 삽질로 해결하고 있습니다. 여기 그 삽질의 자취를 남겨봅니다.


개발환경

Spring Boot 2.7.9

H2 2.1.214

p6spy 1.8.1

slf4j 1.7.36

swagger2 2.6.1

lombok

devtools

postgresql


JPA를 공부하면서 일반적인 RDB의 관점에서 이해가 안되는 점이 있었습니다.

첫째. Save 시 Select 후 Insert 하는 부분

https://hermeslog.tistory.com/693

 

[JPA] JPA Data Save 시 Select 쿼리가 먼저 실행 된다.

요즘 JPA를 공부하고 있습니다. MyBatis 만 하다가, 개인적으로 공부를 진행하고 있습니다. 하다보니 막히고, 이해가 안되는 부분이 많네요. 그때마다 검색과 삽질로 해결하고 있습니다. 여기 그 삽

hermeslog.tistory.com

둘째. Update 시 JpaRepository를 상속받아 @Query 어노테이션을 이용한 JPQL을 사용해야한다는 부분입니다. ( 인터넷을 검색 해보면 예제들이... )

JPA를 사용하면 JPA Repository Method 명명규칙이 있는데, 유독 update만 JPQL의 NativeQuery 처럼 Query를 사용해야하는게 이해가 되지 않았습니다. 

https://docs.spring.io/spring-data/jpa/docs/2.4.3/reference/html/#jpa.query-methods.query-creation

 

Spring Data JPA - Reference Documentation

Example 109. Using @Transactional at query methods @Transactional(readOnly = true) public interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") v

docs.spring.io


1. update Ver.1

package octopus.backend.v1.dao;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import octopus.entity.TCodeM;

// @Repository : JpaRepository를 사용하면 @Repository를 사용하지 않아도 됨.
public interface CodeMRepository extends JpaRepository<TCodeM, String> {
    Optional<TCodeM> findBypCd(String pCd);
    
    int deleteBypCd(String pCd);
    
    // clearAutomatically : Persistence Context
    @Modifying(clearAutomatically = true)
    @Query("update TCodeM m set m.pCdNm = :pCdNm where m.pCd = :pCd")
    int updateTCodeM(@Param(value = "pCd") String pCd, @Param(value = "pCdNm") String pCdNm);
}

CodeMService.java

package octopus.backend.v1.service;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import octopus.backend.v1.dao.CodeMRepository;
import octopus.backend.v1.dto.TCodeMDto;
import octopus.entity.TCodeM;

@Service
@AllArgsConstructor
@Slf4j
public class CodeMService {
    private CodeMRepository codeMRepository;
    
    @Transactional(readOnly = true)
    public TCodeMDto findByCd(String cd) {
        Optional<TCodeM> tCodeM = codeMRepository.findBypCd(cd);
        return new TCodeMDto(tCodeM.get());
    }
    
    @Transactional(readOnly = true)
    public List<TCodeMDto> findAllCd() {
        return codeMRepository.findAll().stream().map(TCodeMDto::new).collect(Collectors.toList());
    }
    
    @Transactional
    public TCodeM save(TCodeMDto tCodeMDto) {
        
        log.debug("tCodeMDto :: {}", tCodeMDto);
        
        TCodeM tCodeM = tCodeMDto.toEntity();
        
        log.debug("tCodeM :: {}", tCodeM);
        
        return codeMRepository.save(tCodeM);
    }
    
    @Transactional
    public int update(TCodeMDto tCodeMDto) {
        
        log.debug("tCodeMDto :: {}", tCodeMDto);
        
        TCodeM tCodeM = tCodeMDto.toEntity();
        
        log.debug("tCodeM :: {}", tCodeM);
        
        return codeMRepository.updateTCodeM(tCodeM.getPCd(), tCodeM.getPCdNm());
    }

    @Transactional
    public void delete(TCodeMDto tCodeMDto) {
        Optional<TCodeM> tCodeM = codeMRepository.findBypCd(tCodeMDto.getPCd());
        
        log.debug("TCodeM :: {}", tCodeM.get());
        
        tCodeM.get().updateUseYn(tCodeMDto);
    }
}

Service에 update Method를 추가를 추가하고, 직접 테스트 했습니다. 물론 Update 가 잘됩니다. ^^

    @Transactional
    public int update(TCodeMDto tCodeMDto) {
        
        log.debug("tCodeMDto :: {}", tCodeMDto);
        
        TCodeM tCodeM = tCodeMDto.toEntity();
        
        log.debug("tCodeM :: {}", tCodeM);
        
        return codeMRepository.updateTCodeM(tCodeM.getPCd(), tCodeM.getPCdNm());
    }

1. update Ver.2

package octopus.backend.v1.service;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import octopus.backend.v1.dao.CodeMRepository;
import octopus.backend.v1.dto.TCodeMDto;
import octopus.entity.TCodeM;

@Service
@AllArgsConstructor
@Slf4j
public class CodeMService {
    private CodeMRepository codeMRepository;
    
    @Transactional(readOnly = true)
    public TCodeMDto findByCd(String cd) {
        Optional<TCodeM> tCodeM = codeMRepository.findBypCd(cd);
        return new TCodeMDto(tCodeM.get());
    }
    
    @Transactional(readOnly = true)
    public List<TCodeMDto> findAllCd() {
        return codeMRepository.findAll().stream().map(TCodeMDto::new).collect(Collectors.toList());
    }
    
    @Transactional
    public TCodeM save(TCodeMDto tCodeMDto) {
        
        log.debug("tCodeMDto :: {}", tCodeMDto);
        
        TCodeM tCodeM = tCodeMDto.toEntity();
        
        log.debug("tCodeM :: {}", tCodeM);
        
        return codeMRepository.save(tCodeM);
    }
    
    @Transactional
    public int update(TCodeMDto tCodeMDto) {
        
        log.debug("tCodeMDto :: {}", tCodeMDto);
        
        TCodeM tCodeM = tCodeMDto.toEntity();
        
        log.debug("tCodeM :: {}", tCodeM);
        
        return codeMRepository.updateTCodeM(tCodeM.getPCd(), tCodeM.getPCdNm());
    }
    
    @Transactional
    public void update02(TCodeMDto tCodeMDto) {
        Optional<TCodeM> tCodeM = codeMRepository.findBypCd(tCodeMDto.getPCd());
        
        log.debug("TCodeM :: {}", tCodeM.get());
        
        tCodeM.get().updateCodeM(tCodeMDto);
    }
    
    @Transactional
    public void delete(TCodeMDto tCodeMDto) {
        Optional<TCodeM> tCodeM = codeMRepository.findBypCd(tCodeMDto.getPCd());
        
        log.debug("TCodeM :: {}", tCodeM.get());
        
        tCodeM.get().updateUseYn(tCodeMDto);
    }
}

update02 Method가 추가되었습니다.

    @Transactional
    public void update02(TCodeMDto tCodeMDto) {
        Optional<TCodeM> tCodeM = codeMRepository.findBypCd(tCodeMDto.getPCd());
        
        log.debug("TCodeM :: {}", tCodeM.get());
        
        tCodeM.get().updateCodeM(tCodeMDto);
    }

조회 후 조회된 결과를 수정하면 영속성 컨텍스트(Persistence Context)가 변경되면서 update 가 실행되는 로직입니다.


또 하나의 의문...

대량 데이터의 수정 시 조회 후 update 를 수행해야 하는데, 이 경우는 JPQL을 이용해야 하는가 입니다.

Select 후 Update는 속도가 느려질 수 있습니다.

조금 더 고민해봐야할 것 같습니다.

728x90