
애플리케이션은 객체지향, DB는 보통 관계형 DB(Oracle, MySQL, ...)를 많이 사용합니다. 그래서 객체를 관계형 DB에 관리합니다. 하지만 객체를 관계형 DB에 보관하고 사용하려면 엄청난 양의 DB용 언어인 query를 작성해야 합니다.
public class Member {
private String memberId;
private String name;
...
}
INSERT INTO MEMBER(MEMBER_ID, NAME) VALUES
SELECT MEMBER_ID, NAME FROM MEMBER M
UPDATE MEMBER SET ...
public class Member {
private String memberId;
private String name;
private String tel;
...
}
INSERT INTO MEMBER(MEMBER_ID, NAME, TEL) VALUES
SELECT MEMBER_ID, NAME, TEL FROM MEMBER M
UPDATE MEMBER SET ... TEL = ?
필드를 하나하나 추가해야 합니다.
관계형 DB와 통신하려면 SQL을 사용해야 합니다. 때문에 SQL의 의존적인 개발을 피하긴 어렵습니다.
객체 지향 프로그래밍은 추상화, 캡슐화, 정보은닉, 상속, 다형성 등 시스템의 복잡성을 제어할 수 있는 다양한 장치들을 제공한다.
객체를 영구 보관하는 저장소로 다음과 같은 것들이 있습니다.
객체 ➡️ SQL 변환 ➡️ RDB에 SQL로 전달 과정을 계속 반복해야 합니다.
관계형 DB는 기본적으로 객체에서 생각하는 상속관계가 없습니다. 상속관계와 비슷하게 부모같은 테이블(슈퍼 타입)과 자식같은 테이블(서브 타입)로 분리해 필요할 때는 가져오는 방식으로 사용할 수 있습니다. 데이터베이스 테이블 설계 기법 중 하나로 슈퍼 타입, 서브 타입 관계 라고 합니다.

Album을 저장하려면..
1. Album 객체에서 데이터를 꺼내는 분해
2. INSERT INTO ITEM ...
3. INSERT INTO ALBUM ...
Album을 조회하려면..
1. 각각의 테이블에 따른 JOIN SQL 작성
2. 각각의 객체 생성
3. 데이터를 다 가져오고.. 부모 테이블 확인하고.. 데이터 다시 세팅하고...
하지만, 자바 컬렉션에서 조회하면?
Album album = list.get(albumId);
// 부모 타입으로 조회 후 다형성 활용
Item item = list.get(albumId);

객체는 참조를 사용 : member.getTeam()
Member와 Team이 연관관계가 있다면 member.getTeam() 또는 member.team 을 사용해서 Member와 연관된 Team을 꺼낼 수 있습니다.
테이블은 외래 키 (Foreign Key)를 사용 : JOIN ON M.TEAM_ID = T.TEAM_ID
참조가 없어 외래 키 (Foreign Key, FK)를 사용해 연관관계를 맺습니다.
Team의 PK를 Mebmer가 TEAM_ID로 FK를 가지게 됩니다.
class Memebr {
String id; // MEMBER_ID 컬럼 사용
Long teamId; // TEAM_ID FK 컬럼 사용 //**
String username; // username 컬럼 사용
}
class Team {
Long id; // TEAM_ID PK 사용
String name; // NAME 컬럼 사용
}
INSERT INTO (MEMBER_ID, TEAM_ID, USERNAME) VALUES ...
객체다운 모델링은 아닙니다.
class Memebr {
String id; // MEMBER_ID 컬럼 사용
Team team; // 참조로 연관관계를 맺음 //**
String username; // username 컬럼 사용
}
class Team {
Long id; // TEAM_ID PK 사용
String name; // NAME 컬럼 사용
}
하지만, 객체답게 모델링을 하게 되면 DB에 INSERT하기 까다로워집니다.
INSERT INTO (MEMBER_ID, TEAM_ID, USERNAME) VALUES ...
// Team에 대한 참조만 있을뿐 TEAM_ID가 존재하지 않는다.
대안으로 member.getTeam()에서 id를 가져오면 PK를 FK로 사용할 수 있습니다. (복잡함)
member와 team이 연관관계가 있는 상태로 가져오고 싶다면 member와 team 둘 다 조회를 해야 합니다. DB 테이블에서는 성능을 위해 보통 한 번에 조회를 합니다.
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAN_ID
public Member find(String memebrId) {
// SQL 실행 ...
Member member = new Member();
// DB에서 조회한 회원 관련 정보를 모두 입력
Team team = new Team();
// DB에서 조회한 팀 관련 정보를 모두 입력
// 회원과 팀 관계 설정
member.setTeam(team);
return memebr;
}
list.add(member);
Member member = list.get(memberId);
Team team = member.getTeam();

객체는 참조를 활용해 자유롭게 객체 그래프를 탐색할 수 있어야 합니다.
Java 객체 안에서 DB의 객체를 관리하는 것은 굉장히 까다로운 일입니다.
컨트롤러, 서비스, 리포지터리, DAO 등 계층의 아키첵처는 다음 계층을 믿고 쓸 수 있어야 합니다.
class MemberService {
...
public void process() {
Member member = memberDAO.find(memberId);
member.getTeam(); // ??
member.getOrder().getDelivery(); // ??
}
}
memberDAO를 다른 곳에서 가져온 상황에서 객체 그래프를 자유롭게 탐색할 수 있을까요? DAO를 다 살펴봐야 알 수 있습니다. 하지만 그렇다고 모든 객체를 미리 로딩할 수는 없습니다. 어마어마한 양의 SQL 쿼리를 작성할 수 없기 떄문입니다.
사실 진정한 의미의 계층 분할이 어렵습니다.
String memberId = "100";
Member member1 = memberDAO.getMember(memebrId);
Member member2 = memberDAO.getMember(memebrId);
member1 == member2; // 다르다
일반적으로 SQL을 사용하는 경우입니다. memberId가 100번인 회원을 두 번 꺼내올 때, 이 인스턴스는 다르다고 나옵니다.
class MemberDAO {
public Member getMember(String memberId) {
String sql = "SELECT * FROM MEMBER WHERE MEMBER_ID = ?";
...
// JDBC API, SQL 실행
return new Member(...);
}
}
보통 SQL 쿼리를 짜고 데이터를 다 넣은 새로운 객체를 만들어서 그 안에 SQL 쿼리 결과를 넣어서 반환합니다. 결과적으로 member1와 member2의 데이터는 같지만 다른 인스턴스가 2개 생성됩니다.
String memberId = "100";
Member member1 = list.get(memberId);
Member member2 = list.get(memberId);
member1 == member2; // 같다
자바 컬렉션에서 같은 인스턴스를 조회하게 되면 같은 참조로 나옵니다.
이론적으로 객체 지향적으로 설계하는 게 좋다고 하지만, SQL로 전환하는 과정에서 비용이 너무 많이 발생한다. 객체답게 모델링 할수록 매핑 작업만 늘어난다.
객체를 자바 컬렉션에 저장하듯이 DB에 저장하는 방법이 JPA, Java Persistence API 입니다.