어플리케이션 ( 객체지향 언어 ) : 자바, 스칼라, ...
데이터베이스 ( 관계형 ) : MySql, Oracle, ...
=> 객체를 관계형 DB에 저장
public class Member{
private Long id;
private String name;
...
}
insert into Member values(...);
=> 만약 Member 필드가 변경되면 SQL까지 전부 변경해야한다.Member member = memberDao.find(id);
member.getTeam();
member.getOrder();
=> team, order에 null이 반환
=> 조회에 따른 여러 메소드 생성
데이터베이스도 있지만 다른 개념이라 없다고 봄.
객체 : 참조 사용
테이블 : 외래키 ( 조인 )
1차 캐시와 동일성 보장 : 같은 트랜잭션 안에서는 같은 엔티티 반환 => 약간의 성능 향상
쓰기 지연
transaction.begin();
em.persist(a);
em.persist(b);
...
transaction.commit();
지연로딩 즉시로딩 => 옵션을 선택 가능.
지연로딩 : 객체가 실제 사용될 때 로딩
Member member = memberDao.find(id); => select m from member m;
Team team = member.getTeam();
String teamName = team.getName(); => select t from team t;
즉시로딩 : 조인으로 연관된 객체 미리 조회
Member member = memberDao.find(id); => select m,t
from member m join Team t;
관계형 데이터베이스에서 MySql, Oracle을 대부분 사용중이다.
이 둘은 관계형 데이터베이스라는 공통점이 있지만 문법에서 차이를 보인다.
Mysql | Oracle | |
---|---|---|
가변문자열 | varchar | varchar2 |
페이지 | limit | rownum |
문자열자르기 | substring | substr |
jpa에서는 이러한 sql 방언에 대해 자동으로 sql을 생성해준다.
@Entity
public class Member {
@Id
private Long id;
private String name;
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;
}
}
// 1. Persistence를 이용해 EntityManagerFactory 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// 2. emf를 이용해 entitymanager 생성
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
// Code
Member member = new Member();
member.setId(1L);
member.setName("member1");
em.persist(member);
tx.commit();
em.clear();
emf.close();
=> 결과
Hibernate:
/* insert jpabasic.jpa.helloJpa.Member
*/ insert
into
Member
(name, id)
values
(?, ?)
try {
Member findMember = em.find(Member.class, 1L);
tx.commit();
}catch (Exception e){
tx.rollback();
}finally {
em.close();
}
=> 결과
Hibernate:
select
member0_.id as id1_0_0_,
member0_.name as name2_0_0_
from
Member member0_
where
member0_.id=?
try {
Member findMember = em.find(Member.class, 1L);
findMember.setName("updateName");
tx.commit();
}catch (Exception e){
tx.rollback();
}finally {
em.close();
}
=> Member 객체에 setName 후 em.persist를 하지 않는다.
=> 자동으로 update 쿼리를 생성.
/* update
jpabasic.jpa.helloJpa.Member */ update
Member
set
name=?
where
id=?
JPA에서 중요한 점
영속성 컨텐스트란 엔티티를 영구 저장하는 환경. 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할. 엔티티 매니저를 통해 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.
EntityManagerFactory => EntityManager => DB접근
비영속
객체를 생성한 상태
Member m = new Member();
m.id = 1;
m.name = "asd";
영속
비영속 상태의 객체를 저장한 상태
EntityManager.persist(m);
준영속, 삭제
Member member = new Member();
member.setId(1L);
member.setName("name1");
em.persist(member);
Member findMember = em.find(1L); => 1차 캐시에서 조회 / select 쿼리 생성 없이 조회
=> 조회한 데이터가 영속성 컨텍스트에 없다면 영속 상태로 저장
=> 트랜잭션이 종료되면 1차 캐시도 없어지기 때문에 성능상 큰 이점은 없다.
Member m1 = em.find(Member.class, "m1");
Member m2 = em.find(Member.class, "m1");
System.out.print(m1 == m2) => true
em.persist(member); => 1차 캐시에 저장
tx.commit(); => commit 시점에 SQL 쿼리 생성
try {
Member findMember = em.find(Member.class, 1L);
findMember.setName("updateName");
tx.commit();
}catch (Exception e){
tx.rollback();
}finally {
em.close();
}
=> Member 객체에 setName 후 em.persist를 하지 않는다.
=> 자동으로 update 쿼리를 생성.
/* update
jpabasic.jpa.helloJpa.Member */ update
Member
set
name=?
where
id=?
flush => Entity와 snapshot(처음 읽어온 데이터) 비교 => 쓰기 지연 저장소에 저장 => commit => flush
@Entity => JPA가 관리
@Table
연관관계 매핑 전
Team team = new Team();
team.setName("team1");
em.persist(team);
Member member = new Member();
member.setName("member1");
member.setTeam(team.getId());
em.persist(member);
매핑 후
Team team = new Team();
team.setName("team1");
em.persist(team);
Member member = new Member();
member.setName("member1");
member.setTeam(team);
em.persist(member);
Member | Team |
---|---|
id | id |
Team team | List members |
name | name |
N | 1 |
Member와 Team 양방향 연관관계 설정 시 탐색 가능.
테이블은 FK로 두 테이블 연관관계를 관리할 수 있다.
객체의 경우 둘 중 하나로 외래 키를 관리해야 함.
데이터베이스 테이블에서는 양방향 연관관계( 외래키를 사용해 )를 설정할 수 있다.
하지만 객체의 양방향 관계는 단방향 관계 2개이다.
멤버와 팀 테이블이 연관관계가 있다면 하나의 데이터가 수정되었을 때 신뢰할 수 있는 데이터를 알 수 없다.
두 관계중 하나를 연관관계 주인으로 설정해야된다.
mappedBy를 사용한 쪽이 종이 된다. => 주인이 아닌 쪽은 읽기만 가능. set을 할 수 없다. DB에 반영이 안됨.
Team team = new Team();
team.setName("team1");
em.persist(team);
Member member = new Member();
member.setName("member1");
member.setTeam(team); => **팀 지정 시 팀의 members에 member 세팅**
em.persist(member);
@Entity
public class Member {
@Id
@Column(name = "member_id")
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
...
pulbic void setTeam(Team team){
this.team = team;
team.getMembers().add(this);
}
}
단방향 매핑만으로도 연관관계 완료.
JPQL에서 역방향으로 탐색할 일이 많음.
단방향 매핑을 하고 필요 시 양방향으로.
SQL을 추상화한 객체 지향 쿼리언어.
엔티티 객체를 대상으로 검색.
JPA는 SQL을 추상화한 JPQL을 제공
JPQL | SQL |
---|---|
Entity 대상으로 쿼리 | DB TABLE 대상으로 쿼리 |
List<Member> list = em.createquery("
select m from Member m where id = 1
")
예외 케이스 문자열을 만들어 더해 주는 방식 사용 => 에러 발생 가능성 높음
대안으로 Criteria => 복잡하고 실용성이 없다 => QueryDSL 권장
Member m = Member.member;
List<Member> result = queryFactory
.select(m)
.from(m)
.where(m.name.like("he"))
.fetch();
SQL에서 제공되는 대부분이 같음
List<Member> list = em.createQuery("select m from Member m order by m.age asc", Member.class)
.setFirstResult(1)
.setMaxResults(10)
.getResult();
String query = "select m from Member m join m.team t";
List<Member> result = em.createQuery(query, Member.class)
.getResultList();
=> 연관관계가 있는 객체간 조회 가능
=> JPA2.1 부터 ON을 활용한 조인 가능 -> 연관관계 없는 객체 외부 조인 가능
SQL, JPQL 해당 로직 실행 전까지 작동여부 확인 불가( 오류 ).
장점
문자가 아닌 코드로 작성
컴파일 시점 문법 오류 발견
동적 쿼리
쿼리에 메소드를 사용할 수도 있다.
query.selectFrom(m)
.where(
m.type.eq(typeParam),
isValid()
)
.fetch();
private BooleanExpression isValid(){
return m.status.eq("A");
}