Java&Spring/Spring

em.persist 이후 get$ObjsectId() 가 가능 한가?

sung.hyun.1204 2023. 2. 4. 17:46

 

 

코드를 보자

 

@Entity
public class PrivateTodo {

    @Id
    @GeneratedValue
    @Column(name = "private_todo_id") //pk
    private Long id ;  
    ....

 

<< primitive type :  long 을 안쓰는 이유는 , Long 은 null 이 들어가지만 long 에는 null 이 불가능 하다.  >>

 

-> id 에 null 이 들어가는 경우를 열어둔 것은 이글의 제목의 답이 될 수 있다.

 

 

Entity Manager 를 통해서  PrivateTodo의 객체를 DB 에 저장하는 코드이다.

@Repository
@RequiredArgsConstructor
public class PrivateTodoRepository {

    @Autowired
    private final EntityManager em;

    public PrivateTodo save(Member member ,String task,Date date) {
        PrivateTodo privateTodo = new PrivateTodo();
        privateTodo.setMember(memeber);
        privateTodo.setTask(task);
        privateTodo.setDate(date);
        privateTodo.setComplete(false);
        privateTodo.setImageUrl("Default");

        em.persist(privateTodo);
		// privateTodo.getId();   ??  가능 ?
        
        return privateTodo;
    }

}

 

 

 

 

질문 :

em.persist(privateTodo); 이후 return privateTodo , 반환값에는 객체의 id 가 있을까 ? 

privateTodo.getId(); 가 바로 가능한가?

 

 

 

정답은 상황에 따라 있다 이다. 왜일까 ? 

 

privateTodo 의 객체 생성시 @GeneratedValue ,@id 에 의하여 id 값이 정해진다.

 

 

조금 더 자세하게 보자.

 

 

1. Entity manager 의 persist 를 보자 ,  Object entity 를 받는다.

public void persist(Object entity);

/**
 * Merge the state of the given entity into the
 * current persistence context.
 * @param entity  entity instance
 * @return the managed instance that the state was merged to
 * @throws IllegalArgumentException if instance is not an
 *         entity or is a removed entity
 * @throws TransactionRequiredException if there is no transaction when
 *         invoked on a container-managed entity manager of that is of type 
 *         <code>PersistenceContextType.TRANSACTION</code>
 */

 

persist를 사용할거면  object entity 가 필요하다라는 것을 알 수있다.

 

entity 는 언제만들까?

 

코드 레포지토리의 save 메서드를 보면  privateTodo 라는 객체를 만든다.

 

        PrivateTodo privateTodo = new PrivateTodo(); 

 

이는 비영속 상태 하단 그림의 (New) 이다.

->영속성 컨텍스트와 전혀 관계가 없는 상태이다.

 

EntityManger 즉   em.persist(privateTodo); 를 통해 우리는 영속성 컨텍스트에 저장을 하고 데이터 베이스에 객체를 저장한다.

 

 

 

 

 

 

 

 

 

 

 

즉 이글의 질문

"em.persist 이후 바로 get$ObjsectId() 가 가능 한가?" 는  당연 하게 "yes" 라는 대답을 하고 싶지만 실상은

 

"pk 생성 전략에 따라 달라진다" 가 맞다.

 

마지막으로 그럼 정확하게 언제 DB 테이블을 확인해서 새로운 객체의 id 값을 줄까? 라는 질문의 답은

@GenrationType 에 달려 있다.

 

//@GeneratedValue 은 GenerationType ,default 가 AUTO 이다. 


@GeneratedValue(strategy = GenerationType.AUTO)
@GeneratedValue(strategy = GenerationType.IDENTITY)
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@GeneratedValue(strategy = GenerationType.TABLE)

 

기본 설정부터 보자.

1 .AUTO (가급적이면 피하자)

@GeneratedValue 의 strategy 의 default value 이다. 

디비의 종류도 많고 데이터 베이스 마다 기본키 생성 전략도 다르다.

데이터베이스 마다 지원하는 서비스가 드른데 이를 데이터 베이스 방언(Dialect ) 라 부른다.

 

Auto 를 선택할시 사용하는 디비의 종류에 맞춰 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다.

 

tip)

- 스키마 자동 생성 기능(ddl-auto)을 사용한다면, 하이버네이트가 기본값을 사용해서 적절하게 만들어준다, 알고 있자.

- Hibernate 5부터 MySQL에서의 GenerationType.AUTO는 IDENTITY가 아닌 TABLE을 기본 시퀀스 전략으로 가져간다.

 

 

2. IDENTITY

데이터 베이스에 위임을 하는 전략이다.

 

 MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본 키를 자동으로 생성해준다.

= AUTO_INCREMENT  는 데이터 베이스에 INSERT SQL을 실행 한 이후에ID 값을 알 수 있다.

 

이게 무슨 말인가?? 

 

당연하 사실 두가지를 적는다.

- 엔티티가 영속 상태가 되기 위해서는 식별자가 필수이다.

- JPA 는 보통 transaction 을 Commit 하는 시점에 INSERTSQL 이 날라간다.

= em.persist() 를 한다고 바로 DB 에서 commit 이 되는 것이 아니다.

 

그러나 

 

"IDENTITY 전략에서는 em.persist()를 하는 즉시 INSERT SQL 을 실행하고 DB에서 식별자를 만든다."

 

- ID 값이 null 로 INSERT SQL 이 날라간다

- MYSQL 의  AUTO_INCREMENT 로  ID 가 자동 생성된다.

 - 1차 캐시에 저장해 두기 위해 DB 에서 반환을 받는다. 이때 넘긴 객체는 영속 상태가 된다.

 

즉 다음 과 같은 코드에서  getId 가 정상 적으로 나오며, SELECT SQL 이 날라가지 않는다.

-> JDBC driver 에 insert 하고 바로 리텀 받는게 내부적으로 존재 한다 한다.

public PrivateTodo save(String task,Date date) {
        PrivateTodo privateTodo = new PrivateTodo();
			...
        privateTodo.setTask(task);
        privateTodo.setDate(date);
			..        

        em.persist(privateTodo);
        /// insert 이후 
        System.out.println(privateTodo.getId());  // 정상적으로 출력 됨
        return privateTodo;
    }

 

3.SEQUENCE

데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트이다

mysql 에서는 사용하지 않는다.  오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용한다.

- > allocationSize로 한 번에 사용할 시퀀스 덩어리 사이즈를 정해서 최적화 할 수 있다.

 

 

4.TABLE

- 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략이다.

- 모든 데이터베이스에 적용 가능하나, 성능적인 손해가 있어서 잘 쓰지 않는다.

 

 

자 최종 정리이다.

- mysql 

- @GeneratedValue(strategy = GenerationType.IDENTITY)  를 이용하면

 

em.persist()메서들 사용하는 순간 ,  Entity 가 DB 에 저장이되고 이때 1차 캐시에 @Id 를 저장한다.

이후 commit() 를 통해 최종적으로 저장을 한다.

 

감상 : "영속성을 이해하자"

 

 

참고 :

https://www.inflearn.com/course/ORM-JPA-Basic/dashboard

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com