1.2 패러다임의 불일치
어플리케이션이 발전하면서 내부의 복잡성은 커짐.
"객체지향 프로그래밍"은 추상화, 캡슐화, 정보은닉, 상속, 다형성 등 시스템의 복잡성을 제어할 수 있는 다양한 정치들을 제공.
"관계형 데이타베이스"는 데이터 중심으로 구조화, 집합적인 사고 필요. 추상화, 상속 다형성 같은 개념이 없다.
객체와 관계형 데이타베이스의 패러다임 불일치. 패러다임 불일치 문제를 해결하는데 시간과 코드 소비.
1.2.1 상속
객체는 상속이라는 기능을 가지고 있지만 테이블은 상속이라는 기능이 없다.
객체 상속 모델
테이블 설계
객체 모델 코드
abstract class Item {
Long id;
String name;
int price;
}
class Album extends Item {
String artist;
}
class Movie extends Item {
String director;
String actor;
}
class Book extends Item {
String author;
String isbn;
}
관련 SQL
-- Album 객체 저장
INSERT INTO ITEM ...
INSERT INTO ALBUM ...
-- Movie 객체 저장
INSERT INTO ITEM ...
INSERT INTO MOVIE ...
관련 처리
- 부모 객체에서 부모 데이타만 꺼냄
- ITEM용 INSERT SQL 작성
- 자식 객체에서 자식 데이터만 꺼내서 ALBUM INSERT SQL 작성
- (조회) ITEM과 ALBUM 테이블을 조인해서 그 결과를 다시 Album 객체 생성
만약 컬렉션 스타일로 한다면
list.add(album);
list.add(movie);
Album album = list.get(albumId);
JPA와 상속
JPA는 상속과 관련한 패러다임의 불일치 문제를 개발자 대신 해결
자바 컬렉션에 객체를 저장하듯이 JPA에게 객체를 저장.
JPA 저장
jpa.persist(album);
JPA는 다음 SQL을 실행해서 객체를 ITEM, ALBUM 두 테이블에 나누어 저장
INSERT INTO ITEM ...
INSERT INTO ALBUM ...
JPA 조회
String albumId = "id100";
Album album = jpa.find(Album.class, albumId);
JPA는 ITEM과 ALBUM 두 테이블을 조인해서 필요한 데이터를 조회하고 결과를 반환
SELECT I.*, A.*
FROM ITEM I
JOIN ALBUM A ON I.ITEM_ID = A.ITEM_ID
1.2.2 연관관계
- 객체는 참조를 사용해서 다른 객체와 연관관계를 가지고
참조에 접근해서 연관된 객체를 조회
- 테이블은 외래 키를 사용해서 다른 테이블과 연관관계를 가지고
조인을 사용해서 연관된 테이블을 조회
객체를 테이블에 맞춰 모델링
- 관계형 데이터베이스 방식에 맞추면 Member 객체와 연관된 Team 객체를 참조를 통해서 조회할 수 없다.
- 좋은 객체 모델링은 기대하기 어렵고 결국 객체지향의 특징을 잃어버린다.
class Member {
String id; // MEMBER_ID 컬럼 사용
Long teamId; // TEAM_ID FK 컬럼 사용
String userName;
}
class Team {
Long id; // TEAM_ID PK 사용
String name;
}
객체지향 모델링
객체지향 모델링을 사용하면 객체를 테이블에 저장하거나 조회하기는 쉽지 않다.
객체 모델은 외래 키가 필요 없고 단지 참조만 있으면 된다.
테이블은 참조가 필요 없고 외래 키만 있으면 된다.
결국, 개발자가 중간에서 변환 역활을 해야 한다.
// 참조를 사용하는 객체 모델
class Member {
String id;
Team team; // 참조로 연관관계를 맺는다.
String username;
Team getTeam() {
return team;
}
}
class Team {
Long id;
String name;
}
JPA와 연관관계
JPA는 연관관계와 관련한
패러다임 불일치 문제
를 해결해준다.
member.setTeam(team); // 회원과 팀 연관관계 설정 jap.persist(member); // 회원과 연관관계 함께 저장
객체를 조회할 때 외래 키를 참조로 변환하는 일도 JPA가 처리
Member member = jpa.find(Member.class, memberId); Team team = member.getTeam();
1.2.3 객체 그래프 탐색
객체에서 회원이 소속된 팀을 조회할 때 참조를 사용해서 연관된 팀을 찾으면 되는데 이것을 "객체 그래프 탐색"이라 한다.
member.getOrder().getOrderItem()... // 자유로운 객체 그래프 탐색
SQL을 직접 다루면 처음 실행하는 SQL에 따라 객체 그래프를 어디까지 탐색할 수 있을지 정해진다.
// 회원 조회 비지니스 로직
class MemberService {
...
public void process() {
Member member = memberDAO.find(memberId);
member.getTeam(); // member->team 객체 그래프 탐색이 가능한다.?
member.getOrder().getDelivery(); // ???
}
}
``
### JAP와 객체 그래프 탐색
> JPA를 사용하면 객체 그래프를 마음껏 탐색
```java
member.getOrder().getOrderItem()... // 자유로운 객체 그래프 탐색
JPA는 연관된 객체를 사용하는 시점에 적절한 SELECT SQL 실행
실제 객체를 사용하는 시점까지 데이타베이스 조회를 미룬다. = 지연로딩
투명한 엔티티
class Member {
private Order order;
public Order getOrder() {
return order;
}
}
지연로딩 사용
// 처음 조회 시점에 SELECT MEMBER SQL
Member member = jpa.find(Member.class, memberId);
Order order = member.getOrder();
order.getOrderDate(); // Order를 사용하는 시점에 SELECT ORDER SQL
1.2.4 비교
- 데이터베이스 : 기본 키의 값으로 각 ROW를 구분
- 객체 : 동일성(Identity) 비교와 동등성(Equality) 비교
동일성 비교와 동등성 비교
- 동일성 비교 : == 비교, 객체 인스턴스의 주소값 비교
- 동등성 비교 : equals() 메소드를 사용해서 객체 내부의 값을 비교
// MemberDAO 코드
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 = memberDAO.getMember(memberId);
Member member2 = memberDAO.getMember(memberId);
member1 == member2; //다르다.
JPA와 비교
JPA는 같은 트랜잭션일 때 같은 객체가 조회되는 것을 보장
// member1과 member2는 동일성 비교에 성공
String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);
member1 == member2; //같다.