[Spring Boot] Persistence Context, Entity Life Cycle

이맑음·2021년 10월 21일
0

Spring Boot

목록 보기
16/21
post-thumbnail

fastcampus 웹 개발 마스터 초격차 패키지를 수강하며 정리한 내용들입니다.

영속성(persistence)

  • 데이터베이스에 연결되어 사라지지 않고 지속적으로 접근할 수 있다.
  • EntityManager : persistence context의 가장 주체적인 역할을 하는 bean.
  • jpa를 통해 springboot가 persistence를 처리해주었기 때문에 특별한 설정없이 사용할 수 있었다.

intellij에서 mysql 사용법

  1. 터미널에서 mysql을 실행해주고, intellij에서 제공해주는 datagrip을 사용한다.
  2. mysql 설치 후 처음 사용한다면 test connection을 누른 후 드라이브를 추가 설치한다. (초기 id는 root, pw는 없다.)
  3. advanced 칸에서 serverTimezone-Asia/Seoul을 추가한다.
  4. ok를 누르면 나오는 console창에서 쿼리를 작성한다.
  5. 현재 사용하는 jpa에 persistence context를 연결하기 위해 application.yml에 아래 코드를 작성한다.
datasource:
	url: jdbc:mysql:localhost
    username: root
    password:
  1. dependencies에 runtimeOnly 'mysql:mysql-connector-java' 도 추가해준다.

@Transactional

  • 아래와 같이 디비에 2번 저장하는 코드를 작성 후 테스트를 실행할 때, 클래스에 @Transactional 어노테이션이 없다면 update 쿼리가 2번 실행된다. 이때, save 메서드의 구현체를 보면 자체에 @Transactional가 달려있는 것을 확인할 수 있다. -> 즉 상위에서 Transactional을 사용하지 않으면 자체적으로 Transactional을 실행하는 것을 알 수 있다.
 @Test
    void cacheFindTest2() {
        User user = userRepository.findById(1L).get();
        user.setName("malgummmm");
        userRepository.save(user);

        System.out.println("---------------------");

        user.setEmail("malgummmm@fastcampus.com");
        userRepository.save(user);
   
    }
  • 또한, save()마다 flush()를 사용하면 클래스에 @Transactional을 사용하더라도 update 쿼리가 2번 실행된다.
  • flush() : persistence cache에 쌓여 아직 디비에 반영되지 않은 변경들을 flush() 메서드가 실행되는 시점에 해당 변경들을 모두 디비에 반영해준다.
  • entity manager가 자체적으로 디비에 영속화 처리를 해주지만, 개발자가 원하는 타이밍에 디비에 영속화를 하려면 flush() 메서드를 사용하면 된다.

persistence context의 내용들이 디비에 반영되는 순간

  1. flush() 메서드가 실행 되었을 때
  2. @Transcational을 사용한 클래스의 경우 해당 메서드의 마지막 줄에서 auto flush()가 실행된다.
    • 테스트의 경우 마지막 줄에서 롤백을 실행한다. cache의 영향으로 update 쿼리 없이 변경된 사항을 확인할 수 있지만, 실질적으로 디비에 변경사항이 반영된 것은 아니다.
  3. jpql 쿼리가 실행 되었을 때
    • 위의 코드에서 마지막 줄에 sout(userRepository.findAll())(=select * from uesr)이 추가된다면, id가 1인 user의 변경된 이름, 이메일 + 기존의 user들의 정보를 merge하여 가져와야 한다. 그러나 jpa에서는 이렇게 복잡한 절차가 아닌 단순히 select 쿼리가 일어나기 전에 flush()를 진행하여 그저 최신 디비 내용을 가져온다.

Entity 생명 주기

New (비영속 상태)

  • 영속성 컨텍스트가 해당 객체(Entity)를 관리하고 있지 않은 상태이다.
  • @Transient(=해당 필드는 영속화에서 제외)를 사용했을 때와 동일한 상태이다.
  • 이때의 Entity는 Entity 보다는 단순히 하나의 java object로 취급한다.
@Service
public class UserService {

    @Transactional
    public void put() {
        User user = new User(); // new로 생성하여 비영속 상태이다.
        user.setName("newUser");
        user.setEmail("newUser@fastcampus.com");
   }
}

@SpringBootTest
class UserServiceTest {
    @Autowired
    private UserService userService;
    @Autowired
    private UserRepository userRepository;

    @Test
    void test() {
        userService.put();

        System.out.println(">>> " + userRepository.findByEmail("newUser@fastcampus.com"));
    }
}
  • test 결과 : select 쿼리가 실행되지만 비영속 상태의 Entity이므로 디비에 반영되지 않고, 단순히 자바 객체로 존재하다가 put메서드가 종료됨과 동시에 가비지 컬렉터에 의해 삭제되면서 null이 리턴된다.

Managed (영속 상태)

  • entity가 persistence context의 관리 하에 있는 상태이다.
  • 이 상태는 객체의 변화를 디비에 직접 반영해주지 않아도 자동으로 반영된다.
  • 방법 1) userRepository.save(user) 추가하기.
    위의 코드에서 userRepository.save(user);만 추가해줘도 영속화 상태가 된다. 아래와 같이 save 메서드의 구현체에 em이 EntityManager로 영속화 상태로 만들어주기 때문이다.
  • 방법 2) 직접 EntityManager를 사용하기.
    @Autowired
    private EntityManager entityManager;

    @Transactional
    public void put() {
    // 중복코드 생략
        entityManager.persist(user);
    }
  • test 결과 : uesr가 디비에 반영되어 sout 된 것을 확인할 수 있다. 후에 setter를 통해 entity의 내용을 변경해도 영속 상태로 동작한다. -> dirty checking에 의해서

Detached (준영속 상태)

  • 영속화 된 객체를 영속성 컨텍스트에서 분리한 상태이다.
  • 위에 코드에 이어 persist한 상태에서 detach를 사용하면 준영속 상태로 이름이 변경되지 않는다.
// 중복코드 생략
entityManager.detach(user);
user.setName("newUserAfterPersist");
  • clear()메서드와 close()메서드를 사용해도 detach()메서드와 비슷하게 동작을 한다. 이름처럼 detach보다 파괴적으로 실행되는데, clear()메서드의 경우 컨텍스트에 변경 예약이 된 부분들도 모두 drop 시켜버린다.
  • persist()메서드가 실행되면서 처음의 setName과 setEmail은 디비에 잘 적용 되었지만, 그 이후의 setName은 merge()를 사용하였음에도 clear()메서드에 의해 drop 되어 변경 사항이 디비에 적용되지 못했다.
@Transactional
public void put() {
    User user = new User();
    user.setName("newUser");
    user.setEmail("newUser@fastcampus.com");

    entityManager.persist(user);
    entityManager.detach(user);

    user.setName("newUserAfterPersist");
    entityManager.merge(user);

    entityManager.clear();
}
  • 준영속 상태여도 EntityManager의 merge()메서드를 사용하면 디비에 변경된 데이터가 반영이 된다.
  • jpa에서 제공해주는 save()메서드로 persist()메서드와 merge()메서드의 기능을 대신할 수 있다.

Removed (삭제 상태)

  • 말 그대로 객체를 삭제한 상태이다.
  • EntityManager의 remove()메서드를 통해 삭제를 실행할 수 있으며, 이미 detach된 객체에는 사용할 수 없다.
profile
하삐

0개의 댓글