💡 ORM? (Object-relational mapping, 객체 관계 매핑 )
객체는 객체대로 설계, 관계형 데이터베이스는 관계형 데이터베이스대로 설계한것을 중간에서 매핑해주는 역할을 한다.
스프링을 이용하여 서버를 개발시 언어는 객체 지향언어 ex) java, koltin
데이터베이스는 관계형 DB를 사용한다. ex) Oracle, MySQL
따라서 우리는 객체를 관계형 DB에 관리한다. → SQL문을 작성
→ 무한 반복, 지루한 코드 🥲
ex) 만약 다음과 같은 Member 객체가 있다고 가정하자.
public class Member {
private String memberId;
private String name;
...
}
간단한 CRUD SQL문을 작성해보면 다음과 같다.
**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; //필드 추가
...
}
그리고 SQL문에 하나하나 그 필드를 추가해줘야한다.
INSERT INTO MEMBER( MEMBER_ID, NAME, TEL) VALUES
SELECT MEMBER_ID, NAME, TEL FROM MEMBER M
UPDATE MEMBER SET … TEL = ?
일일이 모두 추가하다보니 효율성도 떨어지고 실수가 발생할 가능성이 높아진다.
객체와 관계형 데이터베이스의 차이
객체와 관계형 데이터베이스에서 상속관계를 표현하면 다음과 같다.
객체 상속 관계
Table 슈퍼타입 서브타입 관계
💡 이때 Album을 저장하려고 한다면?
자바 컬렉션에 저장
list.add(album);
DB에 저장
객체 분해 → INSERT INTO ITEM …, → INSERT INTO ALBUM …
(슈퍼타입과 서브타입에 모두 저장해주어야함)
자바 컬렉션에서 조회
DB에서 조회
각각의 테이블에 따른 조인 SQL 작성 → 각각의 객체 생성 → … 굉장히 복잡함
객체는 참조를 사용
member.getTeam()
객체다운 모델링
<class Member {
String id;
Team team; //참조로 연관관계를 맺음
String username;
Team getTeam() {
return team;
}
}
class Team {
Long id;
String name;
}
객체는 연관관계를 참조로 맺어 타입이 클래스인 필드로 저장한다.
테이블은 외래 키를 사용
JOIN ON M.TEAM_ID = T.TEAM_ID
객체를 테이블에 맞추어 모델링
class Member {
String id;
Long teamId; //TEAM_ID FK 칼럼 사용
String username;
}
class Team {
Long id;
String name;
}
테이블은 연관관계를 외래키로 맺기때문에 타입이 Long인 키값이 필드에 저장된다.
자바 컬렉션에서 조회
String memberId = "100";
Member member1 = list.get(memberId);
Member member2 = list.get(memberId);
member1 == member2; //같다
자바 컬렉션에서 조회는 같은 참조, 인스턴스가 같다
DB에서 조회
String memberId = "100";
Member member1 = memberDAO.getMember(memberId);
Member member2 = memberDAO.getMember(memberId);
member1 == member2; //오류
객체는 자유롭게 객체 그래프를 탐색할 수 있어야한다.
하지만 RDB에서는 처음 실행하는 SQL에 따라 탐색 범위가 결정된다.
ex) 다음과 SQL문으로 조회했을 경우
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
이렇게 처음 실행하는 SQL에 따라 조회 결과가 달라지면 엔티티 신뢰 문제가 발생한다.
그렇다고 모든 객체를 조인하여 미리 로딩할 수는 없다. → Query가 너무 많아지고 성능이 떨어진다.
따라서 상황에 따라 동일한 조회 메서드를 여러번 생성 해야한다.
객체 답게 모델링 할수록 매핑 작업만 늘어나게된다.
→ JPA를 사용한다면 객체를 자바 컬렉션에 저장 하듯이 DB에 저장할 수 있다 ‼️
JPA는 애플리케이션과 JDBC 사이에서 동작한다.
💡 JDBC(Java Database Connectivity)는 Java 기반 애플리케이션의 데이터를 데이터베이스에 저장 및 업데이트하거나, 데이터베이스에 저장된 데이터를 Java에서 사용할 수 있도록 하는 자바 API이다.
SQL 중심적인 개발에서 객체 중심으로 개발할 수 있다.
생산성과 성능이 향상되고 유지보수가 용이하다.
-> 기존에는 필드 변경시 모든 SQL 수정해야했지만 JPA를 사용한다면 필드만 추가하면 된다.
패러다임의 불일치를 해결할 수 있다.
데이터 접근 추상화와 벤더 독립성 → DB를 교체해도 문제가 되지않는다.
표준이다.
String memberId = "100";
Member m1 = jpa.find(Member.class, memberId); //SQL
Member m2 = jpa.find(Member.class, memberId); //캐시
트랜잭션을 커밋할 때까지 INSERT SQL을 모은다.
JDBC BATCH SQL 기능을 사용하여 트랜잭션을 커밋하는 순간 한번에 SQL을 전송한다.
→ 여러번 통신을 하지않으므로 네트워크 통신비용이 낮아진다.
transaction.begin()/ //트랜잭션 시작
em.persits(memberA);
em.persits(memberB);
em.persits(memberC);
// 여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
transaction.commit(); // 트랜잭션 커밋
//커밋하는 순간 데이터베이스에 INSERT SQL을 모아서 보낸다.
본 포스팅은 김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본편 강의를 참고하여 작성되었습니다.