애플리케이션을 객체지향 언어로 개발하고 & 관계형 데이터베이스로 관리한다면
객체-관계형간의 차이를 해결하기 위해 JPA 를 사용
자바 진영에서 ORM(Object-Relational Mapping)기술에 대한 표준으로 사용되는 인터페이스의 모음
→ 즉, 실제로 구현된 것이 아니라, 구현된 클래스와 mapping해주기 위해 사용되는 프레임워크 O
자바 ORM 기술에 대한 표준 명세
영속성 관리 도구
영속성: 데이터를 생성한 프로그램의 실행이 종료되더라도 사라지지 않는 데이터의 특성
영속성 컨텍스트
- 엔티티를 영구 저장하는 환경
- 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할
참고: 영속성 컨텍스트
쿼리 자동 생성
쿼리(Query): 데이터베이스에 정보를 요청하는 것
최적화
sql을 지원한다면, jpa가 방언들도 알아서 처리
방언 (Dialect)
SQL은 ANSI SQL(표준 SQL)이 있으며,
이 외에도 각 DBMS Vendor(벤더, 공급업체)인 MS-SQL, Oracle, MySQL, PostgreSQL 에서 자신만의 기능을 추가한 SQL이 있다.
ANSI SQL이 모든 DBMS에서 공통으로 사용 가능한 핵심 표준 SQL(= 대한민국의 수도인 서울에서 사용하는 표준어)이지만,
여러 제품의 DBMS에서는 자신만의 독자적인 기능을 위해서 추가적인 SQL(= 여러 지방에서 사용하는 방언)을 만들었다.
(예를 들면, MS-SQL의 T-SQL, Oracle의 PL/SQL)
어플리케이션이 데이터베이스를 직접 다룰 때의 문제점
- 훨씬 더 번거로움
- SQL 의존적이라 변경에 취약
- 객체지향 모델과 관계형 데이터베이스의 패러다임 불일치
--> 이 문제의 해결책으로 ORM(객체 관계 매핑), JPA이 등장
웹 어플리케이션에는 객체를 관계형 데이터베이스에서 관리하는 것이 중요하고, 관계형 데이터베이스가 웹 서비스의 중심이 되었다.
그러다보니, SQL이 어플리케이션 코드 보다 많아졌고, 이를 해결하기 위해 JPA 가 등장했다.
참고: SQL (Structured Query Language, 구조적 질의 언어)
현업에서는 수십~수백 개의 테이블 & 그보다 더 많은 SQL 을 직접 만들고, 유지보수를 해야한다.
객체지향 프로그래밍 언어 : 메시지 기반. 기능과 속성을 한 곳에서 관리
관계형 데이터베이스 : 어떻게 데이터를 저장할지 → DB 는 데이터 중심으로 구조화되어있으므로(객체의 상속, 다형성 같은 개념이 없음)
→ 즉, 관계형 데이터베이스에서는 객체지향 프로그래밍 언어를 표현할 수 X
Java 에서는 객체의 상속 관계 지원 O
RDB 에서는 객체의 상속 관계 지원 X
객체지향 프로그래밍에서, 부모 객체를 가져오고 싶은 경우
// user와 group은 부모-자식 관계이다.
User user = findUser();
Group group = user.getGroup();
++ 여기에 DB 가 추가된 경우
User user = userDat.findUser();
Group group = groupDat.findGroup(user.getGroupId());
user와 group을 각각 따로 조회하게 된다.
→ user와 group이 서로 어떤 관계인지 정확히 알 수 X
→ 이 때문에, 객체 모델링을 DB에서는 구현할 수 없게 돼버림
→ 이를 해결해주는것이 JPA
SQL 문이 아닌 Method 를 통해 DB 를 조작하므로, 비즈니스 로직 구성에만 집중 가능
특정 DB 에 종속되지 X
DB 를 바꿔야 할 경우, 각 EB 마다 쿼리문이 다르므로 전체적인 수정을 해야하지만
JPA 를 사용할 경우, 설정 파일만 바꿔서 DB 를 얼마든지 변경 가능
DB 테이블에 새로운 컬럼이 추가된 경우,
JPA를 사용 하지 않으면, DTO 클래스의 필드를 모두 변경해야하지만
JPA를 사용하면, 테이블과 mapping 된 Class에 필드만 추가하면 된다.
SQL 문을 직접 작성하지 X
→ 유지보수, 재사용성 좋음
JPA 학습에 시간 多
프로젝트 규모가 크거나 or 복잡 or 설계 오류 일 경우, 속도↓ + 일관성 X
복잡한 쿼리 사용 할 경우(통계 같은), 속도를 위해 별도의 튜닝일 필요해져서 SQL 을 사용해야할 경우가 발생
public enum CascadeType{
ALL, // 모두적용
PERSIST, // 영속화
MERGE, // 병합
REMOVE, // 삭제
REFRESH, // 새로고침
DETACH // DETACH
}
jpa.persist(member);
Member member = jpa.find(memberId);
Spring Data JPA
EntityManagerFactory
Persistence 를 통해 생성가능
Persistence.createEntityManagerFactory("unit명");
EntityManager 를 생성하는 Factory
MyBatis 에서의 SqlSessionFactory 에 해당
어플리케이션 전체에서 하나만 생성해서 공유해야 함
최근의 프로그램들은 성능을 위해 여러 Thread들이 일을 같이하게 되어 있음
→ 하나의 큰 일을 동시에 처리하려다 보면, '동시성 문제' 발생!
해결법?
이를 방지하기 위해, 특정 리소스나 정보는 공유하지 못하게 하는 등... 처리가 필요.
→ EntityManager 에서 공유하면 안되는 특정 리소스나 정보를 여러 Thread가 하나의 EntityManager를 이용 할 수 없도록 처리!
→ 그래서, EntityManagerFactory 에서 필요 할 때마다, 여러개의 EntityManager를 생성하는 구조로 설계한 것
(하나의 Thread가 하나의 EntityManager를 이용하도록!)
EntityManagerFactory가 있기 때문에,
EntityManager를 여러개 생성할 필요 X
매번 모든과정을 다시하지 X
일의 비용 ↓
EntityManager
영속성 컨텍스트 내에서 Entity들을 관리
JPA에서 제공하는 interface로 spring bean으로 등록되어 있어, Autowired로 사용 가능
@Autowired
private EntityManager entityManager;
Query Method, Simple JPA repository는 직접적으로 entityManager를 사용하지 않도록 한번 더 감싸준 것
spring jpa 에서 제공하지 않는 기능을 사용 or 특별한 문제가 있어서 별도로 customizing을 해야한다면, entityManager를 직접 받아서 처리
Entity Cache를 갖고 있음
entityManager 와 em 은 코드에서 동일한 의미
Spring Data JPA 를 사용했을 경우
// Entity를 생성!
Member minsook = new Member();
member.setId("abcd1234");
member.setUsername("민숙");
//아래의 내용도 똑같은 과정
memberRepository.save(minsook);
memberRepository.find();
Spring Data JPA 를 사용하지 않을 경우
1. Entity를 생성!
Member minsook = new Member();
member.setId("abcd1234");
member.setUsername("민숙");
//EntityManager를 생성해줄 EntityManagerFactory를 만들어야합니다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa심화주차");
// Entity를 관리해줄 EntityManager를 EntityManagerFactory에서 생성!
EntityManager em = emf.createEntityManager();
// 엔티티를 영속화(저장)
em.persist(minsook);
// 엔티티를 찾기
em.find(Member.class, 100L);
참고: JPA, Spring Data JPA, Hibernate 비교
Cascade = "연쇄"
특정 엔티티를 영속 상태로 만들 때, 연관된 엔티티도 함께 영속 상태로 만들고 싶은 경우에 사용
→ 즉, 영속성 전이를 사용하면, 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장
JPA 는 CASCADE 옵션으로 영속성 전이를 제공
영속성 전이는 연관관계를 매핑하는 것과 아무 관련 X
단지, 엔티티를 영속화 할 때 연관된 엔티티도 같이 영속화하는 편리함을 제공할 뿐이다.
//영속성 전이 설정
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
private List<Address> addresses;
@Setter
@Getter
@Entity
public class Parent {
@Id @GeneratedValue
@Column(name = "PARENT_ID")
private Long id;
private String name;
// cascade = CascadeType.PERSIST : 부모를 영속화 할 때, 자식들도 같이 영속화
@OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
private List<Child> children = new ArrayList<Child>();
}
// cascade = CascadeType.PERSIST 덕분에, 부모와 자식 엔티티를 한 번에 영속화 할 수 있게 됨
@Test
@Transactional
@Rollback(false)
public void printUser() throws Exception {
// 1번 자식 저장
Child child1 = new Child();
// 2번 자식 저장
Child child2 = new Child();
Parent parent = new Parent();
parent.setName("임종수");
child1.setName("임준영");
child2.setName("임주리");
child1.setParent(parent); // 자식 -> 부모 연관관계 설정
child2.setParent(parent); // 자식 -> 부모 연관관계 설정
parent.getChildren().add(child1); // 부모 -> 자식
parent.getChildren().add(child2); // 부모 -> 자식
// 부모 저장
entityManager.persist(parent);
}
CASCADE 실행
쿼리 결과를 보면 데이터가 정상적으로 2건 입력된 것을 확인할 수 있다.
부모와 자식 엔티티를 모두 일일이 제거하려면
Parent findParent = em.find(Parent.class, 1L);
Child findChild1 = em.find(Child.class, 1L);
Child findChild2 = em.find(Child.class, 2L);
em.remove(findChild1);
em.remove(findChild2);
em.remove(findParent);
영속성 전이를 이용해서 제거하려면
1.
@Setter
@Getter
@Entity
public class Parent {
@Id @GeneratedValue
@Column(name = "PARENT_ID")
private Long id;
private String name;
// cascade = CascadeType.REMOVE : 부모를 삭제 할 때, 자식들도 같이 삭제
@OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
private List<Child> children = new ArrayList<Child>();
}
// 부모 엔티티만 삭제하면 연관된 자식 엔티티도 함께 삭제 됩니다.
Parent findParent = em.find(Parent.class, 1L);
em.remove(findParent);
Post게시글, Image사진
Post게시글에 첨부되는 Image사진들은 Post게시글이 생성될때 함께 생성되고 삭제될때 함께 삭제된다.
// Image 엔티티
@OneToMany(cascade = CascadeType.ALL)
private List<Image> iamge;
따라서, 이렇게 해도 아무런 문제가 없다.
Post게시글, Comment댓글
Comment댓글이 달린 Post게시글이 삭제될 경우, 유저는 본인이 쓴 Comment댓글을 더이상 확인할 수 없게 된다.
(유저 본인이 쓴 댓글이 필요가 없으면 Cascade를 걸어도 되겠지만, 그렇지 않은 경우라면 Cascade를 걸면 안될 것이다.)
cascade = {CascadeType.PERSIST, CascadeType.REMOVE}
em.persist(), em.remove()를 실행 할 때 바로 전이가 발생하지 않고, flush 를 호출 할 때 전이가 발생
예시 1
@OneToMany(mappedBy="parent", cascade=CascadeType.ALL, orphanRemoval=true)
private List<Child> children = new ArrayList<>();
예시 2
//자식을 저장하려면 부모에 등록만 하면 됩니다.
Parent parent = em.find(Parent.class , parentId);
parent.addChild(child);
//자식을 삭제하려면 부모에서 제거하면 됩니다.
Parent parent = em.find(Parent.class , parentId);
parent.getChildren().remove(removeObject);
부모 엔티티와 연관관계가 끊어진 자식 엔티티
@OneToMany(mappedBy="parent", orphanRemoval=true)
private List<Child> children = new ArrayList<>();
부모 엔티티에서 orphanRemoval 옵션을 true 로 주어서, 자동 삭제하도록 한다.
(단, @OneToOne, @OneToMany 만 사용가능)
주의! 2가지 조건을 모두 만족할 경우에만 사용
조건 1 : 해당 엔티티를 참조하는 곳이 하나
조건 2 : 특정 엔티티가 개인 소유하는 경우
그렇지 않은 경우에 사용하면, 해당 엔티티를 참조하는 다른 곳에서 조회가 실패되어 문제가 발생
@Setter
@Getter
@Entity
public class Parent {
@Id @GeneratedValue
@Column(name = "PARENT_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "parent", orphanRemoval = true)
private List<Child> children = new ArrayList<Child>();
}
Parent parent1 = em.find(Parent.lcass, id);
parent1.getChildren().remove(0); // 자식 엔티티를 컬렉션에서 제거
// 모든 자식 엔티티를 제거하려면
// parent1.getChildren().clear();
cascade REMOVE 와 orphanRemoval = true 차이점
1. orhanRemoval 은 관계 여부에 따라 달라진다.
자식 엔티티가 삭제된게 아니더라도, 부모와 관계가 끊어지면 자식 엔티티는 지워진다.
orphanRemoval이 cascade remove보다 큰 개념.
고아객체 자동 삭제(orphanRemoval = true) 설정은 자식 엔티티가 해당 부모 엔티티랑만 관계를 맺을 때 사용해야한다.
그렇지 않으면 다른 관계들도 삭제가 된다.관계가 끊어져도 자식엔티티가 존재해야하는 상황에서는 사용 X
public interface MemberRepository extends JpaRepository<Member, Long>{
List<Member> findByEmailAndName(String email, String name);
}
repository 에서 findBy 사용 시 자동 완성으로 코드를 완성하게 되는데JPA
를 활용하기 위해서는
객체와 관계형 데이터베이스를 매핑(Object Relational Mapping)
과 영속성 컨텍스트
이 2 가지를 이해할 필요가 있다.
참고: 영속성 컨텍스트 (persistence context)
참고: JPA 다대일 단방향 양방향
참고: JPA 연관 관계 한방에 정리 (단방향/양방향, 연관 관계의 주인, 일대일, 다대일, 일대다, 다대다)
참고: 다대다
참고: JPA의 정의와 장·단점
참고: JPA란 무엇인가 (Before JPA, 영속성 컨텍스트, Entity, JPQL, 트랜잭션, N+1 문제)
참고: JPA 영속성 전이
참고: 알고 쓰는 Cascade(영속성 전이)
참고: [JPA] 영속성 전이 (CASCADE)
참고: JPA 영속성 전이
참고: 프레임워크(Framework)란? 개념, 장단점, 종류