JPA와 모던 자바데이터 저장 기술
현 문제점??
- 객체지향 언어인 JAVA의 객체를 관계형 데이터베이스를 통해 관리
- SQL 중심의 개발
- SQL 중심의 개발이기 개발자가 객체 지향의 구조를 만들었어도 결국 SQL로 데이터를 관리
(개발자가 직접 SQL을 작성 및 수정)
1. 객체 CRUD
public class Member{
private String memberId;
private String name;
}
Insert INTO MEMBER(MEMBER_ID, NAME) VALUES
SELECT MEMBER_ID, NAME FROM MEMBER M
UPDATE MEMBER SET ...
전화번호를 추가해야하는 요청이 있을 때... 하나 전화번호와 관련된 모든 SQL문을 수정
public class Member {
private String memberId;
private String name;
private String tel;
...
}
Insert INTO MEMBER(MEMBER_ID, NAME, tel) VALUES....
SELECT MEMBER_ID, NAME, tel FROM MEMBER M
UPDATE MEMBER SET ... tel = ?
SQL의 중심으로 개발이 이루어지는 문제가 발생(SQL에 의존적)
2. 패러다임의 불일치 - 객체지향, 관계형 데이터베이스
- 서로 다른 사상
- 객체지향 : 추상화, 캡슐화, 정보은닉, 상속, 다형성 등 시스템의 복잡성을 제어 할 수 있는 다양한 장치들을 제공
- 관계형 DB : 서로 관련된 데이터 포인트에 대한 액세를 저장 및 제공하는 데이터베이스 유형
- 객체를 영구 보관하는 다양한 저장소
RDB - 현재
NoSQL
FILE
OODB - 망함
현실적으로 RDB를 통해 개발을 많이 하였고 하고 있다.
개발자와 SQL은 끊을수 없는 운명(개발자 == SQL문 만드는 사람)
3. 객체와 관계형 데이터베이스의 차이
- 객체의 상속 관계와 Table 슈퍼타입 서브타입 관계가 유사
- 객체의 상속 관계에서 extends와 implements 상속 및 구현이 자유롭고 상속관계간의 캐스팅도 자유로움
- Table에서 슈퍼타입과 서브타입 관계는 서로 혼용 할 수 있는 관계입니다. 슈퍼타입이 여러 개의 서브타입을 가질수 있습니다.
- Album 저장 : 객체 분해, Insert into item.., insert into Album....
- Album 조회 : 각각의 테이블에 따른 조인 SQL 작성, 각각의 객체 생성
결론은 DB에 저장 할 객체에는 상속 관계를 쓰지 않는다.
4. 연관관계
객체 연관 관계 : 참조 사용 - member.getTeam()
테이블 연관 관계 : 외래키를 사용 - JOIN IN M.TEAM_ID = T.TEAM.ID
테이블에 따라 객체를 모델링
class Member {
String id;
Long teamId; //TEAM_ID FK 사용
String username;
}
class Team{
Long id; //TEAM_ID PK 사용
String name;
}
?? 일반적으로 사용하지만 객체지향에 어긋나는 코드 입니다.
객체를 활용한 모델링
class Member {
String id;
Team team; // 객체 참조
String username;
}
insert into MEMBER(member_id, team_id, username) values(....)
member.getTeam.getId();
참조하는 member객체에서 id 값을 가지고 옵니다.
객체 모델링 조회
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
public Member find(String memberId){
//SQL 실행
Member member = new Member();
//데이터터베이스에서 조회한 회원 관련 정보를 모두 입력
Team team = new Team();
//회원과 팀 관계 설정
member.setTeam(team);
return member;
}
객체 그래프 탐색
객체는 자유롭게 객체 그래프를 탐색 할 수 있어야 한다.
처음 실행하는 SQL에 따라 탐색 범위가 결정 됌
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
member.getTeam(); //OK
member.getOrder(); //null
SQL을 확인 하지 않으면 찾고자하는 객체의 속성들의 값을 알 수 없음
모든 객체를 미리 로딩 할 수 없음
memberDAO.getMember(); //Member만 조회
memberDAO.getMemberWithTeam(); //Member와 Team 조회
//Member, Order, Delivery
memberDAO.getMemberWithOrderWithDelivery();
엔티티 신뢰의 문제가 생김
class MemberService {
...
public void process() {
Member member = memberDAO.find(memberId);
member.getTeam(); //???
member.getOrder().getDelivery(); // ???
- SQL문을 확인하지 않으면 엔티티에 어떤 값이 있는지 객체 그래프는 어디까지 탐색이 가능한지 알 수 없습니다.
같은 값으로 두개의 객체 생성
String memberId = "100";
Member member1 = memberDAO.getMember(memberId);
Member member2 = memberDAO.getMember(memberId);
member1 == member2; //다르다.
class MemberDAO {
public Member getMember(String memberId) {
String sql = "SELECT * FROM MEMBER WHERE MEMBER_ID = ?";
...
//JDBC API, SQL 실행
return new Member(...);
참조 주소가 다름
자바 컬렉션으로 비교
String memberId = "100";
Member member1 = list.get(memberId);
Member member2 = list.get(memberId);
member1 == member2; //같다.
JPA 시작하기
1. DB 설치
- H2 DATABASE 사용
2. 프로젝트 생성
- 자바 11 (8 이상 권장)
- 메이븐
- groupId : jpa-basic
- artifactId : ex1-hello-jpa
- version : 1.0.0
라이브러리 추가 - pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>jpa-basic</groupId>
<artifactId>ex01-hello-jpa</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--JPA 하이버네이트-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.6.9.Final</version>
</dependency>
<!--H2 DataBase-->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.1.212</version>
</dependency>
</dependencies>
H2 데이터베이스 사용 시 버전을 확인 하시고 정확하게 적어 주셔야 합니다.
3. JPA 설정
- /META-INF/persistence.xml 생성 (일반적으로 META-INF 밑에 만들어줌)
- persistence-unit name으로 이름 지정
- javax.persistence로 시작: JPA 표준 속성
- hibernate로 시작: 하이버네이트 전용 속성
Maven
<?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>
<!-- 필수 속성 javax 표준, hibernate 전용옵션-->
<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>
MySQLDialect, OracleDialect, H2Dialect 를 변경해줘서 RDB 변경이 가능합니다.
JPA 구동 방식
기본 코드
package hellojpa;
import javax.persistence.*;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
//애플리케이션 로딩 시에 1개만 만들어야 함(데이터베이스 당 1)
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
//실제 동작하는 code
EntityManager em = emf.createEntityManager();
//간단하게 data connection, 모든 데이터작업은 트랜잭션 안에서 해야한다.
emf.close
}
}
- Persistence 클래스에서 persistence.xml 설정한 persistenceUnitName을 인자 값으로 전달하면 인자 값에 해당하는 정보를 가지고 EntityManagerFactory를 만든다.
- EntityManagerFactory인 emf를 이용해서 EntityManager를 만든다.
- EntityManager인 em를 이용해서 데이터베이스에 SQL문을 전달 합니다.
Entity 파일
package hellojpa;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
//jap 사용 인식 및 관리
@Entity
/*@Table(name="USER")*/
public class Member {
@Id
private Long id;
private String name;
public Member(){
}
public Member(Long id, String name) {
this.id = id;
this.name = name;
}
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
- @Entity : 해당 객체를 JPA에서 관리하겠다는 뜻
- @Id : 해당 속성을 PK로 사용하겠다라는 뜻
Transaction
package hellojpa;
import javax.persistence.*;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
//애플리케이션 로딩 시에 1개만 만들어야 함(데이터베이스 당 1)
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
//실제 동작하는 code
EntityManager em = emf.createEntityManager();
//간단하게 data connection, 모든 데이터작업은 트랜잭션 안에서 해야한다.
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
findMember.setName("helloJPA");
tx.commit();
}catch (Exception e){
tx.rollback();
} finally {
// 자원을 닫아준다(데이터커넥션 닫아준다)
em.close();
}
emf.close();
}
}
JPA는 Transaction commit 이후에 데이터에 반영이 됩니다.
EntityManger의 기본적인 CRUD
- persist(저장) : 영속성 컨텍스테 1차 캐시에 저장(DB에 저장X)
- find(조회)
- remove(삭제)
- 변경 : 변경은 따로 함수를 호출하기보다는 find를 해서 가져온 객체에서 setter 메서드를 통해 값을 변경하면 commit()호출시 적용되기 전 시스템에서 변경감지를 통해 기존 객체와 차이점을 찾아서 업데이트를 자동으로 해준다.
주의
1. 엔티티매니지 팩토리는 하나만 생성해서 애플리케이션 전체에서 공유
2. 엔티티 매니저는 쓰레드간 공유하지 않고 쓰고 버린다
3. JPA 모든 데이터 변경은 트랜잭션 안에서 실행
단순 조회가 아닌 조건절을 이용한 SQL문은????
JPQL을 사용!!
- 테이블이 아닌 객체를 대상으로 검색하는 객체 지향 쿼리
- SQL을 추상화해서 특정 데이터베이스 SQL에 의존X
- 객체 지향 SQL
간단한 예제
List<Member> result = em.createQuery("select m from Member as m", Member.class).getResultList();
일반 쿼리는 DB 테이블을 대상으로 조회, JPQL은 테이블이 아닌 Member는 엔티티(Member.class) 으로 한다.
장점?
페이징 처리 시
List<Member> result = em.createQuery("select m from Member as m", Member.class). setFirstResult(1).setMaxResults(10).getResultList();
- limit, offset를 자동으로 바꿔줍니다.
- DB에 마다 다른 구문을 자동으로 변경해 반영합니다.
- ansi가 제공하는 표준 SQL 문법을 모두 제공한다.
https://catsbi.oopy.io/0a48ff61-ee46-4909-b5b4-d5f63937195f
JPA 소개 & 시작하기
JPA 소개
catsbi.oopy.io
'개발 > JPA' 카테고리의 다른 글
고급 매핑 (0) | 2022.06.07 |
---|---|
다양한 연관관계 매핑 (0) | 2022.06.06 |
연관관계 매핑 기초 (0) | 2022.06.05 |
엔티티 매핑 (0) | 2022.06.03 |
영속성 관리 (0) | 2022.06.02 |