JPA(1) - 인프런

JPA(1) - 인프런

1. 객체를 관계형 DB에 관리, SQL에 의존적이다.

객체 필드추가
public class Member {
	private String memberId;
	private String name;
	// tel필드 추가 시, Insert, update, select 모두추가필요
	private String tel;	
}
객체 vs 관계형 데이터베이스

객체와 관계형 DB의 차이점
i) 상속
ii) 연관관계
iii) 데이터타입
iv) 데이터식별방법
-> 객체를 관계형 DB에 저장하기 위해 개발자가 SQL Mapper 역할을 한다.

객체지향에 SQL을 구현하기 위해서는 부가적인 코드가 필요하다

컬렉션처럼 처리할 수는 없을까?


2. JPA

image


3. JPA 구동 방식

image


4. 영속성 관리

JPA에서 가장 중요한 2가지
  1. 객체와 관계형 DB 매핑하기(Object Relational Mapping)
  2. 영속성 컨텍스트(논리적인 개념으로 “엔티티를 영구 저장하는 환경")

EntityManager를 통해서 영속성 컨텍스트에 접근할 수 있다.

EntityManager.persist(entity) // DB에 저장하는 것이 아니라 영속성 컨텍스트에 저장!
Entity 생명주기

image

  1. 비영속 상태(객체를 생성한 상태)

    Member member = new Member();
    
  2. 영속(비영속 상태의 객체를 저장한 상태)

    Member member = new Member();
    member.setId("member1");
    

    EntityManager em = EntityManagerFactory.createEntityManager(); em.getTransaction().begin();

    // 객체를 저장한 상태(영속) em.persist(member);

    
    
  3. 준영속(영속성 컨텍스트에서 분리)

    // 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
    em.detach(member);
    // 객체를 삭제한 상태(삭제)
    em.remove(member);
    
영속성 컨텍스트의 이점

1) 1차 캐시

Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
//1차 캐시에 저장됨
em.persist(member);
//1차 캐시에서 조회(SELECT문을 타지 않는다.)
Member findMember = em.find(Member.class, "member1");

// 1차 캐시에 없는 경우(DB조회 -> 1차캐시에 저장 -> 반환)
Member findMember2 = em.find(Member.class, "member2");

2) 동일성 보장

Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //동일성 비교 true (1차 캐시)

3) 트랜잭션을 지원하는 쓰기지연

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.(쓰기지연 SQL저장소에 저장)
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋

image

4) 변경 감지(Dirty Checking)

// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
//em.update(member) 이런 코드가 필요없다 -> 마치 Collection처럼 set으로만 해결한다.
//기존 id에 대한 스냅샷과 비교하여 변경감지
플러시란?

영속성 컨텍스트의 변경내용을 DB에 반영!

플러시가 발생되는 경우

  • em.flush() - 직접 호출
  • 트랜잭션 커밋 - 플러시 자동 호출
  • JPQL 쿼리 실행 - 플러시 자동 호출

5. 연관관계

객체의 참조 vs 테이블의 외래키 매핑
예시로 Member 객체에서 Team을 알기위해서 teamId 속성을 갖고 있다면
teamId 속성으로 Team 객체를 다시 조회해야 하므로 객체지향적인 방법이 아니다.(데이터 중심 모델링)
따라서, 객체는 참조를 통해 연관객체를 찾는다.

image

@Entity
public class Member {
	@Id @GeneratedValue
 	private Long id;
 
    @Column(name = "USERNAME")
 	private String name;
 	private int age;
	
    // @Column(name = "TEAM_ID")
	// private Long teamId;
 	@ManyToOne
 	@JoinColumn(name = "TEAM_ID")
 	private Team team;
}

@OneToMany

//조회
Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam();

// 새로운 팀B
Team teamB = new Team();
teamB.setName("TeamB");
em.persist(teamB);
// 회원1에 새로운 팀B 설정
member.setTeam(teamB);
양방향 연관관계

image

테이블 연관관계는 TEAM_ID만으로 이미 양방향 성립
객채에서의 양방향 연관관계는 회원->팀, 팀->회원 연관관계가 두개이다!(사실 양방향이 아니다.)

연관관계의 주인과 mappedBy

JPA에서는 두 테이블 중 하나로 외래 키를 관리해야 한다.(양쪽 다 수정할 수 없다.)

양방향 매핑 규칙

  • 객체의 두 관계 중 하나를 연관관계의 주인으로 지정
  • 연관관계의 주인만이 외래키를 관리(등록, 수정)
  • 주인이 아닌쪽은 읽기만 가능
  • 주인이 아니면 mappedBy 속성으로 주인 지정

Comments

comments powered by Disqus