JPA는 Java 진영의 ORM 기술 표준으로, 애플리케이션과 JDBC API 사이에서 동작합니다.
💡 ORM(Object-Relational Mapping)은 객체와 관계형 데이터베이스를 매핑시켜줍니다. 객체와 관계형 데이터베이스의 테이블을 매핑해서 두 패러다임간의 불일치 문제를 개발자 대신 해결해줍니다.
JPA와 Hibernate의 차이
JPA는 Java ORM 기술에 대한 API 표준 명세입니다. 즉, 인터페이스들을 모아둔 것이므로 JPA를 사용하기 위해서는 JPA를 구현한 ORM 프레임워크를 사용해야 합니다. 이에는 Hibernate, EclipseLink, DataNucleus 가 있는데, Hibernate가 가장 대중적으로 사용됩니다.
Hibernate는 자바 진영에서 가장 많이 사용되는 ORM 프레임워크로, 대부분의 패러다임 불일치 문제를 해결해주는 성숙한 ORM 프레임워크입니다.
Spring Data JPA는 JPA를 한 단계 더 추상화 시킨 Repository라는 인터페이스를 제공하여, 개발자가 보다 쉽게 JPA를 사용할 수 있게 해줍니다.
💡 과거, 자바 엔터프라이즈 애플리케이션을 만들기 위해 자바 빈즈(EJB)라는 기술 표준을 만들었고, 그 안에는 엔티티 빈(Entity Bean)이라는 ORM도 포함되어 있었습니다. 그러나 이 기술은 자바 엔터프라이즈 애플리케이션 서버가 없으면 동작하지 못했으며, 지나치게 어렵고 복잡했습니다. 이 때 하이버네이트(Hibernate)라는 오픈소스 ORM 프레임워크가 등장했는데, 가볍고 실용적인데다 기술 성숙도도 높았습니다. 특히나 엔터프라이즈 애플리케이션 서버가 없어도 동작할 수 있었다는 것이 큰 장점이었습니다. 결국 EJB 3.0에서 하이버네이트를 기반으로 새로운 자바 ORM 기술 표준이 만들어졌는데 그것이 JPA입니다.
JPA 사용해보기
1) 의존성 추가
org.hibernate:hibernate-entitymanager, com.h2database:h2를 설치해줍니다.
2) 테스트시 사용할 데이터베이스 환경 구축
나는 도커 컨테이너로 H2를 실행시켰습니다.
docker run -d -p 1521:1521 -p 8082:8082 -v Users/yhlee/Documents/docker-h2 -e H2_OPTIONS='-ifNotExists' --name=h2 oscarfonts/h2
- -d: 도커 컨테이너를 백그라운드에서 실행
- -p: 도커 컨테이너와 로컬 포트를 연결하는 옵션
- -v: h2 컨테이너 생성 후 데이터가 쌓이면 로컬 피씨 상에 저장하는 경로 지정
- H2_OPTIONS=”ifNotExists”: 새로운 테이블 생성이 가능하게 해줍니다.
- 특정 버전의 이미지를 실행시킬 경우 이미지명 뒤에 ':버전'을 명시해야 합니다.
run command가 실행되면 이미지를 가져와 설치하게 됩니다.
- docker exec -it h2 /bin/bash: h2 컨테이너에 접속해 콘솔 실행
yh-MacBookPro:Documents yhlee$ docker exec -it h2 /bin/bash
root@3710cd4aef05:/opt/h2-data# cd /opt/h2/bin
root@3710cd4aef05:/opt/h2/bin# ls -al
total 2496
drwxr-xr-x 2 root root 4096 Mar 21 2022 .
drwxr-xr-x 6 root root 4096 Mar 21 2022 ..
-rw-r--r-- 1 root root 2531600 Jan 17 2022 h2-2.1.210.jar
-rw-r--r-- 1 root root 98 Jan 17 2022 h2.bat
-rw-r--r-- 1 root root 109 Jan 17 2022 h2.sh
-rw-r--r-- 1 root root 105 Jan 17 2022 h2w.bat
root@3710cd4aef05:/opt/h2/bin# java -cp h2-2.1.210.jar org.h2.tools.Shell
데이터베이스에 접속할 수 있게 됩니다.
- Docker 명령어 정리
- 특정 버전의 이미지 받기(생략시 기본적으로 latest가 붙습니다.)
$ docker pull [image name]:[version]
- 이미지 목록 조회
$ docker images
- 이미지 삭제
$ docker rmi [image ID]
- 해당 이미지의 컨테이너까지 삭제
$ docker rmi -f [image ID]
- 실행중인 컨테이너 조회
$ doc docker ps
- 전체 컨테이너 조회
$ docker ps -a
- 컨테이너 종료
$ docker stop [container ID]
- 컨테이너 삭제
$ docker rm [container ID]
3) 프로젝트에 연결하기
spring:
datasource:
url: jdbc:h2:tcp://localhost:1521/test
username: sa
password:
driver-class-name: org.h2.Driver
4) Entity 생성
User 클래스와 ‘MEMBER’ 테이블 간의 맵핑 정보를 설정해주는 어노테이션들을 추가합니다.
- @Entity : 이 클래스가 테이블과 매핑되는 엔티티 클래스임을 알려줍니다.
- @Table : 이 엔티티 클래스가 매핑될 테이블 정보를 알려줍니다. (이 어노테이션 생략 시 엔티티 명을 이용해 테이블명과 매핑합니다.)
- @Id : 기본키에 매핑한다. (@Id가 사용된 필드를 식별자 필드라고 합니다.
- @Column : 컬럼에 매핑한다. 생략할 경우 필드명으로 컬럼명과 매핑합니다. Cf. 데이터베이스가 대소문자 구분을 하지 않을 경우 상관없지만, 대소문자 구분을 하는 경우에는 명시적으로 정의해주어야 합니다.
- @GeneratedValue : 기본키 생성 전략
import javax.persistence.*;
@Table(name = "MEMBER")
@Entity
public class Member {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "age")
private Integer age;
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String username) {
this.name = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
4) persistence.xml 설정
JPA는 persistence.xml을 통해 필요한 설정 정보를 관리할 수 있습니다. 이 파일이 [Class Path]/META-INF/persistence.xml 에 있으면 별도의 설정 없이 JPA가 인식할 수 있습니다.
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
<persistence-unit name="default">
<class>com.example.learnjpa.domain.Member</class>
<properties>
<!-- MariaDB로 대체 -->
<property name="javax.persistence.jdbc.driver" value="org.mariadb.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="yhlee"/>
<property name="javax.persistence.jdbc.password" value="1"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mariadb://localhost:3306/LearnJPA?serverTimezone=Asia/Seoul"/><!-- &characterEncoding=UTF-8 -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MariaDBDialect"/>
<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.id.new_generator_mappings" value="true"/>
</properties>
</persistence-unit>
</persistence>
- Cf. javax.persistence 로 시작하는 속성은 JPA 표준 속성으로 특정 구현체에 종속되지 않습니다. 반대로 hibernate 로 시작하는 속성은 Hibernate 전용 속성이므로 하이버네이트에서만 사용 가능합니다.
- Dialect : 데이터베이스가 제공하는 SQL 문법과 함수가 조금씩 다릅니다. JPA에서는 SQL 표준을 지키지 않거나 특정 데이터베이스만의 고유한 기능을 JPA에서는 방언(Dialect)이라고 하고, 이를 지원해줍니다. (특정 데이터베이스에 종속적인 기능을 덜 사용할 수록 데이터베이스 교체가 쉽습니다.)
- 방언 속성의 값으로 부여하는 값은 표준화되어있지 않습니다.
5) EntityManager 생성해 테이블에 접근하기
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import static org.junit.jupiter.api.Assertions.*;
class MemberTest {
static EntityManagerFactory factory;
@BeforeAll
@DisplayName("init")
static void initEntityManagerFactory() {
factory = Persistence.createEntityManagerFactory("default");
}
@Test
@DisplayName("save member")
void saveMember() {
EntityManager em = factory.createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
executeLogic(em);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
} finally {
em.close();
}
}
private static void executeLogic(EntityManager em) {
Member member = new Member();
// member.setId(0L); // @GeneratedValue값이 존재하기 때문에 값을 부여하면 Exception 발생
member.setName("yhlee");
member.setAge(28);
em.persist(member);
// em.remove(member);
}
}
'Backend > JPA' 카테고리의 다른 글
JPA) Example, ExampleMatcher 사용해 조건에 맞는 값 조회하기 (0) | 2024.07.05 |
---|---|
JPA) 지연 로딩 성능 최적화(N + 1 문제 해결, fetch 조인) (0) | 2024.05.25 |
JPA) 영속성 컨텍스트(Persistence Context) (0) | 2024.05.24 |
Springboot + JPA) 페이지네이션(Pagenation)하기 - Pageable, PageRequest, Page (0) | 2024.04.17 |