JPA에 대한 소개, 활용방안, Spring 프레임워크와 통합
Table of Contents
- JPA 란?
- JPA에서 주목할 부분
- 간단한 예제를 통한 JPA 소개
- EntityManager API
- Entity Lifecycle 과 Persistence Context
- META-INF/persistence.xml
- JAVA SE 에서 JPA 사용하기
- Spring 프레임워크와 통함
- 참고문헌
JPA 란?
JPA(Java Persistence API)는 EJB 2.x에서 DB에 접근하기 위해 사용되었던 Entity Bean을
JSR-220(Enterprise JavaBeans 3.0)에서 대체하는 새로운 기술이다.
JPA는 Entity Bean과는 아주 다른 POJO(Plain Old Java Object) 기반의 ORM(Object-Relational Mapping)
프로그래밍 모델을 제공하며 기존에 존재하던 Hibernate와 같은 ORM솔루션과 유사하다.
또한 EJB3.0에 국한되지 않은 범용적인 기술로 만들어 졌기 때문에 JAVA EE 와 SE 환경에서
모두 사용 할 수 있으며 JAVA SE 5.0 Annotation을 사용하여 Java 객체에서 RDB로 Mapping하는 방법을
단순화시켰다.
JPA에서 주목할 부분
- POJO 기반의 단순한 Persistence Model
- 표준화된 O/R 매핑
- Annotation AND/OR XML 사용
- 디폴트 규칙 적용으로 대부분의 경우 별도의 O/R 매핑을 지정할 필요가 없음.
- 객체간의 상속관계 지원
- EJBQL에 비해 확장된 쿼리 언어
- Bulk Update/Delete, Subquery, Native Query 지원
- Java EE And Java SE 환경 모두 지원
- 프로바이더(Provider)를 플러그인 해서 사용 가능
간단한 예제를 통한 JPA 소개
- 예제에 사용될 데이터 모델
그림1> OrderApp 데이터 모델
- DB의 데이터 모델은 JPA에서 Entity로 표현된다.
- Entity는 데이터 모델을 객체 모델로 표현한 것으로 EJB 2.x의 Entity Bean과 유사하지만
JPA에서는 별도의 인터 페이스 없이 POJO class로 표현된다. - class에서는 Entity임을 표시하기 위해 @Entity Annotation이 달려 있다.소스1> Customer.java — Customer 테이블과 Mapping
/** * Customer entity. */ @Entity //---이 클레스가 Entity임을 표시 ---// @Table(name="ORDERAPP_CUSTOMER") @NamedQuery(name="findCustomerByName", query="select c from Customer c where c.name=:name") public class Customer implements Serializable { // ---Annotation이 필더에 달려 있을 경우 Field access type이라 한다.--- // @Id private String id; private String name; private String address; // ---cascade가 아래와 같이 설정되면 Customer entity가 생성/삭제 될때 --- // // ---해당되는 entity도 같이 생성/삭제된다. --- // @OneToMany(mappedBy="customer", cascade={CascadeType.PERSIST, CascadeType.REMOVE}) private List<Orders> orders; // ---Entity에서 Defult constructor 는 필수이다--- // public Customer(){} public Customer(String id, String name, String address) { this.id = id; this.name = name; this.address = address; } public String getId() {return id;} public String getName(){return name;} public String getAddress(){return address;} public void setAddress(String address){this.address = address;} public List<Orders> getOrders(){return orders;} public void setOrders(List<Orders> orders) {this.orders = orders;} public void addOrder(Orders o){ if(orders == null) orders = new LinkedList<Orders>(); orders.add(o); } }
- Entity는 기본적으로 하나의 Entity에 하나의 DB 테이블에 매핑된다.
(M:N 관계가 존재시는 테이블이 하나더 존재한다)소스2> Item.java — Item 테이블과 Mapping/** * Item entity. */ @Entity @Table(name="ORDERAPP_ITEM") @NamedQuery(name="findAllItems", query="select i from Item i") public class Item implements Serializable { private long id; private String name; private double price; public Item() {} public Item(String name, double price){ this.name = name; this.price = price; } // ---Annotation이 프로퍼티에 달려 있을 경우 Property access type이라 한다.--- // @Id @GeneratedValue 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;} public double getPrice() {return price;} public void setPrice(double price) {this.price = price;} }
소스3> Order.java — Order 테이블과 Mapping/** * Order entity. */ @Entity @Table(name="ORDERAPP_ORDERS") public class Orders implements Serializable { @Id @GeneratedValue private Long id; @ManyToOne private Customer customer; @ManyToMany @JoinTable(name="ORDERAPP_ORDERS2ITEMS") private Collection<Item> items; public Orders() {} public Long getId() {return id;} public void setId(Long id) {this.id = id;} public Customer getCustomer(){return customer;} public void setCustomer(Customer c){this.customer = c;} public Collection<Item> getItems(){return items;} public void setItems(Collection<Item> items){this.items = items;} }
- 예제 소스에 사용된 O/R 매핑 표준 Annotation
- Logical annotation
- @id - Primary Key에 해당하는 ID필드임을 표시한다.
- @GeneratedValue - DB에 의해서 값이 자동으로 생성되는 필드임을 표시한다.
- Physical annotation
- @Table - Table mapping
- Logical annotation
- 예제 소스에 사용된 Relationships 표준 Annotation
- @OneToMany - 1:N 관계를 표현하기 위한 필드임을 표시한다.
- @ManyToOne - N:1 관계를 표현하기 위한 필드임을 표시한다.
- @ManyTOMany - M:N 관계를 표현하기 위한 필드임을 표시한다.
- @JoinTable - 관계를 표현할 때 어떤 테이블을 사용할 것인지에 대해 표시한다.
- 예제 소스에 사용된 Query 표준 Annotation
- @NamedQuery - Static query 를 entity class에 직접 명기
- Cascaed Type (연쇄작업 설정)
- CascadeType.PESIST - 개체 삽입
- CascadeType.MERGE - 업데이트
- CascadeType.REMOVE - 삭제
- CascadeType.REFRESH - 리플레쉬
- CascadeType.ALL - 전부 적용
EntityManager API
- DB에서 엔티티를 가져오거나 생성, 삭제 하는 일은 모두 EntityManager API를 통해서 이루어진다.
EntityManager는 누가 관리되는가에 따라 크게 두가지로 구분된다.- Container-managed EntityManager
JAVA EE 환경에서만 사용되며 컨테이너가 EntityManager 인스턴스의 Lifecycle을 관리한다.
JTA 트랜잭션에 자동으로 연결되기 때문에 JAVA EE 환경에 유용하다.- Container-managerd 에서 EntityManager 객체를 가져오는 방법
Container-managerd EntityManager의 경우 인젝션이나 lookup을 통해서 얻어올 수 있다.- 인젝션 이용. 해당 필드나 메소드에 @PersistenceContext표기
@PersistenceContext private EntityManager em;
- lookup 이용. Class에 @PersistenceContext를 표기해 EntityManager를
ENC(Environment Naming Context) 네임스페이스에 등록@PersistenceContext(name="em", unitName="HR") public class HelloBean implements Hello { // ... InitialContextic = new InitialContext(); EntityManager em = (EntityManager)ic.lookup("java:comp/env/em");
- 인젝션 이용. 해당 필드나 메소드에 @PersistenceContext표기
- Container-managerd 에서 EntityManager 객체를 가져오는 방법
- Application-managerd EntityManager
JAVA EE, JAVA SE 두 환경 모두 사용되며 개발자가 EntityManager 인스턴스의 Lifecycle을 직접 관리하며
트랜젝션도 직접 관리해야 한다.- Application-managerd 에서 EntityManager 객체를 가져오는 방법
Application-managerd EntityManager의 경우 EntityManagerFactory API를 통해서 얻어올 수 있다.@PersistenceContext private EntityManagerFactory emf; // ... EntityManager em = emf.createEntityManager();
- Application-managerd 에서 EntityManager 객체를 가져오는 방법
- Container-managed EntityManager
- Entity 객체를 만드는 것은 일반 JAVA 객체를 생성하듯이 new 생성자를 사용하면 되며 이를
DB에 저장하려면 다음과 같이 Entitymanager.persist() 메소드를 호출해야 한다.Customer customer = new Customer(id, name, address); em.persist(customer);
- DB에서 하나의 Entity 객체를 가져오려면 EntityManager.find() 메소드를 호출한다.
이경우에는 Primary Key에 해당하는 값을 통해서 Entity를 가져올 수 있다.Customer e = em.find(Customer.class, id);
- Java Persistence Query Language를 이용하여 모든 Entity들을 가져오거나 특정 필드값을 비교하여
Entity를 가져올 수 있다.Query query = em.createQuery("select c from Customer c"); List list = query.getResultList();
- Entity 필드를 수정하려면 Entity 객체의 필드를 수정하면 된다.
해당 필드는 EntityManager.flush() 를 호출하거나 트렌잭션이 commit 될때 DB와 동기화 된다.Customer e = em.find(Customer.class, id); c.setAddress(address);
- Entity를 삭제할 때에는 EntityManager.remove() 를 호출한다.
em.remove(customer);
- Entity간의 관계(Relationship)는 다른 Entity에 대한 레퍼런스로 표현된다.
관계를 수정할려면 레퍼런스 값을 바꾸면 된다.
ManyTOMany나 OneToMany 같이 여러 개의 Entity와 관계를 맺고 있을때는 Collection객체로 표현된
값을 바꾼다.public class Customer //... @OneToMany(mappedBy="customer", cascade={CascadeType.PERSIST, CascadeType.REMOVE}) private List<Orders> orders; public void addOrder(Orders o) { orders.add(o); }
Entity Lifecycle 과 Persistence Context
- Persistence Context ?
현재 관리되고 있는 Entity 인스턴스들의 집합을 말함.
Entity 인스턴스의 상태는 EntityManager 오퍼레이션과 밀접한 관련이 있다. - EntityManager 오퍼레이션에 따른 Entity의 Lifecycle
그림2> EntityManager 오퍼레이션에 따른 Entity의 Lifecycle- NEW - Entity instance가 생성되었을 때의 상태로 DB와 연결되어 있지 않다.
EntityManager.perisit()를 통해서 DB에 저장되며 Managed 상태가 된다. - Managed - Entity instance가 Persistence Context에 의해서 관리되는 상태로 Entity 인스턴스
값의 변화가 DB에 반영된다.
Container-managed EntityManager의 경우 트랜잭션이 종료되면 모든 Entity는 detached 상태가 된다. - Detached - Entity instance가 Persistence Context에 의해서 더이상 관리되지 않는 상태로 DB와 연결되지 않는다.
EntityManger.merge()를 통해서 다시 managed 상태로 만들 수 있다. - Removed - DB에서 해당 Entity가 삭제될 상태이다.
- NEW - Entity instance가 생성되었을 때의 상태로 DB와 연결되어 있지 않다.
Java Persistence Query Language
EJBQL과 비슷하지만 JPQL은 Bluk Update/Delete, Subquery, Native Query(DB-specific SQL) 들을 지원한다.
- Query API
Query Object는 EntityManager의 createQuery()를 이용해 생성한다.- setParameter(), setMaxResults(), setFirstResult()
- getResultList(), getSingleResult(), executeUpdate()
- Dynamic Query
동적으로 생성하여 사용하는 queryQuery query = em.createQuery("select c from Customer c where name=:name"); query.setParameter("name", name); List list = query.getResultList();;
- Static Query
Entity Class에 선언해 두고 필요할때 마다 불러 쓰는 query
@NamedQuery 와 @NamedNativeQuery 가 있다.@Entity @NamedQuery(name="findCustomerByName", query="select c form Customer c where c.name=:name") public class Customer { //.... } Query query = em.createNamedQuery("findCustomerByName"); query.setParameter("name", name); List list = query.getResultList();
- An extension of EJBQL
- Projection list
- Explicit JOINS
- Subqueries
- GROUP BY, HAVING
- EXISTS, ALL, SOME/ANY
- Bulk UPDATE, DELETE
META-INF/persistence.xml
- Entity Class를 패키징 할때 META-INF/persistence.xml 디스크립터가 꼭 필요하다.
- persistence.xml은 관련 Entity Class들과 이에 대한 DB DataSource 설정 등과 같이
Persistence-Unit에 대한 정보를 담고 있다. - Persistence-Unit은 Entity를 묶는 단위이며 이것은 하나의 DB에만 매핑된다.
- Persistence-Unit의 UnitName은 EntityManager or EntityManagerFactory를 얻을때 사용된다.
- persistence.xml 예제
<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"> <persistence-unit name="orderunit"> <jta-data-source>jdbc/sample</jta-data-source> <properties> <!-- vendor-specific --> <!-- <property name="toplink.ddl-generation" value="create-tables"/> --> </properties> </persistence-unit> </persistence>
JAVA SE 에서 JPA 사용하기
- JAVA SE 환경에서 사용 할 때는 Application-managed EntityManager 만 사용할 수 있다.
- JDBC 드라이버에 국한된 로컬(resource-local) 트랜잭션만 쓸 수 있다.
- EntityTransaction API를 이용하여 직접 트랜잭션 컨트롤을 해주어야 함.
- EntityManagerFactory를 얻는 방법 - javax.persistence.spi.Persistence Bootstrap API 사용
EntityManagerFactory emf = Persistence.createEntityManagerFactory("orderunit"); EntityManager em = emf.createEntityManager(); //... EntityTransaction tx = em.getTransaction(); tx.begin(); em.persist(customer); tx.commit(); //... em.close(); emf.close();
- META-INF/persistence.xml
JAVA EE 환경과는 달리 모든 Entity 클래스를 명시적으로 나열해 주어야 한다.//... <persistence-unit name="orderunit"> <class>orderapp.entities.Customer</class> <class>orderapp.entities.Item</class> <class>orderapp.entities.Order</class> //...
Spring 프레임워크와 통함
아래 문서에서 정보를 얻으시기 바랍니다
Spring과 Java Persistence API의 사용
The Spring Framework - Reference documentation
참고문헌
- Dev2Dev 에서 가져온 'Spring과 Java Persistence API의 사용' 입니다.
- Micro Software 7월호 - POJO로 돌아온 EJB 3.0과 자바 퍼시스턴스 API
- JSRs: Java Specification Requests
- 2006 JavaOne Online Technical Sessions - Java Persistence API
- The Java Persistence API - A Simpler Programming Model for Entity Persistence
- http://java.sun.com/developer/technicalArticles/J2EE/jpa/
문서에 대하여
최초작성자 : 박동일
최초작성일 : 2006년 7월 29일
버전 : 1.0
문서이력 :
- http://java.sun.com/developer/technicalArticles/J2EE/jpa/
- 2006년 7월 29일 박동일 문서 최초 생성 : 구글검색중 이 스터디 주제와 꼭 맞는 뉴스를 찾고는 기쁜나머지 문서생성함 ㅎ
- 2006년 9월 29일 박동일 : 참고문헌 추가
- 2006년 11월 7일 박동일 : 발표버젼 완료
출처 - http://www.javajigi.net/pages/viewpage.action?pageId=5924
'DB > ORM' 카테고리의 다른 글
jpa primary key(id) composite(기본키 복수 지정) (0) | 2013.03.12 |
---|---|
Entity와 Value Object 그리고 Service (0) | 2013.01.04 |
도메인의 퍼시스턴스 계층을 담당할 Repository의 탄생(DAO vs REPOSITORY) (0) | 2013.01.04 |
JPA 2.0의 형식이 안전한 동적 쿼리 (0) | 2013.01.02 |
jpa wiki (0) | 2012.12.31 |