엔터티는 JPA에서 중요한 개념 중 하나로, 데이터베이스의 테이블과 1:1로 매핑되는 자바 객체를 의미한다. 이러한 엔터티를 사용함으로써 개발자는 데이터베이스의 테이블을 직접 조작하는 대신에 객체 지향적인 방식으로 데이터를 관리할 수 있게 되는것이다.
엔터티의 동등성
엔터티는 식별자 값 (@Id로 정의된 필드의 값)을 기반으로 동등성을 비교해야 한다.
즉, 엔터티 인스턴스의 주소값이 아닌 식별자 값을 기반으로 동등성을 판단하게 된다!!!!
엔터티는 JPA의 핵심 기능으로, 데이터베이스와의 작업을 객체 지향적으로 처리할 수 있도록 돕게 된다. 이를 통해 데이터베이스 테이블과의 직접적인 작업 없이도 데이터를 관리하고, 다른 객체들과의 관계도 설정할 수 있게 된다.
영속성은 데이터를 생성한 프로그램의 수명이 끝나도 사라지지 않는 데이터의 특성을 의미하는데 JPA(Java Persistence API)에서는 이를 위해 RDBMS와 같은 외부 저장소를 사용하여 객체 데이터를 영구적으로 저장한다!!!
Entity Manager는 JPA의 핵심적인!!!! 부분으로, 엔터티의 생명 주기를 관리하고 데이터베이스와의 모든 상호작용을 처리합니다. 주요기능으로서는
Entity Manager 인스턴스를 만드는 공장 역할을 한다. 애플리케이션의 시작 시점에 단 한 번만 생성되어야 하고, 이 팩토리를 통해 여러 Entity Manager 인스턴스들이 생성된다. 각각의 스레드나 요청은 자신만의 Entity Manager 인스턴스를 사용해야 하기 때문에, 스레드 간의 안전성을 보장받을 수 있다.
JPA 엔터티는 명확한 생명 주기를 가지며, 이는 다음과 같다:
영속성 컨텍스트는 JPA의 핵심적인 부분으로, "엔터티를 영구 저장하는 환경"을 의미합니다. 이 영역에서 엔터티는 데이터베이스와 동기화되며, 영속성 컨텍스트는 다음과 같은 특징과 구조를 가진다!예제도 같이 들어서 설명해보겠다.
User user1 = entityManager.find(User.class, 1L);
User user2 = entityManager.find(User.class, 1L);
위 코드에서 user1
과 user2
는 동일한 엔터티를 참조. 첫 번째 find
호출 시 데이터베이스에서 데이터를 가져와 1차 캐시에 저장하고, 두 번째 find
호출 시 1차 캐시의 데이터를 사용. 따라서, 두 번째 호출은 데이터베이스 접근 없이 캐시에서 정보를 가져오게 된다!
boolean isEqual = user1 == user2; // true
user1
과 user2
는 동일한 객체 인스턴스를 참조하므로 isEqual
은 true
를 반환합니다.
entityManager.getTransaction().begin();
User newUser = new User("이름");
entityManager.persist(newUser);
// 여기까지 데이터베이스에 저장되지 않음
entityManager.getTransaction().commit(); // 이 때 데이터베이스에 저장됨
persist
메서드 호출 시 실제 데이터베이스에 저장되지 않고, 트랜잭션 커밋 시에만 데이터베이스에 반영됩니다.
User user = entityManager.find(User.class, 1L);
user.setName("새 이름"); // 엔터티의 상태 변경
entityManager.getTransaction().commit(); // 변경 감지 후 데이터베이스에 이름 업데이트
Order order = entityManager.find(Order.class, 1L);
List<Item> items = order.getItems(); // 실제로 데이터를 사용하는 시점
Order
엔터티에 연관된 Item
엔터티 목록은 getItems()
메서드를 호출할 때 실제로 데이터베이스에서 로딩된다. 이를 통해 불필요한 데이터 로딩을 최소화하고 성능을 최적화할 수 있다.
플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화하는 작업이다. 이 작업은 영속성 컨텍스트에 있는 엔터티의 상태를 데이터베이스와 일치시키는 데 사용되는데. 그렇게 함으로써 데이터베이스의 현재 상태가 영속성 컨텍스트의 상태와 일치!!!
플러시는 다음의 경우에 발생:
User user = entityManager.find(User.class, 1L);
user.setName("변경된 이름");
entityManager.flush(); // 변경 내용을 데이터베이스에 즉시 반영
위의 예제에서 setName 메서드를 통해 엔터티의 상태가 변경되었다. flush 메서드를 호출하면 이 변경 사항이 데이터베이스에 즉시 반영된다.
entityManager.getTransaction().begin();
User user = new User("새 사용자");
entityManager.persist(user);
entityManager.getTransaction().commit(); // 커밋 시 플러시가 자동으로 발생
트랜잭션을 커밋할 때, JPA는 자동으로 플러시를 호출하여 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영합니다.
User user = entityManager.find(User.class, 1L);
user.setName("또 다른 이름");
List<User> users = entityManager.createQuery("SELECT u FROM User u", User.class).getResultList(); // JPQL 쿼리 실행 전 플러시 발생
JPQL 쿼리를 실행하기 전에 영속성 컨텍스트의 상태와 데이터베이스의 상태를 일치시키기 위해 플러시가 발생합니다.
요약: 플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화하는 중요한 작업이다. 이를 통해 영속성 컨텍스트와 데이터베이스 사이에 데이터의 일관성을 유지할 수 있다.
JPA는 객체와 관계형 데이터베이스 간의 패러다임 불일치 문제를 해결하는 프레임워크로, 위의 개념들은 JPA를 효과적으로 사용하기 위해 반드시 이해하고 있어야 하는것이라는걸 알게 되었다. 이러한 개념들을 깊게 파악하고 실제 개발에 적용하면, 데이터베이스 연산을 더욱 효과적으로 처리할 수 있게 될꺼라는 생각을 하는 공부였다!!!
오늘 JPA의 핵심 개념에 대해 깊게 다시 한 번 공부해볼 기회가 있었다. 사실 JPA를 사용하면서도 이런 핵심 개념들에 대해 얕게만 알고 지나갔던 부분들이 많았는데, 오늘 이렇게 상세하게 한 번 더 복습하면서 실질적으로 이해도가 높아진 것 같다.
특히, 엔터티의 생명 주기와 영속성 컨텍스트에 대한 부분이 가장 기억이 난다. 이전에는 단순히 객체를 데이터베이스에 저장하고 조회하는 도구로만 JPA를 생각했었는데, 실제로는 그 이상의 많은 기능과 최적화 기법이 숨어있음을 깨달았다. 이러한 내용을 알게 되면서, JPA를 사용할 때 어떻게 하면 더 효율적으로 데이터를 처리할 수 있을지에 대한 고민도 해보게 되었다.
또한, 플러시에 대한 부분도 깊게 이해할 수 있었다. 앞으로는 이러한 기본 개념을 바탕으로 JPA를 더욱 효과적으로 활용해보고, 다양한 최적화 기법에도 도전해보려고 한다.결과로는 앞으로의 개발에도 큰 도움이 될 것 같다.