Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

Entity 생애 주기 본문

JPA/영속성

Entity 생애 주기

Seung__ 2021. 7. 4. 00:10
 

bsh6463/BookManager

Contribute to bsh6463/BookManager development by creating an account on GitHub.

github.com

출처 : https://sugerent.tistory.com/587

1. 비영속상태, new, transient


영속성 컨텍스트가 해당 엔티티 객체를 관리하지 않는 상태

@Transient가 적용된 필드는 영속화에서 제외, 일종의 자바 object로 처리됨.

 - @Transient설명은 아래 글 참고

 

@Entity 속성 2

1. SQL이란? SQL 종류 들어가기에 앖서서 SQL(Structed Query Language, 구조적 질의언어)에 대해서 간략하게 알아보자. SQL은 관계형 DB의 관리시스템의 DATA를 관리하기 위한 만들어진 특수 목적의 프로그래

bsh-developer.tistory.com

 

@Service
public class UserService {

    @Transactional
    public void put(){
        User user = new User();
        user.setName("newUSer");
        user.setEmail("newUser@naver.com");

    }
}

user entity는 비영속(new)상태이다. 아직 영속성 컨텍스트에서 관리되지 않는 상태임.

 

해당 test를 실행 한 결과 select 쿼리만 실행되었고, email로 찾은 결과는 null이다.

 

entity로서 user data가 DB에 반영된 것 이 아니라, 단순히 자바 객체로 존재하여 transaction 종료 후 삭제되었기 때문.

Hibernate: 
    select
        user0_.id as id1_6_,
        user0_.created_at as created_2_6_,
        user0_.updated_at as updated_3_6_,
        user0_.email as email4_6_,
        user0_.gender as gender5_6_,
        user0_.name as name6_6_ 
    from
        user user0_ 
    where
        user0_.email=?
>>>>null

 

2. 영속상태, managed


해당 entity가 영속성 context관리 하에 존재하는 상태. 즉 객체의 변화를 별도로 처리해 주지 않더라고 db에 반영.

 

기존에 작성한 userRepository의 save를 활용해도 영속화가 가능하다.

save의 구현체를 보면 EntityManager를 사용하고 있기 때문. 여기서는 EntityManager를 직접 사용해보자.

 

- persist() 를 사용하여 영속화가 가능하다. 영속성 컨택스트가 해당 enityt를 관리하는 maanged상태

@Service
public class UserService {

    @Autowired
    private EntityManager entityManager;

    //@Autowired
    //private UserRepository userRepository;

    @Transactional
    public void put(){
        User user = new User();
        user.setName("newUSer");
        user.setEmail("newUser@naver.com");

        //userRepository.save(user);

        entityManager.persist(user);

    }
}

앞의 테스트와 같은 테스트를 돌려보자.

@SpringBootTest
class UserServiceTest {


    @Autowired
    private UserService userService;
    @Autowired
    private UserRepository userRepository;

    @Test
    void test(){

        userService.put();

        System.out.println(">>>>" + userRepository.findByEmail("newUser@naver.com"));
    }
}

1번 비영속상태(new, trasient)에서는 select만 진행이 되고, 검색 결과도 null 이었다.

영속화가 진행이 되지 않았기 때문에 DB에 반영이 되지 않았기 때문이다.

 

2번 영속상태(managed)는 enitty manager의 persist를 통해 entity를 영속화 시켜주었다.

따라서 db에 반영이 되도록 insert query가 실행되었고, 검색 결과도 정상적으로 출력되었따.

Hibernate: 
    insert 
    into
        user
        (created_at, updated_at, email, gender, name) 
    values
        (?, ?, ?, ?, ?)
Hibernate: 
    select
        user0_.id as id1_6_,
        user0_.created_at as created_2_6_,
        user0_.updated_at as updated_3_6_,
        user0_.email as email4_6_,
        user0_.gender as gender5_6_,
        user0_.name as name6_6_ 
    from
        user user0_ 
    where
        user0_.email=?

>>>>User(super=BaseEntity(createdAt=2021-07-03T23:05:12.119539, updatedAt=2021-07-03T23:05:12.119539), name=newUSer, email=newUser@naver.com, id=6, testData=null, gender=null)

 

그럼 persist 이후 data를 변경하면 어떻게 될까?

@Service
public class UserService {

    @Autowired
    private EntityManager entityManager;

    @Transactional
    public void put(){
        User user = new User();
        user.setName("newUSer");
        user.setEmail("newUser@naver.com");


        //비영속(new)상태

       entityManager.persist(user);
        //mnaged 상태

        user.setName("newUserAfterPersist");
    }
}

마지막 ser.Namer()는 DB에 반영되지 못하고 실행이 끝난 후 garbage collector에 의해 삭제될까?

Hibernate: 
    insert 
    into
        user
        (created_at, updated_at, email, gender, name) 
    values
        (?, ?, ?, ?, ?)

Hibernate: 
    update
        user 
    set
        created_at=?,
        updated_at=?,
        email=?,
        gender=?,
        name=? 
    where
        id=?
Hibernate: 
    select
        user0_.id as id1_6_,
        user0_.created_at as created_2_6_,
        user0_.updated_at as updated_3_6_,
        user0_.email as email4_6_,
        user0_.gender as gender5_6_,
        user0_.name as name6_6_ 
    from
        user user0_ 
    where
        user0_.email=?

>>>>User(super=BaseEntity(createdAt=2021-07-03T23:15:38.876605, updatedAt=2021-07-03T23:15:39.012960), 
name=newUserAfterPersist, email=newUser@naver.com, id=6, testData=null, gender=null)

insert -> update -> select 순으로 진행되었다.

 

save도 안했는데, update query가 실행되었고, 마지막 결과에 name이 변경되어있다.

 

즉, 영속성 컨텍스트 내에서 관리되는 entity는 setter를 통해 정보를 변경한 경우 transaction완료 시점에 

save method호출 없이 DB와 정합성을 맞춰준다.(update query추가됨)

 

한마디로  managed  상태인 entity를 setter로 data를 변경할 경우 transatction 완료 시점에

update query를 통해 DB에 반영해준다.

 

update query부분은 영속성 컨텍스트가 제공해주는 dirty check이다.

 

영속성 컨텍스트에서 가지고 있는 객체는 처음 context에 load시 정보를 복사(snap shot)해서 가지고 있다.

 

이후 변경내용을 DB에 적용해야 하는 시점에 복사본(snap shot)과 enitty를 비교하여

변경된 내용이 있으면 변경 내용을 DB에 반영해줌.

*대량의 entity를 다루는 경우 성능저하가 발생할 수 있음.

 

 

3. 준 영속상태, detached


영속화 되었던 객체를 분리하여 영속성 컨텍스트 밖으로 꺼내는 작업.

@Service
public class UserService {

    @Autowired
    private EntityManager entityManager;

    //@Autowired
    //private UserRepository userRepository;

    @Transactional
    public void put(){
        User user = new User();
        user.setName("newUSer");
        user.setEmail("newUser@naver.com");

        //userRepository.save(user);

        //비영속(new)상태태

       entityManager.persist(user);
        //mnaged 상태

        entityManager.detach(user);
        //detached

        user.setName("newUserAfterPersist");
    }
}

 

detach이후는 영속성 컨텍스트에서 관리하지 않는다.

 

Hibernate: 
    insert 
    into
        user
        (created_at, updated_at, email, gender, name) 
    values
        (?, ?, ?, ?, ?)

Hibernate: 
    select
        user0_.id as id1_6_,
        user0_.created_at as created_2_6_,
        user0_.updated_at as updated_3_6_,
        user0_.email as email4_6_,
        user0_.gender as gender5_6_,
        user0_.name as name6_6_ 
    from
        user user0_ 
    where
        user0_.email=?

>>>>User(super=BaseEntity(createdAt=2021-07-03T23:30:07.447712, updatedAt=2021-07-03T23:30:07.447712), name=newUSer, email=newUser@naver.com, id=6, testData=null, gender=null)

 

 

준 영속상태에 있는 entity는 merge를 통해 DB에 반영시킬 수 있다.

 

@Service
public class UserService {

    @Autowired
    private EntityManager entityManager;

    //@Autowired
    //private UserRepository userRepository;

    @Transactional
    public void put(){
        User user = new User();
        user.setName("newUSer");
        user.setEmail("newUser@naver.com");

        //userRepository.save(user);

        //비영속(new)상태태

       entityManager.persist(user);
        //mnaged 상태

        entityManager.detach(user);
        //detached

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

 

결과를 보면 이름이 수정되어 반영이 되었다.

 

Hibernate: 
    insert 
    into
        user
        (created_at, updated_at, email, gender, name) 
    values
        (?, ?, ?, ?, ?)
Hibernate: 
    select
        user0_.id as id1_6_0_,
        user0_.created_at as created_2_6_0_,
        user0_.updated_at as updated_3_6_0_,
        user0_.email as email4_6_0_,
        user0_.gender as gender5_6_0_,
        user0_.name as name6_6_0_ 
    from
        user user0_ 
    where
        user0_.id=?
Hibernate: 
    select
        reviews0_.user_id as user_id8_5_0_,
        reviews0_.id as id1_5_0_,
        reviews0_.id as id1_5_1_,
        reviews0_.created_at as created_2_5_1_,
        reviews0_.updated_at as updated_3_5_1_,
        reviews0_.book_id as book_id7_5_1_,
        reviews0_.content as content4_5_1_,
        reviews0_.score as score5_5_1_,
        reviews0_.title as title6_5_1_,
        reviews0_.user_id as user_id8_5_1_,
        book1_.id as id1_1_2_,
        book1_.created_at as created_2_1_2_,
        book1_.updated_at as updated_3_1_2_,
        book1_.author_id as author_i4_1_2_,
        book1_.category as category5_1_2_,
        book1_.name as name6_1_2_,
        book1_.publisher_id as publishe7_1_2_,
        publisher2_.id as id1_4_3_,
        publisher2_.created_at as created_2_4_3_,
        publisher2_.updated_at as updated_3_4_3_,
        publisher2_.name as name4_4_3_,
        bookreview3_.id as id1_3_4_,
        bookreview3_.created_at as created_2_3_4_,
        bookreview3_.updated_at as updated_3_3_4_,
        bookreview3_.average_review_score as average_4_3_4_,
        bookreview3_.book_id as book_id6_3_4_,
        bookreview3_.review_count as review_c5_3_4_ 
    from
        review reviews0_ 
    left outer join
        book book1_ 
            on reviews0_.book_id=book1_.id 
    left outer join
        publisher publisher2_ 
            on book1_.publisher_id=publisher2_.id 
    left outer join
        book_review_info bookreview3_ 
            on book1_.id=bookreview3_.book_id 
    where
        reviews0_.user_id=?

Hibernate: 
    update
        user 
    set
        created_at=?,
        updated_at=?,
        email=?,
        gender=?,
        name=? 
    where
        id=?

Hibernate: 
    select
        user0_.id as id1_6_,
        user0_.created_at as created_2_6_,
        user0_.updated_at as updated_3_6_,
        user0_.email as email4_6_,
        user0_.gender as gender5_6_,
        user0_.name as name6_6_ 
    from
        user user0_ 
    where
        user0_.email=?

        userhistor0_.user_id=?
>>>>User(super=BaseEntity(createdAt=2021-07-03T23:35:46.645375, updatedAt=2021-07-03T23:35:46.924029),
name=newUserAfterPersist, email=newUser@naver.com, id=6, testData=null, gender=null)

 

즉 persist나 merge는 JpaRepository에서 제공하는 save()로 모두 대응이 가능하다.

 

반영을 해야하는 시점에 명시적으로 save를 호출해 주자!

 

- clear() / close()는 영속성 컨텍스트에 있는 enitty를 모두 밖으로 꺼낼 수 있다.

detach보다 다소 무서움...

clear : 반영하려고 context에 변경 예약이 돼있건 건들 도 모~~두 싸아아아악 지워버린다. 주의하자.

아래는 clear 예시이다.

* detach -> merge -> detach

@Service
public class UserService {

    @Autowired
    private EntityManager entityManager;

    //@Autowired
    //private UserRepository userRepository;

    @Transactional
    public void put(){
        User user = new User();
        user.setName("newUSer");
        user.setEmail("newUser@naver.com");

        //userRepository.save(user);

        //비영속(new)상태태

       entityManager.persist(user);
        //mnaged 상태

        entityManager.detach(user);
        //detached

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

clear에 의해 merge로 예약이 되어있던 set.Name도 날라간다.

>>>>User(super=BaseEntity(createdAt=2021-07-03T23:42:13.557001, updatedAt=2021-07-03T23:42:13.557001),
name=newUSer, email=newUser@naver.com, id=6, testData=null, gender=null)

 * 의문점 : merge를 통해 왜 바로 DB에 반영이 안되었을까?
  merge() : Merge the state of the given entity into the current persistence context.              = 해당 entity를 managed상태로 바꿈
  merge는 entity의 상태를 manged로 바꾸는 것이지 DB에 반영시키는 것이 아니다. 
  manged상태인  entity는 transaction 종료 시점에 dirty check에 의해 DB와 정합성을 맞춘다. 이 경우에는 transaction이 종료되기 전에 clear()가 실행됐기 때문에 DB 반영 전 merge가 날라간 경우이다.

 

 마아아아안약에 clear를 통해 준영속상태로 변경을 하는 경우 clear전에 명시적으로DB에 모두 반영을 시켜주자.

 

*flush() -> clear()

@Service
public class UserService {

    @Autowired
    private EntityManager entityManager;

    //@Autowired
    //private UserRepository userRepository;

    @Transactional
    public void put(){
        User user = new User();
        user.setName("newUSer");
        user.setEmail("newUser@naver.com");

        //userRepository.save(user);

        //비영속(new)상태태

       entityManager.persist(user);
        //mnaged 상태

        entityManager.detach(user);
        //detached

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

        entityManager.flush();
        entityManager.clear();
    }
}

 

4. 삭제상태, remove

managed상태에서 동작해야함, remove를 통해 해당 entity는 더 이상 사용하지 못하는 상태가됨.(삭제됨)

@Service
public class UserService {

    @Autowired
    private EntityManager entityManager;

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void put(){
        User user = new User();
        user.setName("newUSer");
        user.setEmail("newUser@naver.com");

        //userRepository.save(user);

        //비영속(new)상태태

       entityManager.persist(user);
        //mnaged 상태

//        entityManager.detach(user);
        //detached

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

//        entityManager.flush();
//        entityManager.clear();


        User user1 = userRepository.findById(1L).get();

        entityManager.remove(user1);
    }
}

위와 같이 수정해주고 test를 돌려보자.

 

(user1의 경우 DB에서 entity를 가져왔다. DB에 존재하는 entity는 managed상태인가..? 맞다!)

 

중간에 delete query가 실행되고 최종적으로 id가1인 entity가 삭제되었다.

Hibernate: 
    insert 
    into
        user
        (created_at, updated_at, email, gender, name) 
    values
        (?, ?, ?, ?, ?)
Hibernate: 
    select
        user0_.id as id1_6_0_,
        user0_.created_at as created_2_6_0_,
        user0_.updated_at as updated_3_6_0_,
        user0_.email as email4_6_0_,
        user0_.gender as gender5_6_0_,
        user0_.name as name6_6_0_,
        userhistor1_.user_id as user_id6_7_1_,
        userhistor1_.id as id1_7_1_,
        userhistor1_.id as id1_7_2_,
        userhistor1_.created_at as created_2_7_2_,
        userhistor1_.updated_at as updated_3_7_2_,
        userhistor1_.email as email4_7_2_,
        userhistor1_.name as name5_7_2_,
        userhistor1_.user_id as user_id6_7_2_ 
    from
        user user0_ 
    left outer join
        user_history userhistor1_ 
            on user0_.id=userhistor1_.user_id 
    where
        user0_.id=?
Hibernate: 
    update
        user 
    set
        created_at=?,
        updated_at=?,
        email=?,
        gender=?,
        name=? 
    where
        id=?
Hibernate: 
    insert 
    into
        user_history
        (created_at, updated_at, email, name, user_id) 
    values
        (?, ?, ?, ?, ?)

Hibernate: 
    update
        review 
    set
        user_id=null 
    where
        user_id=?
Hibernate: 
    delete 
    from
        user 
    where
        id=?
Hibernate: 
    select
        user0_.id as id1_6_,
        user0_.created_at as created_2_6_,
        user0_.updated_at as updated_3_6_,
        user0_.email as email4_6_,
        user0_.gender as gender5_6_,
        user0_.name as name6_6_ 
    from
        user user0_

User(super=BaseEntity(createdAt=2021-07-03T23:53:51, updatedAt=2021-07-03T23:53:51), 
name=park, email=park@google.com, id=2, testData=null, gender=null)
User(super=BaseEntity(createdAt=2021-07-03T23:53:51, updatedAt=2021-07-03T23:53:51),
name=lee, email=lee@naver.com, id=3, testData=null, gender=null)
User(super=BaseEntity(createdAt=2021-07-03T23:53:51, updatedAt=2021-07-03T23:53:51), 
name=kim, email=kim@google.com, id=4, testData=null, gender=null)
User(super=BaseEntity(createdAt=2021-07-03T23:53:51, updatedAt=2021-07-03T23:53:51), 
name=hyun, email=hyun@google.com, id=5, testData=null, gender=null)
User(super=BaseEntity(createdAt=2021-07-03T23:53:53.869263, updatedAt=2021-07-03T23:53:54.065923), 
name=newUserAfterPersist, email=newUser@naver.com, id=6, testData=null, gender=null)

 

 

5. 정리


 

영속성 context가 관리하고 있는지, 관리하지 않는 상태인지로 구분이 가능

 

연속성 context 관리하에 엔티티는 변경감지(더티체크), 조회(1차 캐시), 쓰지지연 등 처리를 통해

개발자가 신경쓰지 않도록 편의성 제공.

 

킹치만 신경쓰지 않았던 부분에 대한 곳에서 의도치 않은 동작을 일으키기도 함.

 

'JPA > 영속성' 카테고리의 다른 글

Transaction Manager-3 : Isolation  (0) 2021.07.04
Transaction Manager - 2  (0) 2021.07.04
Transaction Manager - 1  (0) 2021.07.04
Entity Cache  (0) 2021.07.02
영속성 컨텍스트  (0) 2021.07.01
Comments