JPA 2강 - JPA기초와 매핑
JPA에서 중요한 게 2가지가 있다.
1. 객체와 RDB를 중간에서 매핑하는 과정(설계)
2. JPA 동작 원리(영속성 컨텍스트)
Hello JPA
H2 데이터베이스
- http://www.h2database.com/
- 최고의 실습용 DB (테스트용으로 쓰기 좋다.)
- 가볍다. (1.5M)
- 웹용 쿼리툴 제공
- MySQL, Oracle 데이터베이스 시뮬레이션 기능
- 시퀀스, AUTO INCREMENT 기능 지원
메이븐 설정
- https://maven.apache.org/
- 자바 라이브러리, 빌드 관리
- 라이브러리 자동 다운로드 및 의존성 관리
객체 매핑하기
- @Entity : JPA가 관리할 객체(엔티티라고 한다.)
@Entity
public class Member {
@Id
private Long id;
private String name;
...
}
- @Id : DB PK와 매핑할 필드
create table Member (
id bigint not null,
name varchar(255),
primary key (id)
)
persistence.xml
- JPA 설정 파일
- /META-INF/persistence.xml 위치
- javax.persistence로 시작 : JPA 표준 속성
- hibernate로 시작 : 하이버네이트 전용
persistence.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
- <persistence-unit name="jpabook">
- <properties>
- <!-- 필수 속성 -->
- <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
- <property name="javax.persistence.jdbc.user" value="sa"/>
- <property name="javax.persistence.jdbc.password" value=""/>
- <property name="javax.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.id.new_generator_mappings" value="true" />
- <!--<property name="hibernate.hbm2ddl.auto" value="create" />-->
- </properties>
- </persistence-unit>
- </persistence>
persistence.xml에는 데이터베이스 접속정보, 옵션등을 넣어주면 된다. 라인 9 ~ 12는 데이터베이스 접속정보이다. property 태그의 name 속성을 "javax.persistence"로 시작하는 것은 JPA에서 표준화 한 것이고, "hibernate.dialect"는 하이버네이트에서만 쓸 수 있는 것이다. "dialect"는 방언이라는 뜻이다.
데이터베이스 방언
- JPA는 특정 데이터베이스에 종속적이지 않은 기술
- 각각의 데이터베이스가 제공하는 SQL 문법과 함수는 조금씩 다르다.
- 가변문자 : MySQL은 VARCHAR, Oracle은 VARCHAR2
- 문자열을 자르는 함수 : SQL 표준은 SUBSTRING(), Oracle은 SUBSTR()
- 페이징 : MySQL은 LIMIT, Oracle은 ROWNUM
- 방언 : SQL 표준을 지키지 않거나 특정 데이터베이스만의 고유한 기능
예를 들어 JPA에서 페이지 API를 호출하면 데이터베이스 Dailect에 따라서 완전히 다른 쿼리가 나온다. 그거를 JPA를 쓰기전에는 개발자가 DB마다 작성을 해줬지만 Dialect가 다 해준다.
데이터베이스 방언
- hibernate.dialect 속성에 지정
- H2 : org.hibernate.dialect.H2Dialect
- Oracle 10g : org.hibernate.dialect.Oracle10gDialect
- MySQL : org.hibernate.dialect.MySQL5InnoDBDialect
- 하이버네이트는 45가지 방언 지원
실습
h2 데이터베이스 쿼리툴을 아래와 같이 띄운다.
테이블을 생성한다.( ctrl + enter로 쿼리 실행)
member 테이블 생성 후 아래와 같이 select 쿼리를 날려 아래와 같이 결과가 나오면 된다.
이번에는 이클립스에서 IDE를 띄워보자.
그 다음 Package Explorer 빈 공간에서 마우스 우클릭 > NEW > Other > Maven 검색 > Maven Project 선택 후 Next > Create a simple project 체크 Next > Group.id : hello.jpa / Artifact.id : hello.jpa Finish
완료되면 hello.jpa라는 프로젝트가 만들어진다. 표준 메이븐 아키텍처 구조가 생성이 된 것이다.
pom.xml 파일을 열고 아래와 같이 작성 후 저장하자.
pom.xml
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>hello.jpa</groupId>
- <artifactId>hello.jpa</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <dependencies>
- <dependency>
- <groupId>org.hibernate</groupId>
- <artifactId>hibernate-entitymanager</artifactId>
- <version>5.3.7.Final</version>
- </dependency>
- <dependency>
- <groupId>com.h2database</groupId>
- <artifactId>h2</artifactId>
- <version>1.4.197</version>
- </dependency>
- </dependencies>
- </project>
라인 9 "hibernate-entitymanager"를 넣으면 나머지 라이브러리가 자동으로 해결된다. 라인 10에서는 5.3.7.Final 버전을 사용하고 있는데 이 버전은 내부적으로 JPA 2.1 버전을 쓴다. 라인 13 ~ 15는 H2 데이터베이스 라이브러리이다. 라인 15에서 버전이 1.4.197로 되어 있는데 로컬에 다운받은 H2 버전과 맞지 않으면 실행이 안될 수 있다.
위와 같이 작성후 아래와 같이 라이브러리를 정상적으로 다운로드 했는지 확인해보자.
pom.xml에 Dependency Hierachy 탭을 보자. "hibernate-entitymanager" 라이브러리가 아래에서 보는 것과 같이 무수히 많은 라이브러리를 같이 다운받은 것을 확인할 수 있다. 간단히 보자면 "hibernate-core"와 "javax.persistence-api"(JPA 인터페이스)를 다운받은 것을 알 수 있다.
아래 "src/main/java" 경로에 "hellojpa"라는 패키지를 생성하자. 그리고 "hellojpa" 패키지에 "Main" 클래스를 생성하자.
Main 클래스 안에 Main 메서드를 만든 후 잘 실행되는지 실행해보자. 그리고 "hellojpa" 패키지에 "entity"라는 패키지를 만들고 그 안에 Member 클래스를 만들자.
Member 클래스를 작성하자.
- package hellojpa.entity;
- import javax.persistence.Entity;
- import javax.persistence.Id;
- @Entity
- public class Member {
- @Id
- private Long id;
- private String name;
- public Long getId() {
- return id;
- }
- public void setId(Long id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
라인 6 @Entity 어노테이션은 아래와 같이 javax.persistence 패키지를 선택해야 한다. 라인 9 @Id 어노테이션도 마찬가지로 같은 패키지를 선택해야한다.(라인 3, 4참고). 마지막으로 라인 1 패키지경로가 같은지 확인하자.
persistence.xml 파일을 만들자. JPA가 실행할 때 이 파일을 읽어서 DB에 접속한다던지 일을 하기 때문에 있어야 한다. 아래와 같이 persistence.xml 파일을 만들자.
persistence.xml 파일 안에 아래와 같이 작성하자.
persistence.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.2">
- <persistence-unit name="hello">
- <properties>
- <!-- 필수 속성 -->
- <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
- <property name="javax.persistence.jdbc.user" value="sa"/>
- <property name="javax.persistence.jdbc.password" value="1234"/>
- <property name="javax.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>
애플리케이션 개발
- 엔티티 매니저 팩토리 설정
- 엔티티 매니저 설정
- 트랜잭션
- 비즈니스 로직(CRUD)
엔티티 매니저 설정
JPA는 Persistence 클래스를 통해서 persistence.xml 설정정보를 읽은 후 EntityManagerFactory를 만든다. EntityManagerFactory는 EntityManager를 만든다. 트랜잭션 단위로 업무를 처리할 때 EntityManager를 새로 만들어야 한다.
Main.java
- package hellojpa;
- import javax.persistence.EntityManagerFactory;
- import javax.persistence.Persistence;
- public class Main {
- public static void main(String[] args) {
- EntityManagerFactory emf =
- Persistence.createEntityManagerFactory("hello");
- System.out.println("Hello");
- }
- }
Main 클래스에서 라인 9, 10과 같이 코드를 추가하자. 라인 10에 Persistenct.createEntityManagerFactory("hello") 코드에서 createEntityManagerFactory() 메서드에 매개변수로 "hello"를 넘기고 있다. 이는 아래 persistence.xml 파일에서 라인 3 <persistence-unit name="hello"> 태그의 name 속성값인 "hello"를 나타낸다. 즉, 메인 클래스에서 "hello"라는 이름으로 persistence.xml 파일에 있는 설정정보를 가져와서 로딩을 하겠다는 의미이다.
persistence.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.2">
- <persistence-unit name="hello">
위와 같이 Main 클래스를 작성 후 실행해보자.
일단 위와 같이 나오면 정상적으로 실행된 것으로 보면 된다. 실행 후 종료가 안될 것이다. Main 클래스에 아래와 같이 라인 5 emf.close(); 코드를 추가하자.
- public static void main(String[] args) {
- EntityManagerFactory emf =
- Persistence.createEntityManagerFactory("hello");
- System.out.println("Hello");
- emf.close();
- }
그리고 Main 클래스를 실행하면 정상적으로 종료되는 걸 확인할 수 있다.
EntityManagerFactory는 서버를 띄울 때 한번 로딩한다. 그리고 유저가 접근하여 비지니스 로직이 실행될 때마다 EntityManagerFactory에서 EntityManager를 꺼내서 쓴다.
Main 클래스를 아래와 같이 작성하고 실행해보자.
- package hellojpa;
- import javax.persistence.EntityManager;
- import javax.persistence.EntityManagerFactory;
- import javax.persistence.EntityTransaction;
- import javax.persistence.Persistence;
- import hellojpa.entity.Member;
- public class Main {
- public static void main(String[] args) {
- EntityManagerFactory emf =
- Persistence.createEntityManagerFactory("hello");
- EntityManager em = emf.createEntityManager();
- //JPA의 모든 활동은 트랜잭션 안에서 이루지기 때문에 아래와 같이 트랜잭션을 얻는다.
- EntityTransaction tx = em.getTransaction();
- //트랜잭션이 시작된다. 데이터베이스에 접근해서 커넥션을 가지고 온 후 실제 트랜잭션을 시작한다.
- tx.begin();
- //member 객체를 저장해보자.
- Member member = new Member();
- member.setId(100L);
- member.setName("안녕하세요");
- //persist는 영구저장하다라는 의미이다.
- em.persist(member);
- tx.commit();
- //EntityManager는 쓰고나서 꼭 닫아줘야 한다.
- em.close();
- emf.close();
- }
- }
라인 31 em.close() 와 같이 EntityManager를 꼭 닫아줘야 한다. 데이터베이스 리소스를 쓰고 있기 때문이다. 그리고 라인 32 emf.close() 와 같이 웹 어플리케이션이 종료되면 EntityManagerFactory도 닫아줘야 한다.
아래와 같이 INSERT 쿼리가 나가는 걸 확인할 수 있다. H2 Console에서 확인해보자.
MEMBER 테이블을 확인하면 데이터가 정상적으로 생성된 것을 확인할 수 있다.
사실 Main 클래스는 아래와 같이 짜는 게 정상이다. 위에서는 테스트를 위해 축약했다.
- package hellojpa;
- import javax.persistence.EntityManager;
- import javax.persistence.EntityManagerFactory;
- import javax.persistence.EntityTransaction;
- import javax.persistence.Persistence;
- import hellojpa.entity.Member;
- public class Main {
- public static void main(String[] args) {
- EntityManagerFactory emf =
- Persistence.createEntityManagerFactory("hello");
- EntityManager em = emf.createEntityManager();
- //JPA의 모든 활동은 트랜잭션 안에서 이루지기 때문에 아래와 같이 트랜잭션을 얻는다.
- EntityTransaction tx = em.getTransaction();
- //트랜잭션이 시작된다. 데이터베이스에 접근해서 커넥션을 가지고 온 후 실제 트랜잭션을 시작한다.
- tx.begin();
- try {
- //member 객체를 저장해보자.
- Member member = new Member();
- member.setId(100L);
- member.setName("안녕하세요");
- //persist는 영구저장하다라는 의미이다.
- em.persist(member);
- tx.commit();
- } catch (Exception e) {
- tx.rollback();
- } finally {
- //EntityManager는 쓰고나서 꼭 닫아줘야 한다.
- em.close();
- }
- emf.close();
- }
- }
주의
- EntityManagerFactory는 하나만 생성해서 애플리케이션 전체에서 공유
- EntityManager는 쓰레드 간에 공유하면 안된다. (사용하고 버려야 한다. 사용자요청이 오면 쓰고 바로 버려야 한다. 다른 유저가 요청 시 EntityManagerFactory에서 EntityManager를 꺼내서 사용해야 한다.)
- JPA의 모든 데이터 변경은 트랜잭션 안에서 실행
'IT공부 > JPA' 카테고리의 다른 글
JPA 6강 - JPA 내부구조 (0) | 2020.05.22 |
---|---|
JPA 5강 - 양방향 매핑 (0) | 2020.05.21 |
JPA 4강 - 연관관계 매핑 (0) | 2020.05.21 |
JPA 3강 - 필드와 컬럼 매핑 (0) | 2020.05.21 |
JPA 1강 (0) | 2020.05.19 |