8월 30, 2023

[Spring] Hibernate 객체 상태 구분 (transient, persistent, removed, detached)

Hibernate에서 객체는 네 가지 상태로 구분된다. 임시(Transient), 영속(Persistent), 제거(Removed), 분리(Detached) 상태로 구분되며 다양한 Session API 메서드를 통해 개별 객체들에 대해 상태 전환 효과를 설정할 수 있다. 

 

각각이 무엇을 의미하는지 정의와 자세한 예시를 통해 살펴보자.


1. 임시(Transient) 상태

객체 저장 이전 즉 객체를 처음 생성한 상태를 의미한다.

new 연산자를 통해 객체를 처음 생성할 경우 새로 만들어진 자바 객체일 뿐 데이터베이스와 매핑되는 객체가 아니기 때문에 우리는 임시 상태의 Transient 상태라고 부른다. 

 

임시상태는 Hibernate가 관리하지 않는 상태라고 부르기도 한다. 

Hibernate가 관리하는 영속(Persistent) 상태로 전환시키기 위해서는

Session.save()

를 사용한다. 

 

Transient 상태의 예시를 알아보도록 하자.

Person person=new Person();
person.setName("홍길동");
person.setEmail("jjamonglemon@gmail.com");

이렇게 name과 email entity를 가지고 있는 Person class가 있고 그것을 통해 Person 객체를 하나 생성했다면 DB와의 매핑이 전혀 존재하지 않는 Transient 상태라고 볼 수 있다.

 

이 상태에서 위에서 언급한 Session.save() 메서드를 통해 객체를 저장하거나 다른 Persistent 상태의 객체로부터 참조화되는 경우 Persistent 상태로 전환된다고 볼 수 있다. 

 

또한 Transient 상태의 객체는 ID 값이 존재하지 않는다. 


2. 영속(Persistent) 상태

다음으로는 위에서도 언급한 Persistent 상태에 대해 알아보자.

위에서 언급한 Session save() 이외에도 saveOrUpdate(), persist() 메서드를 사용하여 저장되었거나 hibernate 조회 메소드 get(), load() 를 사용하여 생성된 경우에도 우리는 객체가 Persistent 상태에 있다고 지칭한다. 

또한 이미 persistent 상태에 있는 객체에 의해 참조된 경우에도 객체는 Persistent 상태로 분류된다. 

 

위 Person 객체를 Persistent 상태로 분류되게 만들기 위해 코드를 몇 줄 추가해보도록 하겠다.

먼저 Session.save()를 사용하기 위해서는 SessionFactory를 import해주어야 하는데 아래와 같은 코드를 자바 코드 맨 윗줄에 추가해주면 된다.

import org.hibernate.SessionFactory;

전체 코드로 살펴보도록 하겠다.

import org.hibernate.SessionFactory;

@Inject private SessionFactory sessionFactory;

Person person=new Person();
person.setName("홍길동");
person.setEmail("jjamonglemon@gmail.com");

Session session = sessionFactory.getCurrentSession();

session.save(person);

이런식으로 SessionFactory에서 currentSession을 가져와 거기에 Transient 상태인 person을 save하면 Persistent 상태로 바뀌는 것이다. 

 

Persistent 상태의 객체는 트랜잭션이 종료될 때까지 데이터베이스와 동기화되고 변경사항을 감지한다.

 

Session.save() 이외에도 get등을 통해 Persistent 객체를 만드는 코드를 살펴보자.

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

@Service
@Transactional
public class AccountServiceImpl implements AccountService{
	@Inject private SessionFactory sessionFactory;
    
    public void createAccount(Account account){
      	getSession.save(account); //account 객체 저장 persistent 상태
    } 
    
    public Account getAccount(Long id){
    	return (Account) getSession.get(Account.class, id); // Session의 get을 통해 Account 객체를 return 받음. 이것도 Persistent 상태
    }
    private Session getSession() {
    	return sessionFactory.getCurrentSession(); // current session을 받을 수 있는 메서드
    }
}

 

여기서 @Transactional annotation의 의미는 모든 public 메서드들이 트랜잭션을 지원한다는 것을 의미하는 것이다. 

 


3. 제거(Removed) 상태

말 그대로 삭제된 상태이며, 객체를 Persistent 영속성 context와 데이터베이스에서 모두 삭제하는 것을 의미한다.


4. 분리(Detached)상태

트랜잭션이 종료되어 세션이 닫힌 경우 세션으로부터 분리되었다고 하여 Detached 상태라고 부른다.

Session의 close() 메서드를 호출하면 DB와의 매핑이 사라지고 동기화가 해제된다. 즉 Persistent 상태에서 detached 된 것이라고 볼 수 있다. 

 


이렇게 위와 같이 Hibernate 객체의 네 가지 상태를 알아보았다. 따라서 트랜잭션의 큰 흐름은 아래와 같다.

 

1. Transaction 시작

2. Hibernate 세션 생성 후 Transaction에 바인딩

3. 서비스 메서드를 실행 (DAO 호출 포함 등등)

4. Hibernate 세션을 데이터베이스로 송출

5. Transaction commit/rollback

6. 리소스 해제, 후처리 (ex) Hibernate session close)