우선 JPA가 왜 필요할까?
1. 현재는 SQL 중심적인 개발을 하고 있기 때문이다.
2. JAVA는 객체지향이고 데이터베이스는 보통 RDB(관계형 데이터베이스) 를 쓰기 때문이다.
CRUD의 무한반복이다.
자바객체를 SQL로 만들고 SQL을 자바 객체로 만들고..
테이블 하나를 생성해도 CRUD를 다 만들어야 하고
컬럼하나를 추가하려고 해도 관련 SQL을 전부 수정해야한다. 그렇기 때문에 실수할 가능성도 높아진다.
관계형 DB랑 객체는 패러다임이 너무 다르다.
다른 둘을 합치려고 하니..
개발자는 SQL매퍼의 역할을 할뿐 ㅎㅎ..
객체에는 상속관계가 있다.
관계형 DB는 상속관계가 없다.
(있긴한데 객체랑 다른개념)
아이템을 상속받는 ALBUM, MOVIE, BOOK 이 있다.
너무 번거롭고 귀찮으니까 db에 저장할 객체에는 상속관계를 안쓴다
list.add(album)
Album album = list.get(albumId)
Item item = list.get(albumId)
객체는 참조를 사용한다 member.getTeam()
* 객체는 Member에서 Team으로 갈 수가 없다. (Tema -> Member만 가능하다.
테이블은 외래키를 사용한다. JOIN ON M.TEAM_ID = T.TEAM_ID
* 테이블은 외래키를 통해 Team으로 갈 수 있다. (Team, Member를 Join 해서 찾는다)
객체지향적으로 객체를 만든다면
class Member {
String id; //MEMBER_ID 컬럼 사용
Team team; //참조로 연관관계를 맺는다. //**
String username;//USERNAME 컬럼 사용
Team getTeam() {
return team;
}
}
class Team {
Long id; //TEAM_ID PK 사용
String name; //NAME 컬럼 사용
}
이걸 저장하려고 한다면?
INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME) VALUES...
에서 TEAM_ID
는
member.getTeam().getId()
로 값을 가져와 Team_ID를 저장한다
이걸 조회하려고 한다면?
데이터를 전부 가져온 후 Member에서 set하고 Team도 set하고 Team을 Member에 set해야한다
public Member find(String memberId) {
//SQL 실행 ...
Member member = new Member();
//데이터베이스에서 조회한 회원 관련 정보를 모두 입력
Team team = new Team();
//데이터베이스에서 조회한 팀 관련 정보를 모두 입력
//회원과 팀 관계 설정
member.setTeam(team); //**
return member;
}
역시나 번거롭다
list.add(member)
Member member = list.get(memberId)
Team team = member.getTeam()
손쉽게 member와 team을 가져올 수 있다
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
...
member.getTeam(); //OK
member.getOrder(); //null
처음에 SQL에서 TEAM과 MEMBER만 가져왔기 때문에 member와 연결되있는 order여도 값을 가져올 수가 없다.
그래서 엔티티의 신뢰에 문제가 발생한다.
class MemberService {
public void process() {
Member member = memberDAO.find(memberId);
member.getTeam(); //???
member.getOrder().getDelivery(); // ???
}
}
위에서 DAO를 들어가보지 않는 이상 getTeam과 getDelivery를 가져올 수 있는지 확인 할 수 없다.
그렇기 때문에 계층형 아키텍처에서 진정한 의미의 계층분할이 어렵게 된다
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; //같다!
결론은! 객체지향언어, RDB 너무너무 안맞아
객체지향적으로 모델링할수록 일만 더 번거로워져..
Collection에 저장하듯이 DB를 사용할 순 없는걸까? => JPA의 등장!
JPA란 Java Persistence API로 자바진영의 ORM 기술 표준이다
JPA는 애플리케이션과 JDBC 사이에서 동작한다
JPA로 명령하면 JDBC API를 이용해서 호출하는 것이다.
쿼리를 개발자가 안만들고 JPA가 대신만들어서 호출해준다.
MemberDAO에서 객체를 저장할 때
Member 객체를 조회하고 싶을 때
여담이지만 EJB가 복잡하고 어려워서 EJB쓰지말자 그냥 순수하게 자바를 이용하자라는 여론이 나오면서 Expert One-on-One: J2EE Development without EJB 라는 책이 나오게 되었다고한다. 그리고 이 책을 기반으로 스프링프레임워크가 탄생하게 되었다고 한다
Hibernate
, EclipseLink
, DataNucleus
가 있다.SQL 중심적인 개발에서 탈출해 객체 중심개발이 가능해진다!
저장: jpa.persist(member)
조회: Member member = jpa.find(memberId)
수정: member.setName(“변경할 이름”)
삭제: jpa.remove(member)
이런 구조가 있다고 생각해보자
jpa.persist(album)
을 하면 알아서 부모클래스에도 저장이된다Album album = jpa.find(Album.class, albumId)
이런식으로 앨범을 조회하면 알아서 조인해서 가져온다.member.setTeam(team);
jpa.persist(member);
Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam();
class MemberService {
...
public void process() {
Member member = memberDAO.find(memberId);
member.getTeam(); //자유로운 객체 그래프 탐색
member.getOrder().getDelivery();
}
}
위의 memberDAO.find()
처럼 이 값이 잘 나오는지 확인할 필요가 없다.
String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);
member1 == member2; //같다.
String memberId = "100";
Member m1 = jpa.find(Member.class, memberId); //SQL
Member m2 = jpa.find(Member.class, memberId); //캐시
println(m1 == m2) //true
이때 SQL은 한번만 실행된다.
성능 향상이 있긴한데 그렇게 획기적으로 향상되는건 아니라고
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 모아서 보낸다.
transaction.commit(); // [트랜잭션] 커밋
Member member = memberDAO.find(memberId); //select문 1번
Team team = member.getTeam();
String teamName = team.getName(); //select 문 2번
가끔씩 Member와 Team을 같이 호출하는거라면 상관없겠지만
Member와 Team이 한쌍이라 늘 같이 호출되는거라면 2번 db를 조회하는게 비효율적이다. 그럴 때 필요한 것이 즉시로딩
Member member = memberDAO.find(memberId); //이때 join으로 team member 같이 호출
Team team = member.getTeam();
String teamName = team.getName();
보통 지연로딩으로 개발을 한 후 최적화가 필요할 때 즉시로딩으로바꾼다
JPA는 JAVA와 RDB 사이의 다른 패러다임을 해소시켜주기 위해 객체지향적으로 DB를 가져올 수 있도록 도와주는 인터페이스다
마치 Collecrion처럼 편하게 CRUD를 할 수 있으며 성능면에서도 우수하기때문에 JPA를 잘 공부해두자 📝
출처
T 아카데미 강의