해당 글은 DEPth IT 연합 프로젝트 동아리 활동의 일환인 서버 스터디에 관련되어 있습니다.🐧
JPA 초기 설정
MAVEN으로 프로젝트 템플릿을 생성한 후에 config 파일을 작성합니다.
pom.xml에 필요한 라이브러리들을 추가합니다. H2, hibernate에 관한 라이브러리를 추가했습니다.
JPA를 사용하기 위해서 몇가지 설정 파일을 넣어주어야 합니다. jpeg 설정 파일에 대해서 알아보겠습니다.
해당 파일은 resoueces/META-INF/persistence.xml라는 위치에 작성이 되어야합니다. 고정적으로 위치가 정해져 있습니다.
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
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_2.xsd">
<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=""/>
<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>
코드는 위와 같습니다. 위에서 중요한 부분은 dialect라는 부분인데 해당 설명은 아래에 있습니다.
데이터베이스 방언
JPA는 특정 데이터베이스에 종속되지 않습니다. 즉 데이터베이스가 어떤 것인지와는 관계 없이 기능을 합니다. 이때 SQL 표준을 지키지 않고 각각의 데이터베이스에서 사용하는 고유 문법이나 기능을 데이터베이스 방언이라고 합니다. 위의 그림과 같이 JPA에서는 Dialect를 사용해서 JPA내부에서 각각 매핑하여서 처리합니다. 따라서 특정 DB에 종속적이지 않습니다. 위의 코드에서도 h2 데이터베이스를 쓰기에 h2Dialect를 사용하는 것을 볼수 있습니다.
어플리케이션 개발
객체, 테이블 생성 후 매핑
CREATE TABLE Member (
id bigint NOT NULL,
name varchar(255),
primary key (id)
);
위 코드와 같이 H2 DB에서 테이블을 만들어 줍니다. id, name을 확인할수 있으며 id가 PK이 입니다.
package hellojpa;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity //jpa가 해당 어노테이션을 통해서 인식을 합니다.
public class Member {
@Id //JPA에게 PK를 알려줍니다.
private Long id;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
위에서 만든 테이블과 매핑이 되는 Member 클래스 코드입니다. 이때 JPA에게 정보를 제공하고 해당 클래스를 인식할 수 있게 어노테이션들을 활용해서 코드를 작성합니다. 자세한 설명은 주석에 있습니다.
public class jpaMain {
public static void main(String[] args) {
// 로딩 시점에 딱 하나만 만들어야합니다, 어플리케이션 전체에서 공유됩니다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// 트랜잭션 단위로 엔티티매니져를 만들어 줘야 합니다. close를 통해서 사용하고 닫아야합니다. 공유X
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin(); //트랜잭션을 실행합니다.
//실제로 데이터 추가, JPA가 실행합니다.
Member member = new Member();
member.setId(2L);
member.setName("HelloB");
em.persist(member);
tx.commit();
em.close();
emf.close();
}
}
JPA에서 데이터의 변경은 트랜잭션 단위로 진행됩니다. 따라서 tx.begin, tx.commit을 볼 수 있습니다. 해당 코드를 실행하면 아까 위에서 만든 테이블에 데이터가 저장이 됩니다. 이때 테이블을 지정하지 않았는데도 저장이 된갓은 관례에 따른것입니다. 하지만 매핑을 직접하는것도 가능합니다. Member 클래스에서 @Table 어느테이션을 사용하면 테이블 이름을 지정할 수 있습니다. 또한 @Column을 사용하면 Column의 이름 또한 지정 가능합니다.
실행 결과는 위와 같습니다. 해당 내용이 출력되는 이유는 persistence.xml 파일에서의 옵션에서 SQL 쿼리를 정리해서 볼수 있게 설정하였고 또한 해당 뭐리가 왜 나온지 알려주는 설정을 추가해서 그렇습니다.
Try-catch / 조회, 삭제, 수정
public class jpaMain {
public static void main(String[] args) {
// 로딩 시점에 딱 하나만 만들어야합니다, 어플리케이션 전체에서 공유됩니다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// 트랜잭션 단위로 엔티티매니져를 만들어 줘야 합니다. close를 통해서 사용하고 닫아야합니다. 공유X
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin(); //트랜잭션을 실행합니다.
try{
Member member = new Member();
member.setId(2L);
member.setName("helloB");
em.persist(member);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
try-catch를 이용해서 트랜잭션 단위로 진행하는 도중에 예외가 일어나면 rollback과정을 진행합니다. 실제로는 해당 코드에서 em.persist정도만 작성하면 나머지는 스프링에서 진행합니다.
조회 / 삭제
try{
Member findmember = em.find(Member.class, 1L);
System.out.println("findMember.id = " + findmember.getId());
System.out.println("findMember.name = " + findmember.getName());
tx.commit();
}
find를 이용해서 pk를 통해서 값을 조회합니다. 여기서 찾은 값을 em.remove() 함수에 넣으면 삭제가 됩니다.
수정
try{
Member findMember = em.find(Member.class, 1L);
findMember.setName("HelloJPA");
tx.commit();
}
java에서 값을 바꾸면 자동으로 DB 값도 함께 바뀝니다. 이는 JPA에서 엔티티를 관리하기 때문입니다. 트랜잭션을 commit하는 시점에 데이터의 변화를 모두 체크합니다. 이때 변경된 내용이 존재한다면 update 쿼리를 자동으로 실행합니다. 따라서 바꾼 후에 persis를 하지 않습니다.
JPQL
JPA를 사용하면 엔티티 갹체를 중심으로 개발이 진행됩니다. 하지만 현업에서는 검색을 하는 경우가 굉장히 많은데 이때 모든 DB 데이터를 객체로 변환해서 검색하는 것은 사실살 불가능한 일입니다. 따라서 필요한 데이터만 DB에서 불러오기 위해서는 검색 조건이 들어간 SQL이 필요합니다. 테이블을 대상으로 뭐리를 만들면 DB에 종속적인 설계가 되기 때문에 JPQL을 사용합니다.
즉, JPQL은 SQL을 추상화한 객체지향 쿼리 언어입니다. SQL의 문법과 유사하지만 엔티티 객체를 대상으로 쿼리가 됩니다. 따라서 특정 데이터베이스에 의존하지 않습니다.