ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • em.persist 이후 get$ObjsectId() 가 가능 한가?
    Java&Spring/Spring 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

     

Designed by Tistory.