목표
객체 - 테이블 제대로 설계하고 매핑하는방법
기본키 외래키 매핑
1:N, N:1, 1:1, N:M 매핑
JPA
객체를 DB에 저장하는 기술은
JDBC -> MyBatis, JdbcTemplate -> JPA 의 순서로 발전해왔다.
과거에는 객체를 DB에 저장하려면 복잡한 JDBC api와 복잡한 SQL을 작성해야했습니다.
jdbcTemplate 나 mybatis같은 sql 맵퍼가 생겨서 코드가 줄었지만 아직까지 SQL을 작성해야했다.
그러다 JPA가 나오면서 SQL문을 작성할 필요가 없어졌다.
JPA는 마치 자바 컬렉션에 객체를 저장하거나 삭제하거나 수정하는 작업을 할수있게 자바코드를 이용해서 JPA가 적절한 SQL 생성해서 DB에 실행해서 객체를 저장하거나 불러온다.
JPA가 어려운이유
1. 객체와 테이블을 잘 설계하고 매핑하는것이 어려워서 (이게 제일 중요!)
2. JPA의 내부 동작방식을 이해하지 못해서
예를들면 JPA가 언제 SQL을 만들어내고 실행하는지 이해하지 못한다.
SQL 중심적인 개발의 문제점
지금의 시대는 객체를 관계형DB에 관리를 한다. 그런데 문제는 SQL문을 많이 작성해야한다.
이러한 작업은 CRUD의 작업이 무한반복되며 수정사항들이 너무많아진다
public class Member {
private String memberId;
private String name;
private String tel; // 추가
}
Insert into Member(Member_Id, name, tel) values // tel 추가
select Member_Id, name, tel from member m // tel 추가
update member set ... tel // tel 추가
다음 예시를 보면 tel을 추가하면 많은부분의 sql문을 수정해줘야한다. 여기서 실수가 많이생긴다.
객체지향 프로그래밍은 추상화,캡슐화,다형성,상속 등 시스템의 복잡성을 제어할수있는 다양한 장치들을 제공합니다.
Object -> RDB, NoSQl, File, 같이 객체를 저장하는 다양한 저장소가 있다.
현실적으로는 RDB를 많이사용하고 NoSQL은 보조로 많이 사용한다.
객체와 관계형 DB의 차이
1. 상속
상속관계가 DB에는 없다. 그래서 부모같은 테이블과 자식같은 테이블 만들어서 데이터를 분리하는 슈퍼타입 서브타입 관계가 있다.
삽입: 객체 분해 -> item -> album
조회: 조인 SQL 작성 -> 객체 생성 -> 복잡... !!
이런 복잡한과정을 자바 컬렉션처럼 하면?
삽입: list.add(album)
조회: list.get(albumId)
이처럼 간단하게 가능!
2. 연관관계
객체는 참조를 사용해 연관관게를 갖는다.. member.getTeam()
테이블은 외래키를 사용해서 연관관계를 갖는다.: Join on m.Team_Id = T.Team.Id
class Member {
String id; // member_id 칼럼 사용
Long teamId; // Team_id 외래키 칼럼 사용
String username; // Username 칼럼사용.
}
class Team {
Long id;
String name;
}
Insert Into member(member_Id, team_id, username) values.. // team_id 필요
// 객체다운 모델링
class Member {
String id;
Team team; // 참조로 연관관계 맺는다.
String username;
}
// 문제점.
insert into member(member_id, team_id, username) values // teamid는 없어.
// member.getTeam().getId()같은 로직으로 사용시 복잡하다.!
3. 데이터타입
위같이 객체다운 모델링으로 DB를 설계시 해당값이 null이 될수도있고 값이있을수도있고 해서 엔티티의 신뢰 문제가 발생.
member.getTeam() // ??
member.getOrder().getDelivery() // ??
그래서 보통 memberDAO.getMember() //member 만 조회
memberDAO.getMemberWithOrderwithDelivery() // member, order, delivery 조회
진정한 의미의 계층 분활이 어렵다.
4. 데이터 식별방법
다음과같이 member1,2는 객체가 다르다.
하지만
결론은 객체다운모델링으로 작업시 매핑작업이 늘어난다.
JPA 소개(SQL 중심개발의 대안으로 JPA 소개)
그래서 객체를 자바 컬렉션에 저장하듯이 DB에 저장해보자! 해서 JPA가 등장!!
JPA(java persistence API) 로 자바의 ORM 기술 표준이다.
ORM은? (object-relational mapping(객체 관계 매핑))
객체는 객체대로 관계형 DB는 관계형 DB로 설계
ORM 프레임워크가 중간에서 매핑을 해줌.
자바 말고도 대중적인 언어에는 대부분 ORM이 존재합니다.
JPA는 DB와 통신하려면 JDBC API를 써서 통신하는데 JPA가 대신해준다
JPA는 member객체를 저장한다 칠떄 memberDAO의 객체를 JPA에 던지면 Entity분석해서 SQL문 생성하고 JDBC API를 통해 DB에 반영한다. 이는 패러다임의 불일치 문제를 해결할수있다.
JPA에게 id 던져주면 JPA가 SQL 문 생성해서 JDBC API와 ResultSet 매핑하고 결과값만 반환해준다.
과거에는 EJB(엔티티빈(자바표준)) 이 있었는데 너무 복잡해서 많은개발자가 안썻다. 그러다 하이버네이트 오픈소스를 만들었었다. 그러다 자바로 작성할수있게 JPA를 만들었고 자바 표준(인터페이스 모음)이 되었다.
JPA는 인터페이스의 모음
jpa 2.1표준 명세를 구현한 3가지 구현체 있는데 그중에 hibernate가 가장많이 쓰임.
JPA 1.0 2006: 초기버전. 복합키 와 연관관계 기능이 부족
JPA 2.0 2009 : 대부분 ORM기능을 포함하고 JPA criteria가 추가됨.,( 2.0이후부터는 크게 변한게 없다)
JPA 2.1 2013: 스토어드 프로시저 접근, 컨버터, 엔티티 그래프 기능이 추가.
JPA를 왜 사용하나?
1. SQL 중심개발 -> 객체 중심으로 개발 가능!
2.생산성이 올라간다.
ex) JPA의 CRUD
저장: jpa.persist(member)
조회: Member member = jpa.find(Member.class, memberId)
수정: member.setName("변경할이름")
삭제: jpa.remove(member)
3. JPA는 동일한 트랜잭션안에서 조회한 엔티티는 같음을 보장.
4. JPA의 성능 최적화 기능
- 1차 캐시와 동일성 보장
: 같은 트랜잭션안에서는 같은 엔티티를 반환해준다. 그래서 약간의 조회 성능이 항상됨.
String memberid = "100";
Member m1 = jpa.find(Member.class, memberId); // SQL (SQL 1번만 실행)
Member m2 = jpa.find(Member.class, memberId); // 캐시
m1 == m2 // true.
- 트랜잭션을 지원하는 쓰기 지연
트랜잭션을 커밋할때 insert sql을 모아서 보낼수있다.
transaction.begin() // 트랜잭션 시작
em.persist(memberA)
em.persist(memberB)
em.persist(memberC)
// 여기까진 insert sql 을 DB에 안보냄.
transaction.commit() // 커밋할떄 Db에 Insert SQL 모아서 보낸다.
- 지연로딩과 즉시로딩
지연로딩: 객체가 실제 사용될떄 로딩 (team을 자주 안쓰고 member만 쓰는경우엔 이게 더 적합)
즉시로딩: jogin sql로 한번에 연관된 객체까지 미리 조회 (team을 자주 사용하면 같이 불러오는게 더 적합)