2023.08.28 - Spring 입문 (2주차) -1

mjjin·2023년 8월 27일
0

Entity

  • JPA에서 관리되는 클래스 즉, 객체를 의미한다.
  • Entity 클래스는 DB의 테이블과 매핑되어 JPA에 의해 관리된다.

작성 예시 코드

@Entity // JPA가 관리할 수 있는 Entity 클래스 지정
@Table(name = "memo") // 매핑할 테이블의 이름을 지정
public class Memo {
    @Id
		@GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // nullable: null 허용 여부
    // unique: 중복 허용 여부 (false 일때 중복 허용)
    @Column(name = "username", nullable = false, unique = true)
    private String username;

    // length: 컬럼 길이 지정
    @Column(name = "contents", nullable = false, length = 500)
    private String contents;
}
  • @Entity : JPA가 관리할 수 있는 Entity 클래스로 지정
    • @Entity(name = "Memo") " Entity 클래스의 이름을 지정
  • @Table : 매핑할 테이블 지정
  • @Column : 테이블의 컬럼을 지정 (default:객체의 필드명)
    • @Column(name = "username") : 테이블의 컬럼과 객체의 필드명 설정 (default:객체의 필드명)
    • @Column(nullabel = false) : 데이터의 null 값 허용 여부 지정 (default: true)
    • @Column(unique = true) : 데이터의 중복 값 허용 여부 지정 (default : false)
    • @Cloumn(length = 500) : 데이터 값(문자)의 길이에 제약조건 생성 (default: 255)
  • @Id : 테이블의 기본 키 지정
    • 영속성 컨텍스트에서 Entity를 구분하고 관리할 때 사용되는 식별자 역할 수행
    • @GeneratedValue(strategy = GenerationType.IDENTITY) : 기본 키 생성을 DB에 위임 가능
      • auto_increment 조건이 추가된다.
      • 해당 옵션을 추가해주면 개발자가 직접 id 값을 넣어주지 않아도 자동으로 순서에 맞게 기본 키가 추가된다.

영속성 컨텍스트

Entity 객체를 효율적으로 쉽게 관리하기 위해 만들어진 공간



JPA는 영속성 컨텍스트에 Entity객체들을 저장하여 관리하면서 DB와 소통한다.

EntityManager

  • 영속성 컨텍스트에 접근하여 Entity 객체들을 조작하기 위해서는 EntityManager가 필요하다
  • EntityManager는 EntityManagerFactory를 통해 생성할 수 있다.

EntityManagerFactory

  • EntityManagerFactory는 일반적으로 DB 하나에 하나만 생성되어 애플리케이션이 동작하는 동안 생성된다.
  • 정보를 전달하기 위해 /resources/META-INF/ 위치에 persistence.xml 파일을 만들어 정보를 넣어두면 된다.

<파일예시>

<?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>
`EntityManagerFactory emf = Persistence.createEntityManagerFactory("memo")` :
EntityManager em = emf.createEntityManager();
  • EntityManagerFactory emf = Persistence.createEntityManagerFactory("memo") :
    JPA는 persistence.xml 의 정보를 토대로 EntityManagerFactory를 생성

  • EntityManager em = emf.createEntityManager() : 코드를 호출하면 EntityManagerFactory를 사용하여 EntityManager를 생성

JPA의 트랜잭션

트랜잭션이란?

  • DB 데이터들의 무결성과 정합성을 유지하기 위한 하나의 논리적 개념
  • 여러개의 SQL이 하나의 트랜잭션에 포함될 수 있다.
  • SQL 중 하나라도 실패하면 모든 변경을 되돌린다.

<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의 트랜잭션

영속성 컨텍스트로 관리하고 있는 변경이 발생한 객체들의 정보를 쓰기 지연 저장소에 전부 가지고 있다가 마지막에 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 를 종료합니다.
}
  • EntityManager에서 EntityTransaction을 가져와 트랜잭션을 적용할 수 있다.
    • EntityTransaction et = em.getTransaction(); : EntityTransaction을 가져와 트랜잭션을 관리
  • et.begin() : 트랜잭션 시작 명령어
  • et.commit() : 트랜잭션의 작업을 DB에 반영
  • et.rollback() : 오류가 발생했을 때, 트랜잭션 작업 취소 후 이전상태로 되돌리는 명령

영속성 컨텍스트의 기능

저장 : em.persist(memo)

<저장 코드 예시>

@Test
@DisplayName("1차 캐시 : Entity 저장")
void test1() {
    EntityTransaction et = em.getTransaction();

    et.begin();

    try {

        Memo memo = new Memo();
        memo.setId(1L);
        memo.setUsername("Robbie");
        memo.setContents("1차 캐시 Entity 저장");

        em.persist(memo);

        et.commit();

    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback();
    } finally {
        em.close();
    }

    emf.close();
}

조회 : em.find(Memo.class, 1)

  • 이 때, 캐시 저장소에 해당 값이 없다면 DB에 SELECT 조회 후 해당 값을 캐시 저장소에 저장해 반환한다.

<조회 코드 예시>

@Test
@DisplayName("Entity 조회 : 캐시 저장소에 해당하는 Id가 존재하는 경우")
void test3() {
    try {

        Memo memo1 = em.find(Memo.class, 1);
        System.out.println("memo1 조회 후 캐시 저장소에 저장\n");

        Memo memo2 = em.find(Memo.class, 1);
        System.out.println("memo2.getId() = " + memo2.getId());
        System.out.println("memo2.getUsername() = " + memo2.getUsername());
        System.out.println("memo2.getContents() = " + memo2.getContents());


    } catch (Exception ex) {
        ex.printStackTrace();
    } finally {
        em.close();
    }

    emf.close();
}

삭제 : em.remove(memo)

<삭제 코드 예시>

@Test
@DisplayName("Entity 삭제")
void test5() {
    EntityTransaction et = em.getTransaction();

    et.begin();

    try {

        Memo memo = em.find(Memo.class, 2);

        em.remove(memo);

        et.commit();

    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback();
    } finally {
        em.close();
    }

    emf.close();
}

0개의 댓글