
Entity 란 JPA 에서 관리되는 클래스(객체)를 의미.
Entity 클래스는 DB 의 테이블과 매핑되어 JPA 에 의해 관리됨

@Entity : JPA 가 관리할 수 있는 Entity 클래스로 지정할 수 있다.
@Entity(name = "Memo") : Entity 클래스 이름을 지정할 수 있다.(디폴트는 클래스명)
JPA 가 Entity 클래스를 인스턴스화 할 때 기본 생성자를 사용하기 때문에 반드시 현재 Entity 클래스에서 기본 생성자가 생성되고 있는지 확인해야 함
@Table : 매핑할 테이블을 지정
@Table(name = "memo") : 매핑할 테이블의 이름을 지정할 수 있다.(디폴트는 엔티티명)
@Column :
@Column(name = "username") : 필드와 매핑할 테이블의 컬럼을 지정할 수 있다. (디폴트는 객체의 필드명)
@Column(nullable = false) : 데이터의 null 값 허용 여부를 지정할 수 있다. (디폴트는 true)
@Column(unique = true) : 데이터의 중복 값 허용 여부를 지정할 수 있다.(디폴트는 false)
@Column(length - 500) : 데이터 값(문자) 의 길이에 제약조건을 걸 수 있다.
@Id : 테이블의 기본 키를 지정
이 기본키는 영속성 컨텍스트에서 Entity 를 구분하고 관리할 때 사용되는 식별자 역할을 수행
따라서 기본키(식별자)값을 넣어주지 않고 저장하면 오류가 발생
@Id 옵션만 설정하면 기본 키 값을 개발자가 직접 확인하고 넣어줘야 하는 불편함이 발생한다.
@GeneratedValue 옵션을 추가하면 기본키 생성을 DB 에 위임 가능

영속성 컨텍스트

Entity 객체를 효율적으로 쉽게 관리하기 위해 만들어진 공간
이제 직접 SQL 을 작성하지 않아도 JPA 를 사용하여 DB 에 데이터를 저장하거나 조회할 수 있으며 수정, 삭제 또한 가능하다.

영속성 컨텍스트에 접근하여 Entiry 객체들을 조작하기 위해서는 EntityManager 가 필요하다.
EntityManager 를 사용해서 Entity 를 저장하고 수정하고 삭제 가능
EntiryManager 는 EntityManagerFactory 를 통해 생성, 사용할 수 있다.

EntityManagerFactory 는 일반적으로 DB 하나에 하나만 생성되어 애플리케이션이 동작하는 동안 사용됨
EntityManagerFactory 를 만들기 위해서는 DB 에 대한 정보를 전달해야함
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="memo">
<class>com.sparta.entity.Memo</class>
<properties>
<property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jakarta.persistence.jdbc.user" value="root"/>
<property name="jakarta.persistence.jdbc.password" value="{비밀번호}"/>
<property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/memo"/>
<property name="hibernate.hbm2ddl.auto" value="create" />
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
</properties>
</persistence-unit>
</persistence>
대략 이렇게...

이런식으로 만들 수 있다.
JPA 의 트랜잭션

트랜잭션은 DB 데이터들의 무결성과 정합성을 유지하기 위한 하나의 논리적 개념
가장 큰 특징은 여러개의 SQL 이 하나의 트랜잭션에 포함될 수 있다는 점.
이 때 모든 SQL 이 성공적으로 수행이 되면 DB 에 영구적으로 변경을 반영하지만 SQL 중 단 하나라도 실패한다면 모든 변경을 되돌린다.
START TRANSACTION; # 트랜잭션을 시작합니다.
INSERT INTO memo (id, username, contents) VALUES (1, 'Robbie', 'Robbie Memo');
INSERT INTO memo (id, username, contents) VALUES (2, 'Robbert', 'Robbert Memo');
SELECT * FROM memo;
COMMIT; # 트랜잭션을 커밋합니다.
SELECT * FROM memo;
JPA 는 DB 의 이러한 트랜잭션 개념을 사용하여 효율적으로 Entity 를 관리하고 있다.

영속성 컨텍스트에 Entity 객체들을 저장했다고 해서 DB 에 바로 반영되지는 않는다.

DB 에서 하나의 트랜잭션에 여러 개의 SQL 을 포함하고 있다가 마지막에 영구적으로 변경을 반영하는 것 처럼 JPA 에서도 영속성 컨텍스트로 관리하고 있는 변경이 발생한 객체들의 정보를 쓰기 지연 저장소에 전부 가지고 있다가 마지막에 SQL 을 한번에 DB 에 요청해 변경을 반영함
성공 예제
@Test
@DisplayName("EntityTransaction 성공 테스트")
void test1() {
EntityTransaction et = em.getTransaction(); // EntityManager 에서 EntityTransaction 을 가져옵니다.
et.begin(); // 트랜잭션을 시작합니다.
try { // DB 작업을 수행합니다.
Memo memo = new Memo(); // 저장할 Entity 객체를 생성합니다.
memo.setId(1L); // 식별자 값을 넣어줍니다.
memo.setUsername("Robbie");
memo.setContents("영속성 컨텍스트와 트랜잭션 이해하기");
em.persist(memo); // EntityManager 사용하여 memo 객체를 영속성 컨텍스트에 저장합니다.
et.commit(); // 오류가 발생하지 않고 정상적으로 수행되었다면 commit 을 호출합니다.
// commit 이 호출되면서 DB 에 수행한 DB 작업들이 반영됩니다.
} catch (Exception ex) {
ex.printStackTrace();
et.rollback(); // DB 작업 중 오류 발생 시 rollback 을 호출합니다.
} finally {
em.close(); // 사용한 EntityManager 를 종료합니다.
}
emf.close(); // 사용한 EntityManagerFactory 를 종료합니다.
}
JPA 에서 이러한 트랜잭션 개념을 적용하기 위해서는 EntityManager 에서 EntityTransaction 을 가져와 트랜잭션을 적용할 수 있다.
실패 예제
@Test
@DisplayName("EntityTransaction 실패 테스트")
void test2() {
EntityTransaction et = em.getTransaction(); // EntityManager 에서 EntityTransaction 을 가져옵니다.
et.begin(); // 트랜잭션을 시작합니다.
try { // DB 작업을 수행합니다.
Memo memo = new Memo(); // 저장할 Entity 객체를 생성합니다.
memo.setUsername("Robbert");
memo.setContents("실패 케이스");
em.persist(memo); // EntityManager 사용하여 memo 객체를 영속성 컨텍스트에 저장합니다.
et.commit(); // 오류가 발생하지 않고 정상적으로 수행되었다면 commit 을 호출합니다.
// commit 이 호출되면서 DB 에 수행한 DB 작업들이 반영됩니다.
} catch (Exception ex) {
System.out.println("식별자 값을 넣어주지 않아 오류가 발생했습니다.");
ex.printStackTrace();
et.rollback(); // DB 작업 중 오류 발생 시 rollback 을 호출합니다.
} finally {
em.close(); // 사용한 EntityManager 를 종료합니다.
}
emf.close(); // 사용한 EntityManagerFactory 를 종료합니다.
}
식별자 값을 안넣어줘서 실패.
따라서 et.rollback() 이 호출되어 트랜잭션 작업 내용들이 취소됨
영속성 컨텍스트의 기능
영속성 컨텍스트는 Entity 객체를 효율적으로 쉽게 관리하기 위해 만들어진 공간

영속성 컨텍스트는 내부적으로 캐시 저장소를 가지고 있다.
우리가 저장하는 엔티티 객체들이 1차 캐시, 즉 캐시 저장소에 저장된다고 생각하시면된다.
캐시 저장소는 Map 자료구조 형태로 되어있다.
key 에는 @Id 로 매핑한 기본키(식별자) 값을 저장
value 에는 해당 Entity 클래스의 객체를 저장
영속성 컨텍스트는 캐시 저장소 key 에 저장한 식별자값을 사용하여 Entity 객체를 구분하고 관리한다.
저장

em.persist(memo) 메서드가 호출되면 memo Entity 객체를 캐시 저장소에 저장
조회

em.find(Memo.class, 1) 호출 시 만약 값이 있다면 그대로 반환해주고 없다면 DB 에 SELECT 조회 후 해당 값을 캐시 저장소에 저장하고 반환 한다.
조회는 값에 변화가 생기는게 아니기 때문에 @트랜젝션 없이도 가능

그냥 찾아서 반환해준다.
1차 캐시 사용의 장점
Entity 삭제

em.remove(memo) 호출 시 삭제할 Entity 를 DELETED 상태로 만든 후 트랜잭션 commit 후 Delete SQL 이 DB 에 요청함
쓰기 지연 저장소(ActionQueue)

JPA 의 트랜잭션을 학습하면서 JPA 가 트랜잭션 처럼 SQL 을 모아서 한번에 DB 에 반영한다는 것.
@Test
@DisplayName("쓰기 지연 저장소 확인")
void test6() {
EntityTransaction et = em.getTransaction();
et.begin();
try {
Memo memo = new Memo();
memo.setId(2L);
memo.setUsername("Robbert");
memo.setContents("쓰기 지연 저장소");
em.persist(memo);
Memo memo2 = new Memo();
memo2.setId(3L);
memo2.setUsername("Bob");
memo2.setContents("과연 저장을 잘 하고 있을까?");
em.persist(memo2);
System.out.println("트랜잭션 commit 전");
et.commit();
System.out.println("트랜잭션 commit 후");
} catch (Exception ex) {
ex.printStackTrace();
et.rollback();
} finally {
em.close();
}
emf.close();
}

commit 후에는 사라진다.
Commit 과 함께 flush() 가 작동하는데 이것이 DB 에 직접적으로 값을 반영시켜준다.
영속성 컨텍스트에 저장된 Entity 가 바뀔때마다 Update SQL 이 쓰기 지연 저장소에 저장되면 낭비다.
때문에, 영속성 컨텍스트에 저장될 떄 최초 상태(LoadedState) 를 저장하고 트랜잭션이 commit 되고 em.flush() 가 호출되면 Entity 의 현태 상태와 저장한 최초 상태를 비교한다.
변경 내용이 있다면 Update SQL 을 생성하여 쓰기 지연 저장소에 저장하고 모든 쓰기 지연 저장소의 SQL 을 DB 에 요청.
마지막으로 DB 의 트랜잭션이 commit 되면서 반영됨.
이러한 과정을 변경 감지(Dirty Checking) 이라 함.
Entity 의 상태

비영속 (new/transient): 영속성 컨텍스트와 관계가 없는 상태. 아직 데이터베이스에 저장되지 않음.
영속 (managed): 영속성 컨텍스트에 의해 관리되는 상태. 데이터베이스와 동기화됨.
준영속 (detached): 영속성 컨텍스트에서 분리된 상태. 데이터베이스와 더 이상 동기화되지 않음.
삭제 (removed): 데이터베이스에서 삭제된 상태.