JPA는 Java 진영의 표준 ORM 기술로 현재 Mybatis를 맹추격 중인 기술이다.
더 자세한 내용은 참고
기존엔 Mybatis를 xml에 쿼리를 정의하고 prarmeterType이랑 resultType이랑 따로 나눠서 객체 리턴 받고
동적인 쿼리라도 나오면 조건문 들어가면서 쿼리가 상당히 길어지고
컬럼이 하나 추가되는 일이 발생한다? 정말 상상하기도 싫다.
관련된 SQL을 모두 전부 수정해야 한다.
DB에 데이터를 저장하기 위해 SQL Mapper를 썼지만 Mapping은 내가 직접 하고 다 내가 짜야했다.
정말 업무를 진행하면서 비즈니스 로직을 위한 고민보다 쿼리를 위해 고민하는 시간이 더 많았다.
하지만 JPA를 사용한다면 이런 고민을 덜 수 있다.
쿼리를 직접 사용하지 않아도 되고, 마치 컬렉션에서 값을 꺼내서 사용하듯 객체를 수정, 삭제 , 조회를 할 수 있다.
그리고 hibernate라는 JPA의 기본 구현체가 SQL을 직접 만들어서
DB에 전달하기 때문에 휴먼 에러를 발생시킬 요인이 있는 나보다 더 정확하게 쿼리를 날려준다.
서론은 이만하고 바로 시작해보자.
개발환경
개발 환경을 하기 이전에 이 글에서는 스프링의 도움 없이 순수 JPA를 사용하다가
추후에 학습이 이어지면서
스프링 부트와 스프링 데이터 JPA를 같이 사용하고 QueryDSL까지 얹어보도록 시리즈로 글을 연재할 계획이다.
Java 11
Intelli J IDEA
Gradle
MySQL 8.0.1
설정 파일 준비
일단 JPA를 사용하기 위해서 Gradle에
implementation group: 'org.hibernate', name: 'hibernate-core', version: '5.3.10.Final'
위 라이브러리를 추가한다.
상위 버전이 있지만 5.3.10을 사용하는 이유는 스프링 부트 2.x에서 JPA의 버전을 5.3.10을 기본으로 사용하기 때문에 같은 버전으로 진행한다.
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>
<!-- 특정 엔티티를 찾지 못하는 경우 패키지를 적어서 추가함. -->
<class>com.example.jpa.jpademo.Member</class>
<!-- 필수 속성 -->
<property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="1234"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/study"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/>
<!-- 옵션 -->
<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>
JPA를 사용하기 위해 위와 같이 설정 파일을 작성하고
위치는
위 이미지와 같이 resources -> META-INF -> persistence.xml을 위치시킨다.
그래야 JPA가 제대로 동작한다.
프로퍼티 항목에 각자 mysql의 유저 이름과 비밀번호를 입력해주면 된다.
참고로
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/>
이 부분은 사용하는 DB에 따라 바꿀 수 있다.
이 부분만 오라클로 바꾸면 모든 SQL이 오라클에 맞게 매핑되어 트랜잭션 실행이 가능하다.
JPA는 특정 DB에 종속적인 기술이 아니어서 어떤 DB를 사용하는지 명시해주어야 한다.
JPA 기본 구조
JPA는 기본적으로 애플리케이션 - JPA - JDBC - DB와 같은 형태로 되어있으며,
JDBC와 어플리케이션 사이에서 기존에 직접 개발자가 하던 SQL 쿼리 작성 및 mapper 작업을 대신하고,
JDBC를 사용하여 DB에 쿼리를 전송한다.
그리고, 어플리케이션 실행 시,
EntityManagerFactory라는 것이 필요하며 이는 JPA가 엔티티를 관리하고 SQL에 매핑할 객체들을 관리하는 오브젝트가 하나 필요하다.
하나의 트랜잭션당 EntityManagerFactory에서 EntityManager를 할당받고, 트랜잭션을 처리하고 반납한다.
마치 DB의 커넥션과 비슷하다.
저장 테스트
일단 저장할 객체가 있어야 하기 때문에 Member Entity를 만들어 본다.
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
import javax.persistence.*;
@Entity
@Table(name = "member")
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name = "name")
private String name;
@Column(name = "age")
private int age;
@Column(name = "dept")
private String dept;
public Member(String name, int age, String dept) {
this.name = name;
this.age = age;
this.dept = dept;
}
}
엔티티 클래스의 간소화를 위해 어노테이션이 많이 붙어있지만 JPA 사용을 위해선 @Entity는 필수로 필요하다.
해당 어노테이션이 붙게 되면 애플리케이션 실행 시, JPA가 관리하는 객체임을 명시한다.
그리고 기본 생성자가 꼭 필요하다.
위 코드는 롬복의
@NoArgsConstructor
로 대체하였다.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
id는 테이블의 키 값이 될 항목이다.
@Id는 해당 객체의 키 값을 의미하고 GeneratedValue 어노테이션은 MySQL의 AI 옵션과 같다.
DB에 데이터가 쌓임에 따라 1씩 증가하는 시퀀스다.
그리고 그 외 다른 항목들은 칼럼과 변수 이름이 같다면 붙여주지 않아도 되지만,
칼럼에는 스네이크 케이스를 쓰고, 객체에는 카멜 케이스를 쓰는 경우가 많아 명시했다.
메인 클래스에 간단하게 코드를 작성해보겠다.
public class JpaDemoApplication {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager(); //EntityManagerFactory를 통해 할당
EntityTransaction ex = em.getTransaction();
ex.begin(); //트랜잭션 시작
Member member = new Member("Tistory", 30, "dev"); //멤버 객체 생성
em.persist(member); //저장이 단 한줄로 끝난다.
ex.commit(); // 커밋
em.close(); //객체 반환
emf.close(); //객체 반환
}
}
일단 첫 번째 줄에서 "hello"는 xml에서 설정한
유닛 이름과 일치시켜준다.
그리고 앞서 JPA의 기본 구조에서 언급했듯이,
엔티티 매니저 팩토리에서 엔티티 매니저를 할당받고 트랜잭션 객체를 얻는다.
em.persist(member)
그리고 Member 객체를 인스턴스 화하고, 생성자에 값을 넣어주고 위 코드 단 한 줄이면 저장이 끝난다.
위 코드를 실행해보면
JPA가 하이버네이트 구현체를 통해 DB에 날린 쿼리가 로그에 표시되고
직접 DB를 확인하여 데이터가 정상적으로 들어갔는지 본다.
그렇다면 조회는 어떻게 할까?
Member member = em.find(Member.class, 1);
find 메서드를 사용하면 된다.
1번째 인자는 객체의 클래스를 넘겨주면 되고, 2번째 인자는 pk값을 넣는다.
값이 잘 조회됐다.
그렇다면 리스트로 받아오려면
이렇게 해주면 된다.
createQuery안에 인자는 JPQL을 작성하면 되는데 이는 Java Persistence Query Language이다.
객체 지향형 쿼리인데 SQL과 많이 다르지 않다.
Member members = em.createQuery("select m from Member m where m.name = :name", Member.class).setParameter("name", "Tistory").getSingleResult();
특정 값을 가진 row를 찾아야 한다면 위와 같이 JQPL을 작성할 수 있다.
수정은 더 말도 안 되게 쉽다.
그냥 객체의 setter를 통해 객체의 값만 바꿔주면 업데이트가 된다.
위 코드를 이용해 Tistory가 이름인 Member를 찾고 이름을 바꿔보겠다.
기존 데이터에서 객체에 값만 바꿨는데 DB에 업데이트된 row를 볼 수 있다.
주의점
JPA는 커밋하는 시점에서 DB에 쿼리를 전송하기 때문에 커밋을 꼭 실행해주어야 한다.
강제적으로 flush 메서드를 호출하여 커밋 메서드 호출 전에 반영은 할 수 있지만, 권장되는 방식은 아니라고 한다.
다음 글에는 엔티티 매핑에 대해 글을 더 작성한다.
'Java' 카테고리의 다른 글
JPA 엔티티와 테이블 매핑 (0) | 2022.03.12 |
---|---|
Java]메서드 오버로딩말고 파라미터를 여러개 받아보자. (0) | 2022.02.23 |
JAVA]배열,ArrayList,List 스트림 정렬 및 타입 변환 방법 정리 (0) | 2022.02.19 |
Java 회전된 문자열인지 확인하기 (0) | 2022.01.30 |
Log4j 보안 이슈 (0) | 2021.12.13 |
댓글