영속성컨텍스트란?
일종의 메모리 저장소로 EntityManager가 Entity 를 추적 관리하는 보관함
기본적으로 영속성이란 사전적으로 영원히 계속되는 성질이나 능력을 의미한다. 데이터 관점에서 영속성은 데이터베이스 등 물리적인 저장소에 데이터를 저장하고, 저장된 데이터는 영속적으로 저장되는 것을 의미한다. 즉 영속성컨텍스트란 애플리케이션과 데이터베이스 사이에 존재하며 엔티티를 영구 저장하는 환경을 뜻하는 것이다. 예를 들어 어플리케이션에서 사용자 정보를 데이터베이스에 저장하기 위해서는 데이터베이스에 접근하기 이전에 반드시 영속성컨텍스트를 거치게 된다. )
객체와 테이블의 관계
* JDBC(:Java Database Connectivity) : 자바 어플리케이션에서 데이터베이스에 접근할 수 있는 인터페이스이다. 그리고 해당 인터페이스의 실제 구현체를 드라이버라고 한다. 각 데이터베이스 제조사들은, JPA 인터페이스를 기준으로 하여 구현 클래스들의 모음을 제공하고 이를 드라이버라고 한다. 즉 자바 애플리케이션 상에서 데이터의 처리는 아래와 같은 구조로 이루어진다고 할 수 있다.
* JDBC Driver : JDBC 인터페이스에 맞추어 구현될 클래스들의 모음.
자바 어플리케이션은 데이터 처리시, 애플리케이션 개발자는 인터페이스를 통해 각 데이터베이스 제조사들이 제공한 구현 클래스를 이용하여 손쉽게 데이터베이스에 접근할 수 있다. 또한 데이터베이스의 변경이 필요한 경우에도 어플리케이션 개발 코드의 변경없이 드라이버에 관한 설정을 변경해주는 것만으로 교체가 가능해진다.
Public class UserJDBCProviderImpl implements UserProvider {
private final String DB_URL = "jdbc:hsqldb:test:test";
private final String USER_Id = "user";
private final String PASSWORD = "password";
public UserJDBCProviderImpl(){
try { Class.forName("org.hsqldb.jdbcDriver");}
catch (ClassNotFoundException e) {
throw new RuntimeExcption("JDBC 드라이버 로드 중 에러 발생")
}
}
public List<User> findAllUsers() {
List<User> users = new ArrayAsList<User>();
String sql = "SELECT USERID, NAME, EMAIl, TYPE "
+ "FROM USER_TB " +"ORDER BY USERID ASC";
connecation conn = null;
ResultSet rs = null;
try {
conn = DriverManager.getConnection(DB_URL, USER_ID, PASSWORD);
psmt = conn.prepareStatement(sql);
rs = psmt.executeQuery();
while(rs.next()) {
User user = new User();
user.setUserId(rs.getString(1));
uset.setName(rs.getString(2));
users.add(user);
}
} catch (SQLException e) {
throw new RuntimeException("전체 사용자 조회 중 오류 발생");
} finallay {
try {
if (rs != null) rs.close();
if (psmt != null) rs.close();
if (conn != null) conn.close();
}catch(SQK Exception e) {}
}
return users;
}
}
위의 코드는 실제 자바 어플리케이션에서 데이터를 처리하는 과정을 구현한 코드이다. 위의 경우 개발자는 직접 sql을 짜야할 필요가 있고, 도메인이 변경되면, 기존에 구현한 sql 쿼리들도 모두 수정해 주어야 한다. 예를 들어 User에 phone이 추가되다면 이와 관련하여 개발자는 직접 관련된 쿼리 하나 하나를 "SELECT USERID, NAME, EMAIl, TYPE, PHONE " + "FROM USER_TB " +"ORDER BY USERID ASC"; 이와 같이 수정해 주어야 한다. 이는 자바 애플리케이션 내에 존재하는 데이터 객체와 데이터베이스에 저장되는 데이터의 형태가 같지 않기 때문에 형태를 맞추어 주기 위해 이루어지는 작업이라 할 수 있다.
SQL Mapping와 OR Mapping은 위와 같은 작업을 개발자가 더욱 손쉽게 할 수 있도록 등장한 Persistence 프레임워크의 일종이다.
* SQL Mapping : 자바 코드와 sql을 분리하며, 개발자가 작성한 sql의 수행 결과를 객체로 매핑하여 준다. ex) Mybatis
* OR Mapping (=ORM=Object-Relational Mapping) : 어플리케이션의 객체와 데이터베이스 테이블을 매핑해주는 기술 또는 도구를 의미한다. 즉 개발자가 직접 sql을 작성하지 않아도 ORM 프레임워크가 만들어 실행해주기 때문에 개발자는 SQL 중심의 개발에서 벗어나 진정한 의미의 객체 중심의 개발을 실현하고, 비즈니스 로직에 더욱 집중할 수 있게 된다.
JPA(Java Persistence API)
JPA는 자바 진영의 ORM 기술 표준으로, 자바 어플리케이션에서 관계형 데이터베이스에 접근하는 방식을 명세화한 인터페이스이다. 자바 애플리케이션과 JDBC 사이에서 동작한다.
* JPA는 인터페이스에 불과하며, 해당 인터페이스를 실제 구현한 클래스들의 모음인 구현체가 필요하다. 일반적으로는 Hibernate 라이브러리가 많이 사용된다.
* Application -> JPA(Hibernate) -> JDBC -> RDBMS 의 과정으로 데이터 처리가 이루어진다.
h2 database 이용하여 jpa 사용해보기
1. http://www.h2database.com/html/main.html 해당 사이트에서 각자 운영체제에 맞는 것을 찾아 다운로드한다.
2. 나(맥)의 경우 Platform-Independent Zip 으로 다운받은 후 bin폴더의 jar파일을 열었더니 아래와 같은 h2 콘솔화면이 나왔다.
h2 데이터베이스는 Server mode / Embedded Mode / In-memory mode 3가지 버전으로 사용할 수 있다. 각 버전에 따라 설정값이 달라지게 된다.
나는 Server mode를 사용하였다. h2 실행 시킨 후에 아래와 같이 맥 위쪽에서 해당 아이콘 클릭 후 Create a new database 클릭하여 준다.
아래와 같이 창이 나오면 데이터베이스를 생성해 줄 경로를 입력해준 후 create를 눌러준다. 이후에 다시 콘솔창으로 돌아와서 생성해준 데이터베이스의 정보를 통해 해당 데이터베이스에 접속한다.
기본적인 gradle 기반의 스프링 프로젝트에 dependency를 추가하여 준다. 기본적으로 나의 build.gradle의 내용은 아래와 같다.
persistence.xml파일은 아래와 같다.
그리고 객체 클래스 Customer는 아래와 같다.
실제로 데이터를 넣는 코드는 아래와 같다.
h2 console을 통해 아래와 같이 데이터가 들어간 것을 확인할 수 있다.
또한 데이터베이스의 데이터를 어플리케이션에서 가져올 때는 entityManager의 find 메서드를 이용하는데, 이때 find 메서드를 통해 가져온 데이터베이스 상의 데이터는 Customer 객체 클래스가 받아서 처리할 수 있도록 기본 생성자가 반드시 존재해야 한다. 때문에 실제로 아무런 인자를 갖지 않는 생서자를 구현하여 주거나 해당 클래스에 @NoArgsConstructor를 추가해주어도 된다.
데이터 변경시에는 데이터베이스에서 받은 데이터를 클래스 객체로 받아 아래와 같이 set메서드를 통해 변경시킬 수 있다. 이것이 가능한 것은 클래스 객체로 변경한 것이여도 entityManager가 있기 때문이다.
* 영속성 컨텍스트는 앞서 이야기 하였듯이 기본적으로 EntityManager를 통해 접근 가능하다. 즉 영속성컨텍스트를 EntityManager가 엔티티(클래스 객체)를 관리하기 위한 보관함으로도 볼 수 있다. EntityManager는 영속성컨텍스트에 엔티티를 보관하는 동시에, 데이터베이스의 데이터와 비교하여 영속성컨텍스트에 존재하는 엔티티와 데이터 간 차이가 존재한다면 이를 동기화하는 기능을 수행한다.
구조를 살펴 보면 영속성 컨텍스트는 내부에 Map을 가지고 있고, Map<[키],[값]>형태로 캐시에 엔티티를 저장하며, 이를 1차 캐시라 한다. 이때 엔티티의 id를 Map의 키로 가지고, 엔티티의 인스턴스를 값으로 가진다. ( 데이터베이스의 기본키와 매핑된 엔티티의 id를 엔티티의 식별자 값 즉 키로 구분하기 때문에, 엔티티를 영속성 컨텍스트에 의해 관리하기 위해서는 반드시 식별자 값(키=엔티티의id)이 존재해야 한다.)
주요 개념
EntityManagerFactory
EntityManagerFactory는 EntityManager를 생성 관리하고, 데이터베이스와의 연결을 설정하는 역할을 하며, Entity 클래스와 매핑 정보를 읽어온다. 애플리케이션의 시작 시점에서 한 번만 생성되어 애플리케이션 전역에서 공유되어 사용된다. EntityManagerFactory는 Persistence 클래스의 createEntityManagerFactory() 메서드를 통해 생성할 수 있는데, 이때 인자에는 persistence.xml에 설정해준 persistence-unit 명을 넣어준다.
* persistence.xml은 jpa를 사용하기 위한 설정 파일로, EntityManagerFactory 생성시 관련 설정에 대한 내용을 읽어오는 파일이라 할 수 있다. resources의 하위에 META-INF폴더에 persistence.xml 파일명으로 생성하면 되며, 해당 파일을 통해 데이터베이스 드라이버 클래스, JDBC URL, user, password, 데이터베이스에 연동될 클래스 파일 위치 등의 정보를 설정할 수 있다.
EntityManager
EntityManager는 EntityManagerFactory를 통해 생성되며, 데이터 등록, 수정, 삭제 등의 트랜잭션 처리를 수행한다.
• find() 메서드
Primary Key를 사용하여 Entity 객체를 조회하는데, 우선 1차적으로 영속성 컨텍스트를 조회하고,
영속성 컨텍스트의 1차 캐시에 해당 엔티티 정보가 존재하지 않는 경우, 데이터베이스를 통해 해당 엔티티를 조회한다.
데이터베이스에 해당 데이터 정보가 존재하는 경우, 데이터베이스로부터 반환된 정보를 통해 엔티티를 생성하여 영속성
컨텍스트의 1차 캐시에 저장하고, 영속성 컨텍스트에 저장된 데이터를 반환하여 준다
• persist() 메서드
새로운 Entity 객체를 영속성 컨텍스트에 등록하는 것으로 이 이후부터 해당 엔티티는 EntityManager에 의해 관리된다. 그래서 해당 Entity에 변경이 발생하는 경우 EntityManager는 이를 데이터베이스에 반영한다. EntityManager는 그러한 변화에 대한 처리를 데이터베이스에 바로 바로 하지 않고 내부 쿼리 저장소에 관련 sql을 모아두었다가 커밋이 있을 때, 최종적으로 한 번에 해당 쿼리들을 데이터베이스에 전달함으로써 성능을 최적화 한다. 트랜잭션을 커밋하면 영속성 컨텍스트의 데이터를 데이터베이스에 반영하는 플러시가 발생하여, 영속성 컨텍스트와 데이터베이스 간 동기화가 이루어진다.
• remove() 메서드
영속성 컨텍스트에서 Entity 객체를 삭제한다.
• merge() 메서드
Entity 객체의 상태를 변경하고, 변경된 상태를 데이터베이스에 반영 및 새로운 영속 상태의 엔티티를 반환한다.
• flush() 메서드
영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다. 플러시는 flush()메서드의 직접 호출, 커밋, jpql 쿼리 실행 시의 경우에 발생한다.
• refresh() 메서드
데이터베이스에서 Entity 객체를 다시 조회하여 영속성 컨텍스트의 상태를 업데이트한다.
관련코드
import com.example.spring01.entity.Customer;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
@SpringBootApplication
public class Spring01Application {
public static void main(String[] args) {
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("myPersistenceUnit");
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
try {
transaction.begin();
// entityManager.persist(Customer.sample());
// 데이터 생성
Customer foundedCustomer = entityManager.find(Customer.class, "0001");
// 데이터 조회
// find : 데이터를 가져오는 메서드
// Customer 객체 형태의 데이터
// id 가 0001인 데이터
foundedCustomer.setName("changed Name");
// 데이터베이스 형태의 데이터를 변경 시킨 것이 아닌 클래스 객체를 통해 데이터의 내용을 변경시킴
transaction.commit();
}catch(Exception e) {
transaction.rollback();
System.out.println(e+"e==============");
}finally {
entityManager.close();
entityManagerFactory.close();
}
}
}
Entity 생명주기
1. 비영속 상태(New Or Transient Stage): 영속성 컨텍스트에 아직 포함되어 있지 않은 상태로, 영속성 컨텍스트와는 관련이 없다.
2. 영속 상태(Managed State) : 영속성 컨텍스트에 저장되어 EntityManager에 의해 관리되는 상태이다.
3. 준영속 상태(Detached State) : 영속성 컨텍스트에 저장되었다가 분리된 상태로, 관리에서 제외된다.
4. 삭제 상태(Removed Stage) : 영속성 컨텍스트에서 삭제된 상태이다.
EntityManager는 Entity 객체의 상태 변화를 추적 관리하는데, 예를 들어 새로운 Entity 객체 생성 시에는 비영속 상태로 인식되며, 이후 persist()메서드를 통해 영속 상태로 변경되어 관리된다. 이후 merge 메서드 호출 시 스냅샵과 비교하여 변경 내용을 추적하고 변경된 내용을 데이터베이스에 반영한다.
* 스냅샷
영속성 컨텍스트에 엔티티가 영속될 때의 상태를 저장해둔 것을 스냅샷이라 한다.( 즉 영속성 컨텍스트에 엔티티가 저장될 때의 상태를 스냅샷으로 저장해두었다가 플러시(영속성컨텍스트와 데이터베이스의 동기화)를 통해 가져온 엔티티와의 상태 비교 등을 위해 사용된다.)
- 흐름
예를 들어 어플리케이션에서 사용자 정보를 데이터베이스에 저장하려 할 때, 어플리케이션은 사용자 정보를 담은 객체 형태의 엔티티를 영속성컨텍스트에 전달하고, 영속성컨텍스트는 해당 사용자 정보를 저장하고, 데이터를 데이터베이스의 데이터 형태로 변환하여 데이터베이스에 전달합니다. 이로써 영속성컨텍스트의 데이터와 데이터베이스의 데이터가 동기화 됩니다. 반대로 어플리케이션에서 사용자 정보를 요청할 때 우선 영속성컨텍스트를 통해 조회한 후 영속성컨텍스트에 사용자 정보가 없을 때에 데이터베이스에 사용자 정보를 요청하여 데이터베이스의 데이터 형태로 사용자 정보를 받은 후 이를 다시 자바의 객체 형태로 변환하여 어플리케이션에 전달합니다. 또한 사용자 정보 변경 뒤 영속성컨텍스트에 보내면 해당 엔티티의 식별키를 가지고 데이터베이스의 사용자 정보를 가져와 두 정보를 비교하여 일치하지 않으면, 변경된 엔티티 정보를 update 쿼리를 사용하여 데이터베이스의 정보도 변경한다. 이것이 바로 영속성 컨텍스트가 하는 일이다. 즉 자바로 이루어진 어플리케이션 서버에서 데이터베이스와 관련한 모든 crud 작업을 처리하는 역할을 하는 것이 영속성 컨텍스트이다. 즉 데이터베이스에 대한 모든 트랜잭션을 수행하는 역할을 하는 것이 바로 영속성 컨텍스트이다.
* Spring Data JPA
spring data jpa는 jpa를 편하게 사용하기 위해 만들어진 모듈로, jpa를 한 단계 더 추상화시켰다. Repository 인터페이스를 사용하며, Repository 인터페이스에 정해진 규칙대로 메서드 입력시 Spring이 알아서 해당 메서드 명에 적합한 쿼리를 날리는 구현체를 만들어 Bean으로 등록해준다.
출처
https://www.youtube.com/playlist?list=PLOSNUO27qFbvzGd3yWbHISxHctPRKkctO
JPA for Beginner
이 강의는 JPA(Java Persistence API) 기초 강의 입니다. 하이버네이트(Hibernate) 기반의 순수 JPA에 대해 학습하며 Spring Data JPA도 포함한 강의 입니다.
www.youtube.com