JPA 영속성과 엔티티

JPA 영속성과 엔티티

JPA 에서의 영속성을 보존하는 방법 그리고 엔티티에 대해서 알아봅시다

영속성이란?

영속성


컴퓨터 공학에서 지속성(Persistence)은 프로세스가 생성했지만 별개로 유지되는 상태의 특징 중 한 가지이며, 별도의 기억 장치에 데이터를 보존하는 것을 목적으로 한다. 이 특징으로 인해 프로그래머는 저장 장치로부터 데이터를 전송하는 작업 및 자료 구조 등을 이용해 데이터를 보존하는 것이 가능하다.

이 특징 없이는 상태는 오직 RAM에만 존재할 수 있고, 컴퓨터가 종료되어 RAM이 전력을 잃어버린다면 상태도 같이 사라져 버리게 된다.

위키백과 지속성

JPA를 공부하다보면, 아니 다른 프레임워크를 공부하거나 단순히 컴퓨터 공학 쪽을 공부를 하다 보면 영속성이란 단어를 접하게 됩니다.

저는 영속성이라는 속성을 다음과 같이 이해했습니다..!!

영속성이란 특정 데이터가 DB에 넣는 식으로 메모리가 아닌 디스크에 저장해 데이터를 저장될 때 얻는 속성!!

JPA = Java Persistence Api


JPA는 그 이름에서부터 알 수 있듯이, 자바에서 영속성 관리할 수 있도록 도와주는 API 입니다! 그러면 JPA는 어떻게 객체의 영속성을 관리하는 걸까요??

그러기 위해서는 먼저 엔티티에 대해서 알아봐야 합니다!

엔티티 (Entity)


Entities in JPA are nothing but POJOs representing data that can be persisted in the database. An entity represents a table stored in a database. Every instance of an entity represents a row in the table.

Baeldung Entity

JPA에서의 엔티티는 데이터베이스에 영속되는 데이터를 표현하는 POJO(Plain Old Java Object) 입니다!! 즉 저장하고 싶은 객체를 RDB에 테이블 형태로 저장하고자 할 때, 해당 자바 객체를 Entity라고 하는 것이죠.

그렇다면 엔티티를 등록하기 위해서는 어떻게 해야할까요?? 바로 @Entity어노테이션을 통해서 가능합니다!!

@Entity  
@Table(name = "penguins")  
@NoArgsConstructor  
public class Penguin {  
  
 @Id @GeneratedValue(strategy = GenerationType.IDENTITY)  
 private Long id;  
 private String name;  
 private int age;  
 private float length;  
 private float weight;  
 private Species species;  
  
 @Builder  
 public Penguin(Long id, String name, int age, float length, float weight, Species species) {  
  this.id = id;  
  this.name = name;  
  this.age = age;  
  this.length = length;  
  this.weight = weight;  
  this.species = species;  
 }  
}

위와 같이 영속화를 하고자 하는 클래스에 @Entity 를 추가해 주는 것 만으로도 JPA의 도움을 받을 수 있죠

@Table


@Table 어노테이션은 해당 객체를 영속화 시키고자 하는 DB의 테이블을 입력하면 됩니다. 대부분의 경우에는 POJO 클래스의 이름과 RDB의 테이블은 일치하지 않으니까요

물론 ddl-auto 설정을 create로 설정할 경우에는 알아서 DB에 테이블을 생성해주지만…. 해당 옵션은 연습이나 개발 단계에서만 사용하기 때문에 문제가 생길 여지가 다분히 존재하죠!!

그렇기 때문에 원하는 테이블에 영속화 시키기 위해서는 해당 어노테이션을 사용주는 것이 좋습니다!!

Entity Manager


JPA에서 제공하는 기능은 크게 엔티티-테이블 매핑을 하는 설계부분, 매핑한 엔티티를 실제로 사용하는 두 부분으로 나누어져있다고 볼 수 있습니다.

그 중에서 오늘 궁금한 부분은 바로 엔티티를 실제로 사용하는 부분입니다!! 엔티티를 저장하고, 수정하고, 삭제하는게 개발자가 해야 하는 일이니까요!!

그런데 실제로 이런 일을 수행하는 것은 누구일까요?? 바로 엔티티 매니저(Entity Manager)입니다!!

말 그대로 엔티티의 관리자로서 엔티티의 CRUD 및 영속화를 하는데 도움을 주죠 개발자는 엔티티를 저장하는 가상의 DB로 생각해도 됩니다.

EntityManagerFactory


EntityManagerFactoryEntityManger 를 만들어주는 공장입니다! 보통 하나의 데이터 베이스를 사용할 경우 EntityMangerFactory는 역시 하나만 생성합니다.

공장답게 해당 객체는 생성하는데 비용이 많이 들기 때문입니다! 그래서 하나만 생성하고, 어플리케이션 내에서 공유를 하게 됩니다. 대신 EntityManagerFactoryThread Safe하다는 특성을 가지고 있습니다!! 생성 비용이 비싼 값을 하는군요!

  • EntityManger를 생성해 주는 공장
  • 보통 DB를 하나만 사용할 경우 하나만 생성함
    • 생성 비용이 비쌈
  • Thread-Safe하다 (여러 스레드가 동시에 접근해도 안전하다)

EntityManager


EntityManager는 위의 EntityManagerFactory로 부터 생성된 객체입니다. EntityManagerFactory 와는 다르게 생성하는데 비용이 거의 들지 않죠!!

그리고 Entity 의 CRUD를 실질적으로 수행하는 역할을 담당하죠!! 실무자와 같은 포지션이네요!!

하지만 EntityManagerFactory 와는 다르게 멀티스레드 환경에 대해서 안전하지 않기 때문세 절대 스레드간에 공유를 하면 안됩니다!!!

그리고 EntityManager들은 생성 되었더라도 DB 연결이 필요하지 않으면, 커넥션을 획득하지 않습니다!! 트랜잭션이 시작되거나 할 때와 같이 DB와의 연결이 필요지면 그때서야 커넥션 풀에서 커넥셔을 하나 가져와서 연결을 합니다!

  • Entity CRUD를 담당하는 실무자 포지션
  • EntityManagerFactory 로 생성 가능
    • 생성하는데 비용이 거의 들지 않음
  • Not Thread-Safe하다 (여러 스레드가 동시에 접근하면 위험하다!! 동시성 이슈!)
  • DB와의 연결이 필요하지 않으면 커넥션을 획득하지 않음

밑의 코드와 같이 생성합니다!!

참고로 해당 코드들은 스프링 프로젝트의 test 디렉토리 아래에서 테스트 환경 아래에서 작성되었습니다!

@SpringBootTest  
class EntityApplicationTests {  
  
 @Autowired  
 EntityManagerFactory emf;  
  
 @Test  
 void contextLoads() {  
  EntityManager em = emf.createEntityManager();  
 }  
}

그림으로 한 번 확인해보겠습니다!!

아마 인터넷에서 많이 보셨을 그림입니다ㅎㅎ 저는 김영한 님의 자바 ORM 표준 JPA 프로그래밍를 참고하면서 글을 작성하다보니 자연스럽게 해당 그림을 자료로 사용하게 되었는데 이해하는데 도움이 될 것같아서 가져왔습니다!!

위 그림을 보시면 지금까지의 나온 특성들이 잘 표현되어 있습니다.

  • EntityManagerFactory
    • 연결된 DB가 하나라서 1개만 생성됨
    • 요청에 들어오면 EntityManager를 생성함
  • EntityManager
    • 여러 개가 존재해도 무방함
    • DB연결이 필요 없는 EntityManager는 커넥션을 획득하지 않음

영속성 컨텍스트 (Persistence Context)


JPA를 이해하는데 많은 사람들이 가장 강조하는 매우 중요한 요소입니다!!

간단히 말하자면, Entity를 영구히 저장하는 환경이란 뜻!!

EntityManager는 영속성 컨텍스트에 Entity를 저장합니다!!

Entity 생명주기


역시 유명한 그림을 먼저 보겠습니다!!

Entity 생명주기

Entity는 총 4가지 생명 주기가 존재합니다.

  • 비영속 (new/transient) : 영속성 컨텍스트와 전혀 관련이 없음
  • 영속 (managed) : 영속성 컨텍스트에 저장된 상태
  • 준영속 (detached) : 영속 되었었지만, 현재는 분리된 상태
  • 삭제 (removed) : 삭제된 상태

각 상태에 대해서 자세히 알아보죠!!

저는 이 개념을 처음 이해하는데 매우 어려웠습니다.. 그래서 저는 비유를 통해서 이해를 했는데요 그렇기 때문에 각 생명 주기에 대해서 제 나름대로 비유가 들어갈 예정입니다!!

해당 비유는 제 나름대로의 비유이므로, 오히려 헷갈릴 수 있기 때문에 참고만 하거나, 아예 이해가 안된다면 무시해주시면 감사하겠습니다!!

각 비유 대상은 다음과 같습니다

penguin영속성 컨텍스트EntityManager
유치원생유치원아이 부모

비영속 / New


아직은 영속성 컨텍스트와 아무런 관련이 없는 그저 POJO 상태일 때의 상태입니다.

Penguin penguin = Penguin.builder()  
  .id(1L)  
  .age(3)  
  .length(140)  
  .name("핑구")  
  .species(Species.EMPEROR_PENGUINS)  
  .weight(30)  
  .build();

위와 같이 Penguin 객체를 하나 생성했습니다!! 하지만 위 객체는 아직 영속성 컨텍스트, JPA와는 아무런 관련이 아직 없습니다!!

그냥 평소에 하듯이 객체를 생성 했을 뿐이죠!!

누구세요??

아직은 서로에 대해 모른다

비유


아이는 얼마 전 이 동네로 이사왔고 아직 유치원에 다니지 않는다

즉, 아이와 유치원은 서로의 존재에 대해 모른다

영속 / Managed


이제 엔티티 매니저를 통해 해당 객체를 영속화 해보겠습니다!!

@Test  
void contextLoads() {  
 EntityManager em = emf.createEntityManager();  
  
 Penguin penguin = Penguin.builder()  
   .id(1L)  
   .age(3)  
   .length(140)  
   .name("핑구")  
   .species(Species.EMPEROR_PENGUINS)  
   .weight(30)  
   .build();  
  
 em.persist(penguin);  
}

영속화 빔!!! !!

안뇽!!

자! 이제 EntityManager 는 penguin을 영속화 시켰고, 영속성 컨텍스트안에서 관리하게 되었습니다!!

이렇게 POJO 객체를 persist() 시키는 방식 외에도

  • em.find() 를 통해 가져온 객체
  • JPQL을 통해 가져온 객체 들 또한 영속 상태로 가져오게 됩니다!!!

JPA가 가져왔으니 처음부터 영속성 컨텍스트 안에서 관리하겠다…. 저는 이런 식으로 이해했습니다!

비유


아이 부모는 아이를 유치원에 등록 하고자 해서 유치원에 등록시킨다

얘야 내일부터 유치원 가자! 네에!

어머 네가 오늘부터 오기로 한 아이구나! 네!!

이제부터 유치원은 아이를 관리한다.

준영속 / Detached


영속 상태인 객체를 영속성 컨택스트에서 분리하면, 더 이상 영속성 컨텍스트가 관리하지 않으면 준영속 상태라고 합니다!

나와!!! 안녕!!

객체를 준영속 상태로 만드는 방법은

  • em.detach()
  • em.close()
  • em.clear()

등의 메소드를 사용하면 됩니다. em.clear() 등의 메소드를 사용하면 영속성 컨텍스트가 초기화 되는데, 이때 영속성 컨텍스트가 관리하던 영속상태의 Entity들은 준영속 상태가 됩니다

비유


이제 집에 가자~ 네 엄마~

아이 부모는 유치원에서 아이를 하원시킨다 이제 유치원은 더 이상 아이를 관리하지 않는다.

아이 부모에 의해 아이는 유치원에 더 이상 관리되지 않는 상태에 놓여져 있다.

그렇다고 EntityManager가 객체를 관라히지 않습니다!!! 비유적인 표현입니다!!

삭제 / Removed


말 그대로 객체가 삭제된 상태입니다. 영속성 컨텍스트와 DB 모두에서 삭제를 시킵니다. em.remove() 메소드로 삭제할 수 있습니다

내가 없어져 볼게

얍!

비유


시간이 지나 부모는 다시 이사를 가네요… 이제 여기서 떠나는 군요ㅠㅠ

아이 부모는 아이를 데리고 이사를 갑니다

아이 부모에 의해 아이는 사라집니다…. 나중에 인연이 되면 또 만날지도…?

마무리


오늘은 일단 다음에 대해서 알아봤습니다

  • 영속성
  • Entity
  • EntityManager
  • EntityManagerFactory
  • 영속성 컨텍스트
  • Entity 생명주기

다음에는 바로

  • 영속성 컨텍스트의 특성에 대해서 알아보도록 하겠습니다!!

사실 1차 캐시, 변경 감지 등의 진짜 중요한 내용인데… 너무 템포가 길어지는 것 같아 일단은 여기서 끊겠습니다!