August 21, 2021
Java Persistence API
자바 진영의 ORM
기술 표준. 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
이다.
💡 ORM(Object Relational Mapping)
- SQL을 직접 다루지 않고, 객체와 테이블을 매핑하여 간접적으로 DB데이터 관리
- 객체 간 관계를 바탕으로 SQL 문장 자동 생성
- SQL이 아닌 메소드로 데이터 조작
애플리케이션과 JDBC 사이에서 동작.
개발자가 JPA를 사용하면 JPA 내부에서 JDBC API를 사용하여 SQL을 호출, DB와 통신한다.
💎 Dialect (방언) 설정
JPA 프로젝트로 변환
: 프로젝트 우클릭 - Configure - Convert to JPA Project 클릭하여 JPA 프로젝트로 변환한다 - 마지막에 Disable Library Configuration을 설정해 줄 것이다(메이븐으로 사용할 것임)
아래와 같이 persistence.xml
파일이 생겼으면 정상적으로 변환 완료
Maven 프로젝트로 변환
: 프로젝트 우클릭 - Configure - Convert to Maven project
...
</build>
<dependencies>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.2.Final</version>
</dependency>
<!-- lombok 사용 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!-- oracle driver의 설정 -->
<dependency>
<groupId>com.jslsolucoes</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.1.0</version>
</dependency>
</dependencies>
<!-- oracle driver의 추가 설정 정보 -->
<repositories>
<repository>
<id>oracle</id>
<name>ORACLE JDBC Repository</name>
<url>https://maven.atlassian.com/3rdparty/</url>
</repository>
</repositories>
</project>
<?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="oracleDB">
<class>model.entity.Team</class>
<class>model.entity.Member</class>
<properties>
<property name="javax.persistence.jdbc.driver"
value="oracle.jdbc.OracleDriver" />
<property name="javax.persistence.jdbc.url"
value="jdbc:oracle:thin:@127.0.0.1:1521:xe" />
<property name="javax.persistence.jdbc.user"
value="SCOTT" />
<property name="javax.persistence.jdbc.password"
value="TIGER" />
<property name="hibernate.dialect"
value="org.hibernate.dialect.OracleDialect" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.id.new_generator_mappings" value="true" />
<property name="hibernate.hbm2ddl.auto" value="create" />
</properties>
</persistence-unit>
</persistence>
<persistence-unit>
: JPA가 연동할 DataBase에 대한 정보 보유 (연동하고자 하는 DB가 여러개인 경우 여러개의 persistence-unit 설정하면 됨)
<class>model.entity.Team</class>
: Entity 클래스를 등록<property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect" />
: 방언처리기 지정 (오라클 Dialect 사용할 것이다)<property name="hibernate.show_sql" value="true" />
: 콘솔창에 생성된 sql문 출력하겠다<property name="hibernate.id.new_generator_mappings" value="true" />
: 테이블의 PK컬럼을 자동 생성하겠다(value=“true” : SequenceStyleGenerator를 사용하겠다)<property name="hibernate.hbm2ddl.auto" value="create" />
: DDL(테이블 생성, 삭제, 변경)을 자동으로 실행하겠다. (실행하지 않으려는 경우 value=“none”)객체의 참조 관계와 테이블의 외래키를 매핑하는 것을 의미
⚡ Member 객체가 Team 객체를 참조하는 경우
// JDBC에서의 설계
class Member{
private long memberId;
private String name;
private long teamId;
}
// JPA에서의 설계
class Member{
private long memberId;
private String name;
private Team team; // 엔티티 객체를 참조
}
해당 엔티티를 중심으로 상대 엔티티를 바라봤을때
DB 설계상 ‘다’인 쪽이 외래 키를 갖는다. 😊무.조.건.
데이터베이스 테이블은 외래키 하나로 양쪽 테이블을 조인하므로 단방향이니 양방향이니 나눌 필요가 없다.
하지만 객체는 참조용 필드가 있는 객체만 다른 객체를 참조하는 것이 가능하므로, JPA를 사용하여 데이터베이스와 패러다임을 맞추기 위해서는 연관관계의 방향을 선택해야 한다.
🤷♀️ 단방향.. 양방향…? 어떨때 써야할까???
- 다대일 단방향 관계 : member1.getTeam() 처럼 멤버가 팀을 참조 가능
양방향 관계 : team1.getMembers()처럼 팀이 멤버를 참조도 가능하게 하려면 양방향으로 설정하면 됨
=> 역방향으로 객체 탐색이 꼭 필요할때 양방향 관계를 설정하자
외래키를 갖는 테이블이 연관관계의 주인이 된다. 😊무.조.건.
mappedBy
속성을 사용하면 된다.<Team.java> - 부모 엔티티
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@Entity
class Team{
@Id
@Column(name="team_id")
private Long teamId;
@Column(length=20, name="team_name")
private String teamName;
/* 해당 엔티티(Team)가 상대 엔티티(Member)의 여러 객체를 가질 수 있음
* `일`인 쪽에 @OneToMany를 추가하여 양방향 관계 만듬
* - 팀에서 여러 멤버들을 참조 가능하게 ArrayList 만들어준다
* mappedBy 사용하여 연관관계의 주인을 설정해줌
* - mappedBy의 값은 대상이 되는 변수명(Member 테이블의 teamId 변수)
*/
@OneToMany(mappedBy="teamId")
List<Member> members = new ArrayList<Member>();
<Member.java> - 자식 엔티티, 외래키를 가짐(Team), 연관관계의 주인
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@Entity
class Member{
@Id
@Column(name="member_id")
private long memberId;
@Column(length=20)
private String name;
// JoinColumn : 외래키를 매핑하는 애노테이션.
// 해당 엔티티(Member)의 여러 객체가 상대 엔티티(Team)에 대응 - 다대일 관계(@ManyToOne)
@JoinColumn(name="team_id")
@ManyToOne
private Team teamId;
}
API | 역할 |
---|---|
Persistence | EntityManagerFactory 인스턴스를 얻기 위한 메소드를 가짐 = createEntityManagerFactory() |
EntityManagerFactory |
클라이언트 요청시마다(=각 스레드마다) EntityManager 객체 생성, 관리 |
EntityManager |
엔티티에 대한 모든 작업 (CRUD) 처리 |
EntityTransaction |
EntityManager와 1:1 관계. 각 EntityManager의 작업은 EntityTransaction 클래스에 의해 유지 관리. |
메소드 | 기능 |
---|---|
persist(Object entity) |
entity를 영속화 (INSERT) |
merge(Object entity) |
준영속 상태의 entity를 영속화 (UPDATE) |
remove(Object entity) |
영속 상태의 entity를 제거 (DELETE) |
find(Class entity, Object pk) |
하나의 entity 검색 (SELECT one) |
createQuery(String jpql, Class resultClass) |
JPQL에 해당하는 entity 목록 검색 (SELECT list) |
public class RunningTest {
void test() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("oracleDB");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try{
// 엔티티 클래스의 setter 메소드 - tx.commit() 시에 UPDATE문 수행
Team t1 = new Team();
t1.setTeamName("우리팀");
Member m1 = new Member();
m1.setName("장보리");
m1.setTeamId(t1);
Member m2 = new Member();
m2.setName("서리태");
m2.setTeamId(t1);
t1.getMembers().add(m1);
t1.getMembers().add(m2);
// INSERT
em.persist(t1);
em.persist(m1);
em.persist(m2);
// SELECT one
Member oneMember = em.find(Member.class, 1L); // pk값(1)으로 조회
System.out.println("조회한 멤버 : " + oneMember);
// SELECT list
List<Member> allMember = em.createQuery(
"select m from Member m", Member.class).getResultList();
System.out.println("멤버 수 : " + allMember.size());
// DELETE
em.remove(oneMember); // 객체를 파라미터로 받아서 pk값을 통해 해당 데이터 삭제
tx.commit();
}catch(Exception e) {
// 에러 발생시 CRUD 결과 저장하지 않고 rollback
tx.rollback();
e.printStacktrace();
}finally {
// 자원반환
em.close();
emf.close();
}
}
}
[참고한 사이트]