인프런 김영한님 강의를 보고 정리한 내용입니다
객체와 관계형 DB의 패러다임이 다르다.
1. 상속관계
- 객체에는 있지만 관계형 DB 에는 없음
- table 수퍼타입 서브타입의 관계가 그나마 객체 상속 관계와 유사함
- 상속 받은 객체를 테이블에 넣으려면 insert를 부모, 자식 2번 넣어야함
- 조회 시 테이블을 join해서 가져온 후 자식에 데이터만 골라서 반환해줘야 함
객체는 참조를 사용해서 get으로 불러오지만 관계형 DB는 foreign key를 이용해서 join
객체의 참조는 단방향이지만 관계형 DB의 join은 양방향 접근이 가능함
// (1) 외래키로 연관관계를 맺는 경우
class Member {
String id; //MEMBER_ID 컬럼 사용
Long teamId; //TEAM_ID FK 컬럼 사용
String username;//USERNAME 컬럼 사용
}
class Team {
Long id; //TEAM_ID PK 사용
String name; //NAME 컬럼 사용
}
// 쿼리문 -- mybatis가 이런걸 잘 해줌, 하지만 객체의 느낌이 없음...
insert into member(member_id, team_id, username) values...
// (2) 객체로 연관관계를 맺는 경우
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 컬럼 사용
}
// 쿼리문 -- join으로 조회한 후 객체에 각각 할당하고 관계도 직접 설정해 줘야함
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
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();
// 이걸 SQL에 매핑하는 순간 너무 복잡해져서 super dto를 만들고 한번에 때려넣게 됨
class MemberDAO {
public Member getMember(String memberId) {
String sql = "SELECT * FROM MEMBER WHERE MEMBER_ID = ?";
...
//JDBC API, SQL 실행
return new Member(...);
}
}
// (1) SQL에서 조회
String memberId = "100";
Member member1 = memberDAO.getMember(memberId);
Member member2 = memberDAO.getMember(memberId);
member1 == member2; //다르다.
// (2) 자바 컬렉션에서 조회
String memberId = "100";
Member member1 = list.get(memberId);
Member member2 = list.get(memberId);
member1 == member2; //같다.
==
)비교와 equality(동등성, equals()
) 비교로 나뉜다.SQL을 사용하면 계층형 아키텍처에서 진정한 의미의 계층 분할이 어렵다.
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
member.getTeam(); //OK
member.getOrder(); //null
엔티티의 신뢰 문제
로 이어진다.Java Persistence API, 자바 ORM 표준
ORM이란?
- Object-relational mapping (객체 관계 매핑)
- 객체와 관계형 DB는 각각 설계 후 ORM 프레임워크가 중간에서 매핑해줌
2.0 이상 쓰면 무리 없이 동작
- JPA 1.0(JSR 220) 2006년 : 초기 버전. 복합 키와 연관관계 기능이 부족
- JPA 2.0(JSR 317) 2009년 : 대부분의 ORM 기능을 포함, JPA Criteria 추가
- JPA 2.1(JSR 338) 2013년 : 스토어드 프로시저 접근, 컨버터(Converter), 엔티
티 그래프 기능이 추가
// 저장을 위해 개발자가 할일
jpa.persist(album);
// 저장을 위해 JPA가 하는 일
INSERT INTO ITEM ...
INSERT INTO ALBUM ...
// 조회를 위해 개발자가 할일
Album album = jpa.find(Album.class, albumId);
// 조회를 위해 JPA가 하는 일
SELECT I.*, A.*
FROM ITEM I
JOIN ALBUM A ON I.ITEM_ID = A.ITEM_ID
// 연관 관계
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();
}
}
지연 로딩
은 실제 조회를 사용하는 시점에 sql이 나가서 데이터가 채워지기 때문String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);
member1 == member2; //같다.
// sql이 한 번만 실행
String memberId = "100";
Member m1 = jpa.find(Member.class, memberId); //SQL
Member m2 = jpa.find(Member.class, memberId); //캐시
println(m1 == m2) //true
JDBC BATCH
SQL 기능을 사용해서 한번에 SQL 전송이 가능하지만 코드가 정말 지저분해짐transaction.begin(); // 트랜잭션 시작
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 모아서 보낸다.
transaction.commit(); // 트랜잭션 커밋
transaction.begin(); // 트랜잭션 시작
changeMember(memberA);
deleteMember(memberB);
비즈니스_로직_수행(); //비즈니스 로직 수행 동안 DB 로우 락이 걸리지 않는다.
//커밋하는 순간 데이터베이스에 UPDATE, DELETE SQL을 보낸다.
transaction.commit(); // 트랜잭션 커밋
// 지연 로딩: 객체가 실제 사용될 때 로딩
Member member = memberDAO.find(memberId); -> SELECT * FROM MEMBER
Team team = member.getTeam();
String teamName = team.getName(); -> SELECT * FROM TEAM
// 즉시 로딩: join sql로 한번에 연관된 객체까지 미리 조회
Member member = memberDAO.find(memberId); -> SELECT M.*, T.* FROM MEMBER JOIN TEAM …
Team team = member.getTeam();
String teamName = team.getName();