JPA 소개

강정우·2024년 2월 24일
0

JPA

목록 보기
7/12
post-thumbnail

JPA

JPA 강점

앞서 자세히 설명 했기 때문에 간단히 짚어만 보겠다.

  1. 신뢰할 수 있는 엔티티, 계층 (객체 그래프 탐색)
  2. 성능 최적화
    1. 1차 캐시와 (동일 트랜잭션 안에서) 동일성 보장
    2. 트랜잭션을 지원한느 쓰기 지연 ( transactional write-behind ) -> 모아서 한 큐에 쑝
    3. 지연 로딩 ( Lazy Loading ) -> Row Lock 최소화 -> 실제로 객체가 사용될 때 까지 요청하지 않음.
  3. DB dialect 즉, db 방언으로 db 마다 사용법이 다 다른데 그딴거 없게 해준다.

JPA config with Maven

1. pom.xml 의존성 추가

    <dependencies>
        <!-- JPA 하이버네이트 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>6.4.2.Final</version>
        </dependency>

        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>

        <!-- H2 데이터베이스 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>2.2.224</version>
        </dependency>

2. /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="hello">
        <properties>
            <!-- 필수 속성 -->
            <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="jakarta.persistence.jdbc.user" value="sa"/>
            <property name="jakarta.persistence.jdbc.password" value=""/>
            <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>

            <!-- 옵션 -->
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.use_sql_comments"  value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="create" />
        </properties>
    </persistence-unit>

</persistence>

설치 위치가 매우 중요하다. 표준 위치가 정해져 있기 때문에 반드시 해당 위치에 넣어줘야한다.

<persistence-unit> : DB 당 1개씩 만듦
필수 속성: DB 접근 정보임.
옵션 속성: 해당 옵션들을 true로 해두면 추후 애플리케이션을 실행하면 DB에 쿼리문이 날아갈 때 해당 쿼리문을 보여준다는 옵션들이다.

  • 참고
    보면 jakarta로 시작하는게 있고 hibernate로 시작하는게 있는데 앞서 말 했듯hibernate는 JPA를 구현한 구현체이다. 이외에 Elclipse Link 같은 것도 있다.
    따라서 다른 JPA 구현체를 사용한다면 hibernate로 시작하는 부분은 해당 JPA 구현체로 바꿔줘야한다.

JPA 구동방식

1. config

우선 위 사진을 보면 1번으로 설정 정보를 조회한다고 하여 생성한다고 되어있는데 이를 코드로 구현해주면 된다.

public class JpaMain {

    public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        //code

        em.close();
        emf.close();
    }
}

EntityManagerFactory를 만드는 순간 DB와 연결도 되고 웬만한건 다 된다.
그리고 EntityManager를 이용하여 DB에 데이터를 저장한다든지 기타 등등의 작업을 하면 된다.

도메인 생성

create table Member (
	id bigint not null,
    name varchar(255),
    primary key (id)
);

만약 이렇게 생긴 매우 간단한 테이블이 있다고 가정할 때

@Data
@Entity
public class Member {
	@Id
    private Long id;
    private String name;
} 
  1. 위와 같은 테이블에 매핑되는 entity가 존재해야한다.
    즉, @Entity 가 붙어있어야 jpa가 처음 로딩될 때 jpa를 사용하는 애구나 라고 인식을 하고 본인이 관리하게 되는 것이다.

  2. 또한 반드시 기본생성자가 있어야한다.
    왜냐하면 JPA는 기본적으로 내부에서 리플렉션 등 여러 기술을 사용하며 동적으로 객체를 생성해내야하기 때문에 반드시 기본생성자가 있어야한다.

그리고 마치 JDBC 처럼 일일이 다 쳐보자면 아래와 같다.
물런 get conn 은 없지만 대신 Transaction이 매우 중요해서 get tx 가 존재한다.
또 물론 나중엔 annotation 으로 다 퉁칠 수 있다.

public class JpaMain {

    public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();
        try {
            Member member = new Member();
            member.setId(2L);
            member.setName("Hello B");
            em.persist(member);
            
            Member findMember = em.find(Member.class, 2L);
            findMember.setName("Hello JPA");
            
            List<Member> result = em.createQuery("select m from Member as m", Member.class)
            		.setFirstResult(1)
                    .setMaxResults(10)
                    .getResultList();

            System.out.println("result = " + result);
            
            tx.commit();
            
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
        emf.close();
    }
}

참고로 위 코드에서 볼 수 있는 update는 굳이 persist를 해주지 않아도 되는데 그 이유는 앞서 sql의 중심 개발의 문제점을 해결하려고 마치 java collection과 같이 객체 지향적으로 편하게 개발하도록 설계되어있기 때문이다.
그래서 set 메서드를 사용하면 그냥 그대로 db에 반영되어버린다.
여기서 나온게 이제 앞서 포스팅한 순환참조의 위험이 나온다.

  • 주의
  1. Entity Manager Factory 는 웹 서버에가 올라오는 시점에 딱 하나만 생성해서 애플리케이션 전체에서 공유됨.
  2. Entity Manager 는 Entity Manager Factory에서 생성되어 고객의 요청마다 생성되고 .close() 로 버려진다. 이때, 쓰레드 간 공유를 하면 안 되고, 쓰고 버려야한다.
  3. JPA의 모든 데이터 변경은 트랜잭션 안에서 실행되어야함. 참고로 모든 RDB 들은 데이터 변경 자체가 트랜잭션 안에서 실행하도록 설계가 되어있다.
  4. 애플리케이션이 끝날 땐(WAS가 내려갈 때) Entity Manager Factory을 닫아줘야 내부적으로 리소스가 release 된다.

복잡한 쿼리

단순 조회, 삭제, 수정 정도는 JPA에서 기본적으로 제공하는 함수를 사용하면 되지만 조건이 붙는다든지 조금 복잡한 동적쿼리들은 처리할 수 있는 여러 방법들 중 .createQuery() 메서드로 직접 쿼리(sql아니고 jpql)를 짤 수 있기도 하다.

JPA 입장에서는 코드를 짤 때 table이 아닌 객체( entity )를 대상으로 코드를 작성한다.
그래야 앞서 sql문 중심의 설계의 한계를 벗어날 수 있고 DB들의 dialect에 영향을 받지 않기 때문이다.

profile
智(지)! 德(덕)! 體(체)!

0개의 댓글