티스토리 뷰

spring

day5 class04 JPA 개념, class05 JPA 환경설정

일상다반ㅅㅏ 2019. 2. 25. 17:23

- ORM(Object-Relation Mapping)은  정확하게 일지하지 않는 자바의 객체와 데이터베이스의 테이블을 매핑해준다.

- 이전까진 스프링 JDBC나 Mybatis를 이용하여 자바 객체와 테이블을 매핑해왔지만 어떤 DB 연동 기술이나 프레임워크를 사용하더라도 SQL 명령어를 자바 클래스나 외부 XML에 작성해야 했다. 그리고 유지보수 과정에서 지속적으로 수정되고 새롭게 추가 된다.

- ORM의 큰 특징은 필요한 SQL을 자동으로 생성한다. 대표적인 ORM 프레임워크는 Hibernate 프레임워크이고 이러한 ORM 프레임워크를 표준화한 것이 JPA다.


1. JPA 특징

- JDBC와 마찬가지로 프레임워크 변경이 가능하다.



2. JPA 프로젝트 설정

2-1 JPA 프로젝트 생성

- new > Maven Project > maven-archetype-quickstart 선택 > group id : com.springbook.biz.board / artifact id : JPAProject / version : 0.0.1-SNAPSHOT / package : com.springbook.biz.board 


2-2. jre system library 버전 설정

- 프로젝트 우클릭 > properties > project facets > convert to faceted form... > jpa > runtimes > jdk1.8~ 선택 > futher configuration available... > JPA Facet창에서 type : Disable Library Configuration > ok

- META-INF/persistence.xml 파일이 생성된다.

- 위의 과정을 해야 하는데 내 sts환경에서는 jpa가 보이지 않아 여기서부턴 소스를 그냥 따라 타이핑했다. 되긴 된다...


2-3. JPA 라이브러리 내려받기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- pom.xml -->
 
<!-- JPA, 하이버네이트 -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>5.1.0.Final</version>
</dependency>
<!-- H2 데이터베이스 -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.196</version>
</dependency>
cs


2-4. 엔티티 클래스 매핑

- board 패키지 우클릭 > new > jpa entity > class name : Board

- 엔티티 클래스의 모든 멤버변수를 private으로 선언한다. 특히 일반적인 프로그램에서는 객체를 식별하기 위해서 유일 식별자를 사용하지는 않지만, 영속 객체가 테이블과 매핑될 때 객체 식별 방법이 필요하므로 유일 식별자를 소유하는 클래스로 작성한다.

> BoardVO객체도 다 private를 썻는데 다른게 있나??

- 이 과정도 역시 목록에서 보이지 않아 그냥 작성했다.

- 이 과정이 된다면 META-INF/persistence.xml에 해당 클래스가 등록된다.

어노테이션 

의미 

@Entity 

엔티티 클래스라고하며, @Entity가 붙은 클래스는 테이블과 매핑된다. 

@Table 

엔티티와 관련된 테이블을 매핑한다. name 속성을 사용하여 BOARD 테이블과 매핑했는데 생략하면 클래스 이름이 테이블 이름과 매핑된다. 

속성 

설명 

name 

매핑될 테이블 이름을 지정한다. 

catalog 

데이터베이스 카탈로그를 지정한다. 

schema 

데이터베이스 스키마를 지정한다. 

uniqueConstraints 

결합 unique 제약조건을 지졍하며, 여러 개의 컬럼이 결합되어 유일성을 보장해야 하는 경우 사용한다.(복합키) 


@Id 

엔티티 클래스의 필수 어노테이션으로서, 특정 변수를 테이블의 기본 키와 매핑한다. 예제에서는 seq 변수를 테이블의 SEQ 칼럼과 매핑했다. @Id가 없는 엔티티 클래스는 JPA가 처리하지 못한다. 

@GeneratedValue 

@Id가 선언된 필드에 기본 키를 자동으로 생성하여 할당할 때 사용한다. 다양한 옵션이 있지만 @GeneratedValue만 사용하면 데이터베이스에 따라서 자동으로 결정된다. H2는 시퀀스를 이용하여 처리한다. 

@Temporal 

날짜 타입의 변수에 선언하여 날짜 타입을 매핑할 때 사용한다. TemporalType의 DATE, TIME, TIMESTAMP 중 하나를 선택할 수 있다. 

@Column

엔티티 클래스의 변수와 테이블의 칼럼을 매핑할 때 사용한다. 일반적으로 엔티티 클래스의 변수 이름과 칼럼 이름이 다를 때 사용하며, 생략하면 기본으로 변수 이름과 칼럼 이름을 동일하게 매핑한다.

- name : 칼럼 이름을 지정한다.

- unique : unique 제약조건을 추가한다. 기본값 false

- nullable : null 상태 허용 여부를 설정한다. 기본값 false

- insertable : 입력 SQL 명령어를 자동으로 생성할 때 이 칼럼을 포함할 것인지를 지정한다. 기본값 ture

- updatable : 수정 SQL 명령어를 자동으로 생성할 때 이 칼럼을 포함할 것인지를 지정한다. 기본값 true, 작성자 같이 다시 덮을 필요 없는거

- columnDefinition : 이 칼럼에 대한 DDL 문을 직접 설정한다.

- length : 문자열 타입의 칼럼 길이를 지정한다. 기본값 255

- precision : 숫자 타입의 전체 자릿수를 지정한다. 기본값 0

- scale : 숫자 타입의 소수점 자릿수를 지정한다. 기본값 0

@Transient 

퀴리로 생성 될때 해당 변수를 매핑 대상에서 제외 시켜준다. 검색조건이나 파일명 같은거 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Board.java
 
@Entity
@Table(name = "BOARD")
public class Board {
 
    @Id
    @GeneratedValue
    private int seq;
    private String title;
    private String writer;
    private String content;
    @Temporal(TemporalType.DATE)
    private Date regDate = new Date();
    private int cnt;
cs

2-5. persistence.xml 파일 작성
- <persistence-unit>은 영속성 유닛이라 하며 데이터베이스당 하나의 영속성 유닛을 사용한다.
- <class>는 스프링 프레임워크나 J2EE 환경에서 JPA를 사용한다면 자동으로 엔티티 클래스를 검색하여 처리하는 기능이 제공되므로 엔티티 클래스들을 일일이 등록할 필요는 없다.
- 스프링 프레임워크와 연동할 때는 데이터 소스가 스프링 설정 파일에 등록되어 있으므로 셩속성 유닛 설정에서는 제거될 수 있다.
- dialect는 방언이라는 의미로 ORM 프레임워크의 가장 중요한 특징이자 장점이다. 애플리케이션 수행에 필요한 SQL 구문을 자동으로 생상한다는 것이다. DBMS에 따라서 키 생성 방식도 다르고 지원되는 함수도 조금씩 다르기 때문에 DBMS가 변경되면 이런 세세한 부분을 개발자가 적절하게 수정해야 한다. JPA는 특정 DBMS에 최적화된 SQL을 제공하기 위해 DBMS마다 다른 dialect를 제공한다. h2뿐만아니라 mysql, oracle 등 다양한 클래스가 있다. 
- JPA 구현체 관련 속성 설정

속성 

의미 

hibernate.show_sql 

생성된 SQL을 콘솔에 출력한다. 

hibernate.format_sql 

SQL을 출력할 때, 일정한 포맷으로 보기 좋게 출력한다. 

hibernate.use_sql_comments 

SQL에 포함된 주석도 같이 출력한다. 

hibernate.id.new_generator_mappings 

새로운 키 생성 전략을 사용한다. 

hibernate.hbm2ddl.auto 

테이블 생성이나 수정, 삭제 같은 DDL 구문을 자동으로 처리할지를 지정한다.

- create : 애플리케이션을 실행할 때, 기존 테이블을 삭제하고 엔티티 클래스에 설정된 매핑 설정을 참조하여 새로운 테이블을 생성한다.(drop -> create)

- create-drop : create와 같지만 애플리케이션이 종료되기 직전에 생성된 테이블을 삭제한다.(drop -> create -> drop)

- update : 기존에 사용 중인 테이블이 있으면 새 테이블을 생성하지 않고 재사용한다. 만약 엔티티 클래스의 매핑 설정이 변경되면 변경된 내용만 반영한다.(alter) 



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- persistence.xml -->
 
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
    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_1.xsd">
    <persistence-unit name="JPAProject">
        <class>com.springbook.biz.board.Board</class>
        <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="false" />
            <property name="hibernate.id.new_generator_mappings" value="true" />
            <property name="hibernate.hbm2ddl.auto" value="create" />
        </properties>
    </persistence-unit>
</persistence>
cs



2-6. 클라이언트 프로그램 작성

- insert와 select 쿼리를 자동으로 생성하여 처리하는것을 콘솔로 확인할 수 있다.

- persist == insert

- merge == update

- remove == delete

- find == select one

- createQuery == select list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// BoardServiceClient.java
 
package com.springbook.biz.board;
 
import java.util.List;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
 
public class BoardServiceClient {
 
    public static void main(String[] args) {
        // EntityManager 생성
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("JPAProject");
        EntityManager em = emf.createEntityManager();
        // Transaction 생성
        EntityTransaction tx = em.getTransaction();
        try {
            // Transaction 시작
            tx.begin();
            
            Board board = new Board();
            board.setTitle("JPA");
            board.setWriter("JPA");
            board.setContent("JPA 왜 나만 안돼");
            
            // 글 등록
            em.persist(board);
            
            // 글 목록 조회
            String jpql = "select b from Board b order by b.seq desc";
            List<Board> boardList = em.createQuery(jpql, Board.class).getResultList();
            for(Board brd : boardList) {
                System.out.println("---> " + brd.toString());
            }
            // Transaction commit
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            // Transaction rollback
            tx.rollback();
        } finally {
            em.close();
        }
        emf.close();
    }
}
 
cs