Article article = new Article(content); // Transient/New
em.persist(article); // Managed(영속)
Managed(영속) 상태
는Persistence Context
가 관리할 수 있는 상태를 의미한다.Managed(영속)
된 엔티티들은flush
가 호출되면EntityManager
가 변경을 감지하여 자동으로update/insert
쿼리를 수행한다.
Article article = new Article(content); // Transient/New
em.persist(article); // Managed(영속)
Managed(영속) 상태
는 Persistence Context
가 관리할 수 있는 상태를 의미한다.Managed(영속)
된 엔티티들은 flush
가 호출되면 EntityManager
가 변경을 감지하여 자동으로 update/insert
쿼리를 수행한다.Managed(영속) 상태
의 엔티티가 Persistence Context
에서 detach
된 것을 준영속 상태
라 한다.
em.detach(article);
article.setContent(anotherContent);
Managed(영속) 상태
에서 detach
메소드를 호출하면 해당 엔티티는 준영속 상태
가 되어 더 이상 Persistence Context
에 의해 관리되지 않는다.준영속 상태
가 되는 경우는 다음과 같이 여러 상황이 존재한다.
Transient/New
엔티티의 id가 Persistence Context
에는 없고 DB
에는 존재할때 해당 엔티티는 준영속 상태
이다.Transient/New
엔티티의 id가 Persistence Context
에 있고 DB
에는 존재하지않을때 해당 엔티티는 준영속 상태
이다.EntityManager
로부터 find
메소드를 통해 엔티티를 찾으면 Managed(영속) 상태
가 되고 이후 detach
메소드를 호출하면 준영속 상태
가 된다.EntityManager
로부터 find
메소드를 통해 엔티티를 찾으면 Managed(영속) 상태
가 되고 이후 close
메소드를 호출하면 준영속 상태
가 된다.EntityManager
로부터 find
메소드를 통해 엔티티를 찾고 detach 또는 close 메소드를 호출하면 준영속 상태Article article = new Article(id, content);
em.merge(article);
준영속 상태
의 엔티티를 Persistence Context
또는 DB
에 업데이트Transient/New
상태의 엔티티를 merge
할때 Persistence Context
, DB
에 아무것도 존재하지 않는다면, Managed(영속)
화된 엔티티를 생성하여 반환한다.merge
메소드를 호출하면 member
엔티티의 id가 있는지 확인한다. (SELECT문 실행)Persistence Context
에 id가 있다면 4.
을 수행한다. 그렇지 않다면 DB
에 해당 id의 엔티티를 요청한다.Persistence Context
에 저장한다.merge
한 엔티티를 Persistence Context
에 반영한다.Managed(영속) 상태
의 엔티티를 반환한다. 이때 merge
로 전달된 엔티티는 준영속 상태
이다.em.remove(comment);
Persistence Context
에서 엔티티가 삭제되고 트랜잭션 종료시 DB
에서도 삭제된다.JpaRepository의 구현 클래스 중 SimpleJpaRepository의 save 메소드를 확인하면 다음과 같다.
@Transactional
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null.");
if (this.entityInformation.isNew(entity)) {
this.em.persist(entity);
return entity;
} else {
return this.em.merge(entity);
}
}
entityInformation의 isNew를 확인하여 해당 엔티티가 새로 만들어진 엔티티인지 아닌지를 판별한 후 persist 또는 merge를 수행하는 것을 확인할 수 있다. 새로 만들어진 엔티티의 경우 당연히 DB 또는 persistence context에 없기때문에 존재하는지 확인하지 않고 바로 persist 할 수 있는 것이다.
하지만 일반적으로 엔티티를 정의할때 isNew라는 메소드가 없기 때문에 save를 수행할때마다 merge를 하게 된다. 즉, 무조건 DB에 해당 엔티티가 존재하는지 확인한다. 그리고 이런 동작은 새로운 엔티티임에도 불필요한 SELECT문이 발생하면서 성능 이슈의 원인이 된다.
@Entity
public class Article extends AuditingFields implements Persistable<Long> {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
@Override
public boolean isNew() {
return this.createdAt == null;
}
}
위 예시 코드처럼 Persistable의 isNew를 Override하면 되는데 간단하게 createdAt(생성일자)가 null일 경우 새로운 엔티티(true)라고 판단하게 했다.
일반적으로 update를 수행하면 모든 필드에 대해 쿼리가 적용된다. 예를 들어 article(게시글)이라는 엔티티의 제목(title)만 update하더라도 그 외 내용(content)도 같이 update가 일어난다.
update article set title=?, content=? where id=?
필드가 많을 경우 이렇게 모든 필드가 update되면 부담이 될 수 있기때문에 @DynamicUpdate를 사용하여 간단하게 변경 부분만 update할 수 있도록 반영할 수 있다.
@Entity
@DynamicUpdate
public class Article extends AuditingFields implements Persistable<Long> {
...
}
save 뿐만 아니라 delete를 수행할 때도 SELECT문이 추가로 발생한다. 하지만 delete는 DB에 해당 엔티티가 존재해야 수행할 수 있기때문에 SELECT문으로 확인하는 것은 당연하다. (내부적으로도 수정이 어렵다.)
[reference]
https://hermeslog.tistory.com/693
Spring Security Filter의 순서와 동작 (0) | 2024.01.07 |
---|---|
JPA의 자동 키 생성 전략 (0) | 2024.01.05 |
AOP(Aspect Oriented Programming)의 개념과 적용 (1) | 2024.01.03 |
OneToOne 관계에서 자식 엔티티를 삭제했을 경우 발생하는 DataIntegrityViolationException (1) | 2024.01.03 |
Redis의 SpinLock을 사용한 동기화 문제 해결 (0) | 2023.12.28 |