현 시대는 객체를 관계형 DB에 저장해서 관리를 합니다
그렇다면 관계형 DB안에 객체를 저장하려면 SQL을 이용하여 저장해야 합니다
SQL 중심의 개발이죠 Insert, Update, Select, Delete , Create..등등
1. 무한 반복, 지루한 코드
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 ...
위 객체를 RDB(Relational Database, 관계형 데이터베이스) SQL을 이용하여 저장한 코드입니다
그런데 만약 DB구성을 다 해놓았고 SQL도 다 짜놓았는데
public class Member {
private String memberId;
private String name;
private String tel;
}
아 이렇게 전화번호도 놓아줘~라고 클라이언트가 요구한다면 밑에처럼 SQL을 바꿔줘야겠죠 물론 하나면 되겠지만 그게.. 계속되면
노가다의 반복입니다 또한 문장 하나를 빠드리거나 하면 "실수"가 발생할 수 있겠죠?
INSERT INTO MEMBER(MEMBER_ID, NAME, TEL) VALUES
SELECT MEMBER_ID, NAME ,TEL FROM MEMBER M
UPDATE MEMBER SET ... TEL = ?
2. 엔티티 신뢰 문제
class MemberSerivce {
...
public void process(String id) {
Member member = memberDAO.find(id);
member.getTeam(); //???
member.gerOrder().getDelivery(); // ???
}
}
Member가 있고 Team에 속해있다고 가정해봅시다 위 코드를 보면 객체지향적인 코드라고 볼 수 있다고 생각합니다
그런데 이렇게 할 수 있을까요? 그런데 우리는 memberDAO안에 Team이 있고 Order..이 있는지 알 수가 없습니다
안을 들여다보아야 하죠 이러면 또 문제가 생기죠 다른사람이 짠 코드라면 신뢰할 수 없고 유지보수가 힘들어집니다
3. 패러다임의 불일치
RDB(관계형 DB) vs 객체는 서로 나온 사상이 다릅니다
RDB는 데이터를 어떻게 잘 저장할까 생각으로 나온 것이고 객체는 어떻게 해서 잘 관리할 수 있을까요 추상화할 수 있을까
이렇듯 서로 패러다임의 불일치로 우리는 객체를 어떻게 RDB에 잘 저장할까 항상 생각하죠
그렇다면 이렇게 RDB vs 객체는 잘 안맞는데 꼭 RDB에 넣어서 저장해야 하나?라는 질문이 생기죠
대답은 현실적으로는 지금 대안은 그것뿐이다입니다 또는 NoSQL을 쓸 수도 있지만 현재는 RDB가 주이고 NoSQL은 소수의 데이터만 처리하고 있죠
4. 객체와 RDB의 차이
4-1 상속
RDB에는 상속 관계가 있을까요? 없습니다 왜 없을까요?
RDB에서는 데이터의 중복을 줄이고 일관성을 높이기 위해 하나의 데이터를 여러개의 테이블로 쪼개어 저장하고 필요할 때 조인을 해서 사용하게 됩니다 하지만
객체지향에서는 하나의 객체가 다른 객체에 대한 참조를 포함하며, 연관된 두 객체는 모두 메모리상의 존재하기 대문에 하나의 객체로 이와 연관된 객체들의 데이터를 손쉽게 얻을 수 있죠
자 이제 Album을 저장하려고 합니다
1. 객체 지향 설계 위와 같이 해놓았죠
2. SQL을 짜야겠죠 Item이랑 Album Insert 쿼리 2개를 사용합니다
INSERT INTO Album, INSERT INTO Item
Album 조회 JOIN쿼리를 이용하여 조회를 하면 됩니다
하지만 만약 Movie도 조회하려면 또 다른 INSERT, JOIN 또 반복된 노가다입니다..
그래서 DB에 저장할 객체에는 상속 관계를 사용하지 않습니다 복잡해지고 할일이 많아지니까요
그래서 보통 슈퍼 Item 쿼리를 만듭니다 한번에 모든 것을 해주는 SQL를!
자바 List 컬렉션에 넣는다면 그냥 들어갑니다 한 줄이면 list.add(album) 조회시 Album album = list.get(albumId) 한 줄이면 되죠
그런데 DB에 넣으려면.. 또 많은 쿼리가 발생하겠죠
4-2 연관관계
위 객체는 참조를 사용합니다 : member.getTeam(), 테이블은 외래 키를 사용: JOIN ON M.TEAM_ID = T>TEAM_ID
객체는 Member -> Team으로 갈 수 있지만 Team -> Member로 갈 수가 없습니다..
Member Team 조인시 -> Team 가능
Team -> Member 가능
하지만 객체의 참조를 사용한 관계는 방향이 있는데 DB의 참조키는 방향성이 없습니다!! (중요)
teamId라는 외래키를 Member class에 그대로 넣습니다 이렇게 외래키를 1:1로 매칭합니다
이렇게하면 teamId만 꺼낼 수 있지 Teamd을 꺼낼수는 없죠
하지만 이렇게하면 한방에 쿼리를 짤 수 있습니다 밑에 코드처럼 말이죠
그러면 이렇게 말고 객체지향으로 한번 코드를 짜볼까요?
Member.getTeam()을 하면 바로 Team 객체를 가져올 수 있죠? 객체지향을 잘 이용했습니다 그런데 Team을 어떻게 가져오져
원하는 Team을 가져올 수 없습니다 밑에처럼 Team_id를 쿼리에 넣을 방법이 없습니다!!
그렇다면 member.getTeam().getId()를 통해 가져오도록 만들어야겠죠
위처럼 Member.getTeam()을 하려면 Member에 Team을 넣고 쿼리문으로 Team이랑 Member를 조인합니다
그런데 쿼리문이 엄청 길어졌죠 복잡합니다
이렇게 돼어 있는 Class 들이 있습니다 연관관계가 맺어져있죠 그런데 안에 관계가 맺어져있는지 확인을 해봐야겟죠
밑에는 Team은 쿼리했지만 Order은 되지 않습니다 이렇듯 처음 실행하는 SQL에 따라 탐색 범위가 결정이됩니다
이렇게하면 member1!=member2가 됩니다 new를 써서새로 member 인스턴스 2개 생성하기 때문에 다릅니다
이렇게 하면 같죠 같은 메모리를 참조하니까요
그러면 객체를 자바 컬렉션에 저장하듯이 DB에 저장할 수 없을까 왜냐하면 객체답게 모델링 할수록 매핑 작업만 늘어나기 때문입니다
위에서 계속 설명한 내용입니다
그래서 JAVA 진형에서는 JPA(Java Persistence API)를 만듭니다 Persistence: 영구저장이라고 생각하면 됩니다
ORM(Object-Reational Mapping)프레임워크가 중간에서 매핑을 해주는 역할을 해줍니다 알아서 SQL을 날려주죠
이렇게 말이죠
이렇게 JPA를 사용하면 SQL 중심 개발에서 객체 중심으로 개발이 가능하고 유지보수가 되며 .. 패러다임의 불일치 해결이 됩니다
JPA의 성능 최적화 기능
1. 1차 캐시와 동일성 보장
이러면 SQL은 2 개가 날라갈까요? 아닙니다 같은 트랜잭션에서 작동하기 때문에 member1은 SQL은 한 번만 날리고 member2는 캐시에서 찾습니다
2. 트랜잭션을 지원하는 쓰기 지연
이렇게 하면 쿼리가 3번 나가야 하지만 JPA를 이용하면 커밋하는 순간! 한번에 쿼리를 모아서 보내줍니다
저는 여기서 참 중요하다고 생각하는데
지연로딩에서는 team객체가 있죠 저기서 team은 쿼리를 써서 찾아내지 않습니다 team.getName()을 쓰면 그제서야 team쿼리를 날려주죠 쓸대없는 쿼리를 날리지 않습니다 하지만 쿼리가 두번 날라가죠?
다음으로 즉시로딩으로는 설정을 하나만 바꾸면 memberDAO.find()만 하면 한번에 getName()까지의 쿼리를 날려줍니다
자주 사용하는 쿼리는 이렇게 바꾸면 되겠죠?
그런데 또 중요한 점은 ... 이렇게 할려면 SQL을 어떻게 짤지 미리 머리에서 로직을 돌리고 바꿀 수 있어야 한다는 겁니다
JPA만 알면 SQL을 기본만 하면 되겠지?
어리석은 생각이였다는 걸 알게 되는 강의였습니다
최적화를 할려면 SQL개발을 먼저 알고 그다음에 JPA를 사용해야되구나를 알게 되었습니다
장애의 90프로는 DB가 차지한다고 합니다!!
RDB & SQL도 같이 공부해야 겠네요 !!
이 글은 밑의 T아카데미의 김영한 강사님의 강의를 보고 쓴 글입니다!
https://www.youtube.com/watch?v=WfrSN9Z7MiA
'스프링 > JPA' 카테고리의 다른 글
[JPA] EAGER Loading Vs LAZy Loading N+1이란? (0) | 2024.08.27 |
---|---|
[Spring] JPA에서 Entity Life의 Cycle은 어떻게 이루어지나?(면접 질문 있음) (0) | 2024.06.14 |
[Spring] JPA Save메소드를 이용한 양방향 매핑 (Gradle) (0) | 2023.01.23 |