[자바 표준 ORM JPA]1장 JPA 소개
시작하기에 앞서…
스프링부트를 공부한지 대략 3개월 정도 지난거 같다. 이제서야 대략적인 구조를 짤 줄 알게 되었다. 그러면서 간단한 CRUD는 구사 할 줄 알게 되었지만 그게 전부다. 책에도 나와있듯이 인터넷에 나와있는 예제들만 복사를 해서 사용해 한계를 느낀거 같다. 스프링부트를 공부하면 할수록 JPA 공부의 필요가 느껴져 이번 기회에 책을 공부해려고 한다.
1장은 김영한님의 JPA 필요성 경험을 소개하다보니 블로그 내용이 조금 길어졌다. 자 가보자~
1장. JPA 소개
객체를 관계형 데이터베이스에 저장하려면 많은 시간과 코드를 소비해야 한다. 이런 문제를 해결하기 위해 JPA라는 자바 ORM을 사용한다. SQL없이 데이터베이스에 직접 저장할 수 있도록 객체와 데이터베이스의 차이도 해결해준다.
1.1 SQL을 직접 다룰 때 발생하는 문제점
1.1.1 반복, 반복 그리고 반복
public class Member{ // 회원객체
private String memberId;
private String name;
}
public class MemberDAO{ // 데이터 접근객체
public Member find(String MemberId){...} // 회원 조회
public void save(Member member){...} // 회원 등록
}
위와 같이 회원 객체와 DAO가 존재한다고 하자.
회원을 조회하는 기능을 개발하면
- 회원조회용 SQL을 작성한다.
SELECT MEMBER_ID, NAME MEMBER M WHERE MEMBER_ID = ?
- JDBC API를 사용해서 SQL을 실행한다.
ResultSet rs = stmt.excuteQuery(sql);
- 조회 결과를 Member 객체로 매핑한다.
String memberId = re.get String("MEMBER_ID"); String name = re.getString("NAME"); Member member = new Member(); membet.setMembetId(memberId); membet.setName(name);
회원 등록 기능을 개발하면 - 회원 등록용 SQL을 작성한다.
String sql = "INSERT INTO MEMBET(MEMBET_ID, NAME) VALUES(?,?)";
- 회원 객체의 값을 꺼내서 등록 SQL에 전달한다.
patmt.setString(1, membet.getMembetId()); patmt.setString(2, membet.getName());
- JDBC API를 사용해서 SQL을 실행한다.
```patmt.executeUpdate(sql);
이와 같이 SQL을 작성하고 JDBC API를 사용하는 반복을 해야한다. 회원조회와 수정도 비슷할 것이다.
1.1.2 SQL에 의존적인 개발
만약 위의 회원객체에 회원의 전화번호를 추가해보자
public class Member{ // 회원객체
private String memberId;
private String name;
private String tel; //추가
}
벌써부터 쉽지 않다. 객체에 맴버 변수 하나만 추가되었을 뿐인데 SQL부터 SQL의 수정에 따라 JDBC 코드도 전면 수정해야한다.
이와 같이 애플리케이션에서 SQL을 직접 다룰 때 발생하는 문제점을 요약하면
- 진정한 의이의 계층 분할이 어렵다.
- 엔티티를 신뢰할 수 없다.
- SQL에 의존적인 개발을 피하기 어렵다.
1.1.3 JPA와 문제 해결
JPA가 제공하는 CRUD API를 간단히 알아보자.
- 저장 기능
JPA.persist(member); // 저장
- persist() 메소드는 객체를 데이터베이스에 저장한다.
- 조회 기능
-
String memberId = "helloId"; Member member = jpa.find(member.class, memberId); //조회
- find() 메소드는 객체 하나를 데이터베이스에서 조회한다.
-
- 수정 기능
*
Member member = jpa.find(Member.class, memberId); member.setName("이름변경") // 수정
- JPA는 수정 메소드를 제공하지 않는다 그대신 객체를 조회해서 값을 변경하면 트랜젝션을 커밋할 때 데이터 베이스에 UPDATE SQL이 전달된다.
- 연관된 객체 조회
-
Member member = jpa.find(Member.class, MemberId); Team team = member.getTeam(); // 연관된 객체 조회
- JPA는 연관된 객체를 사용하느 시점에 적절한 SELECT SQL을 실행한다.
1.2 페러다임의 불일치
객체와 관계형 데이터베이스는 지향하는 목적이 서로 다르므로 둘의 기능과 표현 방법도 다르다. 이것을 객체와 관계형 데이터베이스의 패러다임 불일치 문제라고 한다.
지금부터 패러다임 불일 칠로 인해 발생하는 문제를 구체적으로 살펴보자.1.2.1 상속
객체는 상속이라는 개념이 있지만 테이블을 상속이라는 기능이 없다. 그나마 슈퍼타입 서브관계를 사용하면 객체 상속과 비슷하게 설계를 할 수 있다. Album 객체를 저장하려면 이객체를 분해해서 다음 두 SQL을 만들어야 한다.
INSERT INTO ITEM ...
INSERT INTO ALBUM ...
Movie 객체도 마찬가지다.
-
JDBC API를 사용해서 이 코드를 완성하려면 부모 객체에서 부모 데이터만 꺼내서 ITEM용 INSERT SQL을 작성하고 자식 객체에서 자식 데이터만 꺼내서 ALBUM용 INSERT SQL을 작성해야 하는데, 작성해야 할 코드량이 만만치 않다. 그리고 자식 타입에 따라서 DTYPE도 저장해야한다.
조죄하는 것도 쉬운일은 아니다. 예를 들어 Album을 조회한다면 ITEM과 ALBUM테이블을 조인해서 조회한 다음 그 결과로 Album객체를 생성해야한다.
이러한 과정이 모두 패러다임의 불일치를 해결하려고 소모하는 비용이다.
1.2.2 연관관계
객체는 참조를 사용해서 다른 객체와 연관관계를 가지고 테이블은 외래키를 사용해서 다른 테이블과 연관관계를 가진다.
객체지향 모델링
객체는 참조를 통해서 관게를 맺는다.
class Member{
String id; // MEMBER_ID 컬럼 ㅏ용
TEAM team; // 차모졸 연관관계를 맺는다.
String username; // USERNAME 컬럼 사용
Team getTema(){
return team;
}
}
class Team {
Long id; //TEAM_ID PK 사용
String name; // NAME 컬럼 사용
}
Member.team 필드를 보면 외래 키의 값을 그래돌 보관하는 것이 아니라 연관된 Team의 참조를 보관한다. 이제 연관된 팀을 조회할 수 있다.
Team team = member.getTeam();
객체 모델은 외래키가 필요 없고 단지 참조만 있으면 된다. 반면에 테이블은 참조가 필요 없고 외래키만 있으면 된다. 결국, 개발자가 중간에서 변환 역할을 해야한다.
1.2.3 객체 그래프 탐색
다음과 같은 객체 탐색 그래프가 있다고 하자
객체는 마음껏 객체 그래프를 탐색할 수 있어야 한다. 그런데 마음껏 객체 그래프를 탐색할 수 있을까?
예를 들어 MemberDAO에서 member 객체 조회할 때 이런 SQL을 실행해서 회원과 데이터가 member.getTeam()
은 성공하지만 다음처럼 다른 객체 그래프는 데이터가 없으으로 탐색 할 수 없다.
membet.getOrder(); //null
SQL을 직접 다루면 처음 실행하는 SQL에 따라 객체 그래프를 어디까지 탐색할 수있는지 정해진다.
** JPA를 사용하면 객체 그래프를 마음껏 탐색할 수 있다. **
1.2.4 비교
데이터베이스는 기본 키의 값으로 각 로우~row~를 구분한다. 반면에 객체는 동일성~identity~ 비교와 동등성~equality~ 비교라는 두가지 비교 방법이 있다.
- 동일성 비교는 == 비교다. 객체 인스터스의 주소값을 비교한다.
- 동등성 비교는 equals() 메소트를 사용해서 객체 내부의 값을 비교한다.
만약getMember()
라는 함수를 만들어서 두번 조회를 해 비교하면 인스턴스를 비교하기 떄문에 불일치한다는 결과가 나온다.
이런 패러다임의 불일치 문제를 해결하기 위해 데이터베이스의 같은 로우를 조회할 떄마다 같은 인스턴스를 반환하도록 구현하는 것은 쉽지 않다. 여기에 여러 트랜잭션이 동시에 실행되는 상황까지 고려하면 더 어려워진다.
JPA는 같은 트랜잭션일 떄 같은 객체가 조회되는 것을 보장한다.
1.2.5 정리
객체지향의 애플리케이션답게 정교한 객체 모델링을 할수록 패러다임의 불일치 문제가 더 커진다. 이 틈을 메우기 위해 개바자가 소모해야하는 비용도 저점 더 많아진다. 결국 객체 모델링은 힘을 잃고 점점 데이터 중심의 모델로 변해간다. JPA는 패러다임의 불일치 문제를 해결해주고 정교한 모델링을 유지하게 도와준다.
1.3 JPA란 무엇인가?
JPA는 자바 진영의 ORM 표준이다.
ORM~Object-Realtional Mapping~은 이름 그대로 객체와 관계형 데이터베이스를 매핑한다는 뜻이다.
INSERT SQL을 직접 작성하는 것이 아니라 객체를 마치 자바 컬랙션 저장하듯이 ORM 프레임워크에 저장하면된다. 그러면 ORM 프레임 워크가 적절한 INSERT SQL을 생성해서 데이터베이스에객체를 저장해준다.
조회도 JPA를 통해 직접 조회하면 된다.
1.3.1 JPA 소개
하이버네이트^hibernate.orrg^라는 오픈소스 프레임워크가 등장했는데 가볍고 기술 성숭도도 높았다. 또한 자바 앤터프라이즈 애플리케이션 서버 없이도 동작했기 때문에 많은 개발자들이 사용하기 시작했다. 하이버네이트를 기반으로 새로운 자바 ORM 기술 표준이 만들어졌는데 이것이 바로 JPA다.
1.3.2 왜 JPA를 사용해야 하는가?
- 생상성
- JPA를 사용하면 자바 컬렉션에 객체를 저장하듯이 JPA에게 저장할 객체를 전달하면 된다.
- 유지보수
- SLQ과 JDBC API 코드를 JPA가 대신 처리해주므로 유지보수해야 하는 코드 수가 줄어든다.
- 패러다임의 불일치 해결
- JPA는 상속, 연관관계, 객체 그래프 탐색, 비교하기와 같은 패러다임의 불일치 문데를 해결해준다.
- 성능
- JDBC API를 사용해서 해당 코드를 직접 작성했다면 회원을 조회할 떄마다 SELECT SQL을 사용해서 데이터 베이스와 두 번 통신 했을 것이다.
- JPA를 사용하면 회원 조회하는 SELECT SQL을 한 번만 데이터베이스에 전달하고 두 번쨰는 조회한 회원 객체를 재사용한다.
- 데이터 접근 추상화와 벤더 독립성
- 어플리케이션이 특정 데이터베이스 기술에 종속되지 않도록 한다. 만약 데이터베이스를 변경하면 JPA에게 다른 데이터베이스를 사용한다고 알려주기만 하면된다.
- 표준
- JPA는 자바 진영의 ORM 기술의 표준이다. 앞서 이야기 했듯이 표준을 사용하면 다른 구현 기술로 손쉽게 변경할 수 있다.
1.4 정리
지금까지 SQL을 직접 다룰 떄 발생하는 다양한 문제와 객체지샹 언어와 관계형 데이터베이스 사이의 패러다임 불일치 문제를 설명했다. 그리고 각문제를 JPA로 어떻게 해결하는지 알아보았다.
Leave a comment