반응형
들어가며
이 포스팅의 내용은 «자바 ORM 표준 JPA 프로그래밍»을 참고하고 정리한 것입니다.
SQL 중심적인 개발의 배경
- JPA와 모던 자바 데이터 저장 기술
- 애플리케이션 객체 지향 언어 (Java, Scala 등) + 관계형 DB (Oracle, MySQL 등)
- 객체를 영구 보관하는 다양한 저장소 (RDB, NoSQL, File, OODB 등)가 존재하지만 현실적인 대안은 관계형 DB다.
- 즉, 객체를 관계형 DB에 저장해서 관리하는 것이 중요하다.
- 관계형 DB를 사용하려면 SQL을 짤 수 밖에 없다.
- 관계형 DB를 쓰는 상황에서는 SQL에 의존적인 개발을 피하기 어렵다.
- 하지만! SQL 중심적인 개발에는 여러 문제점이 있다.
SQL 중심적인 개발의 문제점
지루한 코드의 무한 반복
- CRUD의 반복
- 자바 객체를 SQL로, SQL을 자바 객체로 변환하는 과정의 반복
- 예시)
- 회원에 나이 정보를 추가하고자 한다.
- Member 클래스에 'private int age'를 추가한다.
- INSERT, SELECT, UPDATE 등 관련된 모든 쿼리에 age 정보를 추가한다.
객체 지향과 관계형 데이터베이스 간의 패러다임 불일치
- 객체 지향
- 필드와 메서드 등을 묶어서 잘 캡슐화해서 사용하는 것이 목표
- 객체 지향 프로그래밍은 추상화, 캡슐화, 정보은닉, 상속, 다형성 등 시스템의 복잡성을 제어할 수 있는 다양한 장치들을 제공한다.
- 관계형 데이터베이스
- 데이터를 잘 정규화해서 보관하는 것이 목표
- 패러다임이 다른 두 가지를 가지고 억지로 매핑하기 때문에 여러 가지 문제가 발생한다.
- Object를 RDB에 넣으려고 하니까 문제가 발생한다.
- 하지만, RDB가 인식할 수 있는 것은 SQL뿐이기 때문에 결국 SQL 작업이 많다.
- Object -> [SQL 변환] -> RDB에 저장
객체(Object)와 관계형 데이터베이스(RDB)의 차이
상속
- Object
- 상속 관계가 있다.
- RDB
- 상속 관계가 없다
- Object의 상속 관계와 유사한 물리 모델로, Table 슈퍼타입-서브타입 관계가 존재한다.
- Album 객체를 DB에 저장하는 과정
- 객체를 분해한다. (Album 객체는 Item의 속성을 모두 가짐)
- 각각 다른 테이블에 대한 INSERT 쿼리를 두 번 날린다.
INSERT INTO ITEM ...
INSERT INTO ALBUM ...
- Album 을 조회하는 과정 (문제!!!)
- 각각의 테이블에 따른 Join SQL을 작성한다. (Item과 Album을 Join해서 데이터를 가져온다.)
- 각각의 객체를 생성하고 모든 필드 값을 세팅한다.(Item과 Album 각각 모든 필드값을 세팅한다.)
- Movie, Book을 조회하고 싶으면 위의 과정을 또 반복해야 한다.
DB에 저장할 객체에는 상속 관계를 사용하지 않는다.
- 자바 컬렉션에 저장하면?
- 저장: list.add(album);
- 조회: Album album = list.get(albumId);
- 다형성 활용: Item item = list.get(albumId);
- 심지어 객체이기 때문에 필요하면 부모 타입으로 조회한 후 다형성으로 활용할 수도 있다.
- 자바 컬렉션에 저장하면 굉장히 단순한 작업이 관계형 데이터베이스에 넣고 빼는 순간, 중간중간의 Object와 RDB의 매핑 작업을 개발자가 직접 해줘야 하기 때문에 굉장히 번잡한 일이 된다.
연관 관계
- Object
- 참조(Reference)를 사용하여 연관 관계를 찾는다.
- Ex) member.getTeam();
- 단방향으로만 관계가 존재한다.
- Member -> Team은 가능하지만, Team -> Member는 불가능하다.
- 참조(Reference)를 사용하여 연관 관계를 찾는다.
- RDB
데이터 타입
데이터 식별 방법
모델링 과정에서의 문제
- 객체를 테이블에 맞추어 모델링
class Member {
String id; // MEMBER_ID 칼럼사용
Long teamId; // TEAM_ID PK 칼럼사용
String username; //USERNAME 칼럼사용
}
class Team {
Loing id; //TEAM_ID PK 사용
String name; //NAME 칼럼사용
}
객체의 참조값 자체가 필드에 들어가는 것이 더 객체지향스럽다고 할 수 있다.
- 객체지향 모델링
class Member {
String id; // MEMBER_ID 칼럼사용
Team team; // 참조로 연관관계를 맺는다.
String username; //USERNAME 칼럼사용
}
class Team {
Loing id; //TEAM_ID PK 사용
String name; //NAME 칼럼사용
}
- Team 객체의 참조값 자체를 필드에 넣는다.
- Team team;
- 이렇게 설계된 객체를 DB에 저장하려면,
- member.getTeam().getId() 로 값을 얻어와 TEAM_ID에 넣는다.
- INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME) VALUES ...
- Member 객체를 조회하려면,
- MEMBER와 TEAM을 Join 해서 데이터를 한 번에 모두 가져온다.
- Member와 Team에 대한 값을 각각 모두 세팅한다.
- member.setTeam(team);과 같이 직접 연관 관계를 맺어 준다.
- 해당 member 객체를 반환한다.
객체 그래프 탐색에서의 문제
- 객체는 자유롭게 객체 그래프(연관 관계가 있는 객체 사이)를 탐색할 수 있어야 한다.
- member.getTeam(), member.getOrder().getOrderItem() 등 …
- 하지만, 서비스 로직에서 RDB와 연결된 데이터를 탐색할 때 객체 그래프를 탐색할 수 없다.
- Why? 처음 실행하는 SQL에 따라 탐색 범위가 결정되기 때문이다.
- Ex) 처음 SQL에서 Member와 Team을 가져왔다고 하면, Order는 가져오지 않았기 때문에 member.getOrder()는 null이 된다.
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
member.getTeam(); // OK
member.getOrder(); // null
“비교하기” 에서의 차이
- 일반적인 SQL을 사용하는 경우
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(...);
}
}
- 식별자가 같아도 DAO의 getMember()에서
- 각각에 대해서 SELECT Query를 날리고 (두 번 날림)
- new Member()로 객체를 생성하기 때문에 당연히 다르다.
- 자바 컬렉션에서 조회하는 경우
String memberId = "100";
Member member1 = list.get(memberId);
Member member2 = list.get(memberId);
member1 == member2; // 같다!!! (참조값이 같다.)
식별자가 같을 때 컬렉션에서의 두 객체의 참조값은 같기 때문에 두 객체는 같다.
마무리
- 객체답게 모델링할수록 매핑 작업만 늘어난다.
- 객체를 자바 컬렉션에 저장하고 불러오듯이 DB에 저장할 수는 없을까?
- 이 고민의 결과가 바로 JPA(Java Persistence API)이다
반응형
'스프링 > Spring Data' 카테고리의 다른 글
Hello JPA!! 애플리케이션 개발하기 (0) | 2021.05.20 |
---|---|
JPA 소개 및 JPA의 기본 동작 과정 (0) | 2021.05.20 |