해당 포스팅은 김영한님의 JPA프로그래밍을 읽으면서 공부한 내용을 정리하는 포스팅이다.
Spring Boot를 사용한다면 Spring Data JPA 를 바로 사용해도 좋지만 기본 원리는 알고 있다면 이슈 대응이나 기술에 대한 이해도 측면에서 좋을 것 같아서 우선 책에 나온 예제를 토대로 사용 기술을 정리할 예정이다.
(참고로 책에나온 설치과정이나 간단한 설정 부분은 다루지 않는다. 우리는 JPA가 무엇이고 어떻게 사용하며 어떤 클래스들이 있는지 익혀가는데 중점을 두고 포스팅 할 예정이다. )
Index
- 프로젝트 구조
- 엔티티 매니저 설정
- 트랜잭션 관리
- 비즈니스 로직
- JPQL
- 정리
프로젝트 구조
JpaMain.java
package jpabook.start;
import javax.persistence.*;
import java.util.List;
/**
* @author holyeye
*/
public class JpaMain {
public static void main(String[] args) {
//엔티티 매니저 팩토리 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");
EntityManager em = emf.createEntityManager(); //엔티티 매니저 생성
EntityTransaction tx = em.getTransaction(); //트랜잭션 기능 획득
try {
tx.begin(); //트랜잭션 시작
logic(em); //비즈니스 로직
tx.commit();//트랜잭션 커밋
} catch (Exception e) {
e.printStackTrace();
tx.rollback(); //트랜잭션 롤백
} finally {
em.close(); //엔티티 매니저 종료
}
emf.close(); //엔티티 매니저 팩토리 종료
}
public static void logic(EntityManager em) {
String id = "id1";
Member member = new Member();
member.setId(id);
member.setUsername("지한");
member.setAge(2);
//등록
em.persist(member);
//수정
member.setAge(20);
//한 건 조회
Member findMember = em.find(Member.class, id);
System.out.println("findMember=" + findMember.getUsername() + ", age=" + findMember.getAge());
//목록 조회
List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
System.out.println("members.size=" + members.size());
//삭제
em.remove(member);
}
}
|
JpaMain.java : JPA를 이용한 CRUD 작업을 수행하는 main 메서드
Member.java
package jpabook.start;
import javax.persistence.*; //**
/**
* User: HolyEyE
* Date: 13. 5. 24. Time: 오후 7:43
*/
@Entity
@Table(name="MEMBER")
public class Member {
@Id
@Column(name = "ID")
private String id;
@Column(name = "NAME")
private String username;
private Integer age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
|
Member.java : 테이블과 메핑되는 클래스로 @Entity 어노테이션을 사용
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 : 연결하려는 DB의 필수값과 선택값을 설정하는 파일
엔티티 매니저 설정
"엔티티매니저설정" 이미지를 보면 Persistence 에서 시작한다. 그런데 잘 생각해보면 Persistence는 우리가 만든 클래스가 아니다. 우리는 javax.persistence 패키지에서 제공하는 클래스를 사용하게 된다.
1. persistence.xml 참조 - 가장먼저 수행하는 부분은 persistence.xml을 참조한다. 하지만 참조하는 부분을 아직 찾지 못했다. (강의에서 들었던 내용은 프로젝트의 META-INF 밑에 만들면 설정정보를 참조한다고 알고 있는데 아마 안쪽에 숨겨져 있는 것 같다. 이번 내용에서는 패스ㅠ)
2. EntityManagerFactory 생성 - Persistence클래스의 createEntityManagerFactory 를 호출하면 우리는 EntityManagerFactory 를 얻을 수 있다.
(참고 - createEntityManagerFactory() 메소드를 호출하려면 매개변수로 String persistenceUnitName 을 넘겨줘야 된다. )
내부적으로 궁금해서 조금더 살펴봤더니 아래 흐름대로 호출하고 있다.
createEntityManagerFactory(String persistenceUnitName) ->
createEntityManagerFactory(String persistenceUnitName, Map properties) -> PersistenceProvider.createEntityManagerFactory(String emName, Map map)
마지막에 PersistenceProvider는 인터페이스이며 아래 이미지를 보면 PersistenceProvider의 createEntityManagerFactory를 호출한다. 그리고 이를 구현한 클래스들이 몇가지 있다.
너무 깊이 들어가면 포스팅이 길어질 것 같아서 여기 까지만 확인하자... 중요한건 우리는 2번을 수행함 으로서 EntityManagerFactory 에서 우리가 최종적으로 사용할 EntityManger를 생성할 수 있게 된다는 것이다.
3. EntityManger 생성 - EntityManagerFactory 의 createEntityManager() 메서드로 EntityManger 를 얻을 수 있다.
트랜잭션 관리
트랜젝션의 시작과 종료를 관리 해주는 EntityTransaction 인터페이스를 사용하고 있다. 해당 구현객체는 우리가 앞서 만들었던 EntityManager 에서 getTransaction()으로 얻을 수 있다.
EntityTransaction 인터페이스 안에는 여러가지 트랜젝션관리를 위한 메소드 존재하지만
우리가 이번에 사용하는 메소드는 아래의 두가지다.
begin() : 트렌젝션 시작commit() : 트렌젝션 종료
이미지 "테스트 코드" 를 보면 트랜젝션을 시작하고 종료하는 부분을 볼 수 있다.
그리고 그 사이에는 우리가 같은 트랜젝션 내에 수행할 코드를 작성해서 처리하면된다.
비즈니스 로직
1. Member 객체에 id,username,age 를 set
2. EntityManager 객체의 persist(memeber) 를 이용해서 등록한다.
3. member.SetAge(20)을 하면 수정이 일어난다. (실제로 DB에 UPDATE 문 날림)
4. EntityManager 객체의 find(Member.class, id) 를 이용해서 id에 해당하는 member를 조회한다.
5. EntityManager 객체의 createQuery()를 통해서 JPQL을 작성하고 member리스트를 조회한다.
6. EntityManager 객체의 remove(member) 로 현재 객체를 DB에서 삭제한다.
우리가 일반 객체를 만들어서 set메소드를 호출하면 아무일도 일어나지 않지만
persist로 등록한 이후에 set메소드를 호출하면 DB에 해당 데이터를 자동으로 바꿔준다.
JPA가 일반 객체와 구분을 하는 것 같다.
JPQL
JPQL ( Java Persistence Query Langugage ) : SQL을 추상화한 JPQL이라는 객체이향 쿼리 언어를 제공한다.
JPQL은 SQL과 문엉비 거의 유사해서 SELECT,FROM,WHERE,GROUP BY, HAVING, JOIN등을 사용할 수 있다. 둘의 가장 큰 차이점은 다음과 같다.
- JPQL은 엔티티 객체를 대상으로 쿼리한다. 쉽게 이야기해서 클래스와 필드를 대상으로 쿼리한다.
- SQL은 데이터베이스 테이블을 대상으로 쿼리한다.
여기서 재미 있는건 JPQL은 데이터베이스의 테이블을 전혀 알지 못한다는 것이다. 결국
목록조회에 사용한 "select m from Member m" 에서 Member는 회원 엔티티 객체를 말하는 것이다.
JPA는 JPQL을 분석해서 아래와 같은 쿼리로 데이터 베이스에서 조회 한다.
SELECT M.ID
, M.NANE
, M.AGE
FROM MEMBER M
정리
간단하게 위에서 봤던 아래의 이미지를 다시보자
이 이미지 하나면 충분할 것 같다... 누가 누구를 생성하게 해주는지 알면 끝!! 어차피 반복 작업이라서 아마도 Spring Data JPA는 지금 수행한 단계를 많이 줄여줄 것 같다.
그리고 또 하나는 EntityTransaction 을 EntityManager에서 얻을수 있다는 것만 알면 이번 예제는 어느정도 정리가 끝난 것 같다. !
개인적인 생각으로 SQL에 있어서 조회때 일어나는 여러가지 단점을 보완하고 해결하기 위해서 JPQL이 나온것 같다.
우리를 SQL로 부터 완전히 분리시키고 싶다는게 보인다. 아마 100% JPA만 사용할 수는 없을 것 같지만 나보다 훨씬 뛰어난 엔지니어들이 고민하고 또 고민해서 만든 기술이고 세계적으로 90%이상이 JPA를 사용한다고 하니까 분명
엄청난 이점을 가지고 있는건 확실한 것 같다.