JPA 모던 자바 데이터 저장 기술
Java : 객체 지향 언어 데이터베이스 : 관계형 DB [ Oracle, MySQL, .. ]이다.
지금 시대는 "객체"를 "관계형 DB"에 관리하고 있다.
그래서 RDB는 SQL을 짜야 함. SQL! SQL!! SQL!!!
SQL 중심적인 개발의 문제
-무한 반복, 지루한 코드.. CRUD, CRUD, CRUD 자바 객체 ↔ SQL
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 ... =?
앗 개발자님! 연락처가 빠졌네요? 연락처 추가해주세요! 멤버 객체에 private String tel 넣고, 쿼리 필요한 부분에 tel을 넣습니다. 필요한 부분에 일일이 다 찾아서 넣습니다.. 수 백 개면? 수 백 개 쿼리 수정 해야 햄... 뭐 해야지...
엔티티 신뢰 문제
class MemberService{
...
public void process(String id){
Member member = memberDAO.find(id);
member.getTeam(); // ???
member.getOrder().getDelivery(); // ???
}
}
객체 지향적으로 생각했을 때 이런 식으로 가져올 수 있다. 그냥! 근데 이렇게 가져오려면? 이런 식으로 만들어 둬야해. 안에 Member안에 Team안에도 데이터 들어가 있어야 하고, Member안에 Order안에 Delivery에도 데이터 들어가 있어야 해. 안에 logic 까봐서 제대로 들어가 있는지 체크해봐야 함.. null 들어있을 수 있지. 생각한 거랑 달라. 왜냐고? DB에서 테이블 연결해서 가지고 오고 있어야 하는거지...
SQL에 의존적인 개발을 피하기 어려움
어떤 쿼리를 짜냐에 따라 비즈니스 코드 생긴 게 달라짐...
★패러다임의 불일치
객체 vs 관계형 데이터베이스
관계형 데이트 베이스는 어떻게 데이터 잘~저장할까에 포커싱 되어 있음. 객체는 저장이 목적이 아니라, 어떻게 추상화하고 관리 잘 할까?가 목표.
패러다임이 다른 두 개를 연결하려다 보니 안 맞는 부분이 생긴다.
현실적인 대안은 관계형 데이터베이스
저장하려면? 객체를 SQL로 바꿔야지. 한 땀, 한 땀 뜯어서 바꿔야 해! 개발자 = SQL 매퍼 ㅋㅋ
객체와 관계형 데이터베이스의 차이
상속
객체 지향적으로 설계하면, 그림 왼쪽 처럼 상속 관계를 설계할 수 있다. DB는? 그림 오른쪽처럼 만들게 된다. DTYPE 넣어서 join하는 식으로 사용한다.
관계형 DB에는 상속 관계? 없다. 둘 비슷하게 생겼지만 전혀 다른 패러다임으로 움직이고 있어.
Album 추가해서 팔거야! Album 정보를 DB에 저장해보세요!
- 객체 분해
- INSERT INTO ITEM...
- INSERT INTO Album...
그럼 Album 정보 조회를 해보자.
- 각각의 테이블에 따른 조인 SQL 작성
movie 가져올꺼야? book 가져올꺼야? 모두 조인 SQL 작성해야지! 그래서 나온 꼼수가ㅋㅋ 그냥 super item DTO를 쓰게 됨 아이템, album, movie, book 다 들어 있는거 만들어서 씀 ㅋㅋ 그래서 DB에 넣을 때 상속 관계 크게 신경 안 씀 ㅋㅋ
↔비교해서 생각해봐
자바 컬렉션에 저장하면? list.add(album); 이걸 converting하려니까 복잡해지는 거야.
Album album = list.get(albumId);
Item item = list.get(albumId);
객체 지향적으로 설계를 할 수 없어 ㅋㅋ DB에다가 sql로 객체 담으려다보니까...
연관 관계
객체는 참조를 사용 : member.getTeam();
테이블은 외래 키를 사용 : JOIN ON M.TEAM_ID = T.TEAM_ID
객체는 그냥 member.getTeam()이런 식으로 Team 객체를 가져올 수 있어. DB는 FK를 사용해서 JOIN해서 가져 와.
둘이 비슷한 것 같지만 전혀 다르다. member.getTeam()에서 Team객체를 가져왔어. 반대로 team.getMember() 가능할까? 불가능하다. (방향성이 있다)
RDB는 member에서 team 정보를, team에서 member 정보를 불러올 수 있어. (방향성이 없다. 양방향)
객체를 테이블에 맞추어 모델링한다면?
class Member{
String id;//MEMBER_ID 컬럼 사용
Long teamId;//TEAM_ID FK 컬럼 사용 **
String username; //USERNAME 컬럼 사용
}
class Team{
Long id; //TEAM_ID PK 사용
String name; //NAME 컬럼 사용
}
실제 이렇게 함. Member 클래스에 객체 Team team을 넣는 게 아니라, DB에서 썼던 것 처럼 teamId 외래 키를 그대로 넣는다.
객체 지향적이지 못함... 더 객체 지향적?.. Team team이 있어야 하는데 id만 꺼낼 수 있음. team자체만 꺼낼 수 있는 게 아냐.
그럼에도 왜 teamId 그냥 쓰냐? 그래야 쿼리 짤 때 좀 더 쉽기 때문이야;
INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME) VALUES ...
선배님! 왜 객체 지향 언어인데 객체 지향적으로 언어를 안 짜는 건가요?? 객체 지향적으로 모델링을 해보자.
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 컬럼 사용
}
Member member = new Member(); member.getTeam()하면 객체를 가져올 수 있게 생겼다!
쿼리를 짜보자!
INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME) VALUES ...
TEAM_ID 없네? 그럼 이렇게 member.getTeam().getId(); 꺼내서 넣자. FK니까 이렇게 할 수 있지. 굿굿 실제로 잘 동작도 하네! 보십쇼 팀장님 좋지 않습니깡
자! 조회를 해보자 ㅋㅋ
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
// ORACLE 방식은... 다른 DB로 바꿨을 때 안 돌아간다. JOIN ON으로 학습해야 함 ㅠㅠ
public Member find(String memberId){
//SQL실행 ...
Member member = new Member();
//데이터베이스에서 조회환 회원 관련 정보를 모두 입력
Team team = new Team();
//데이터베이스에서 조회한 팀 관련 정보를 모두 입력
//회원과 팀 관계 설정
member.setTeam(team); //**
return member;
}
SQL실행하고 Member객체 만들고 값 넣고, Team객체 만들고 값 넣고, member.setTeam(team)해 줘 코드가 오지게 길어짐......
그래서 그냥 super 클래스 만들어서 다 때려 넣음...;
↔객체 세상이었다면? list에다가 객체를 채로 넣어! 그러면 리스트에 Team객체를 담고 있는 Member 객체가 들어가있겠지?
list에서 필요한 Member의 getTeam().getId()하면 그대로 값을 가져올 수 있음.
list.add(member);
Member member = list.get(memberId);
Team team = member.getTeam();
패러다임이 다른 곳에 넣었다 빼려니.... 코드가 길어졍 ㅎㅎ
객체 그래프 탐색
객체는 자유롭게 객체 그래프를 탐색할 수 있어야 한다. 연관 되어 있다? 그럼 그냥 조회할 수 있어야 되는거야. get팀.get멤버.get오더.get오더아이템.get아이템.get카테고리 근데 RDB 쓰면서 그게 안돼... 만들어져 있어야만 쓸 수 있어.
처음 실행하는 SQL에 따라 탐색 범위가 결정될 뿐이다.
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
member.getTeam(); //ok
member.getOrder(); //null 조회할 수 없어. 왜냐고? 쿼리에 그렇게 안 만들었으니까.
//근데 제대로 내가 쿼리 만들었나? 확인하려면 까봐야 알 수 있음 ㅋㅋ 한 20개 까진 기억하지
//100개-200개 되면? 다른 사람이 만들면? 또 쿼리문 작성하고?
다시 초반에 봤던 코드를 보면
class MemberService{
...
public void process(String id){
Member member = memberDAO.find(id);
member.getTeam(); // ???
member.getOrder().getDelivery(); // ???
}
}
다른 사람이 로직 짜 놨어. 써도 돼? nullPointerException 터지면 그냥 장애남... 또 하나하나 까봐야지
모든 객체를 미리 로딩할 수 없다
이걸 해결하려면? 상황에 따른 모든 쿼리를 짜놓아 ㅋㅋ
memberDAO.getMemeber(); //member만 조회
memberDao.getMemeberWithTeam(); // member와 team 조회
member.getMemeberWithOrderWithDelivery(); //Member,Order,Delivery
복잡해질 수록? 경우의 수 너무 많아집니다 ㅋㅋㅋㅋㅋ 뭔가 이상하넹...
비교하기
String memberId = "100";
Member member1 = memberDAO.getMemeber(memberId);
Member member2 = memberDAO.getMemeber(memberId);
member1 == member2; //다르다
class MemberDAO{
public member getMemeber(String memberId){
String sql = "SELECT * FROM MEMEBER WHERE MEMBER_ID =?";
...
//JDBC API, SQL 실행
return new Memeber(...);
}
}
member1과 member2는 같지 않다. 객체를 따로 따로 만들어서 값 가져오고 셋팅했기 때문에.
만약 자바의 컬렉션이었다면?
String memberId = "100");
Member member1 = list.get(memberId);
Member member2 = list.get(memberId);
member1 == member2; // 같다
이런 것도.. 다르지? 패러다임이 뭔가 달라. list에 담아서 보관하는 거랑 sql에 담아서 보관하는 거랑 달라. list에 넣는 게 객체 지향 패러다임에 좀 더 가까워.
해당 포스팅은 T아카데미에서 진행한 김영한 강사님의 JPA 유튜브 강의를 듣고 정리한 것입니다 : ) https://www.youtube.com/watch?v=WfrSN9Z7MiA&list=PL9mhQYIlKEhfpMVndI23RwWTL9-VL-B7U
'Spring > JPA' 카테고리의 다른 글
JPA 필드와 칼럼 매핑 (0) | 2021.04.11 |
---|---|
JPA persistence.xml과 라이브러리 설정 (0) | 2021.04.11 |
JPA 기초와 매핑 : 실습 (0) | 2021.04.11 |
JPA 기초와 매핑 (0) | 2021.04.11 |
JPA에 대해서 (0) | 2021.04.11 |
Uploaded by Notion2Tistory v1.1.0