1. JPA 소개
1. JPA 소개
JPA(Java Persistence API)는 자바 진영의 ORM 기술 표준
ORM(Object Relational Mapping)은 객체와 데이터베이스의 관계를 매핑해주는 도구
JPA는 실행 시점에 자동으로 SQL을 만들어서 실행 -> 코드가 간결해지고 생산성과 유지보수 향상 + DB 변경 쉬움(예전 SQL 매퍼 사용 시 변경해야될 사항들이 많았다.)
1.1. SQL을 직접 다룰 떄 발생하는 문제점
관계형 데이터베이스는 가장 대중적이고 신뢰할 만한 안전한 데이터 저장소
DB를 관리하려면 SQL을 사용해야 함
자바로 작성한 애플리케이션은 JDBC API를 사용해서 SQL을 데이터베이스에 전달
1.1.1. 반복
SQL을 직접 다룰 때의 문제점을 살펴보자.
회원 객체
public class Member {
private String memberId;
private String name;
...
}
DAO(Data Access Object)
public class MemberDAO {
public Member find(String memberId){...}
...
}
이제 find 메소드를 완성해서 회원을 조회하는 기능을 개발해보자.
-- 1. 회원 조회용 SQL을 작성한다
SELECT MEMBER_ID, NAME FROM MEMBER M WHERE MEMBER_ID=?
// 2. JDBC API를 사용해서 SQL을 실행한다
ResultSet rs = stmt.executeQuery(sql);
// 3. 조회 결과를 Member 객체로 매핑한다
String memberId=rs.getString("MEMBER_ID");
String name=rs.getString("NAME");
Member member=new Member();
member.setMemberId(memberId);
member.setName(name);
갑자기 회원 등록 기능을 추가해야되는 상황이다.
회원 등록 기능 추가 DAO(Data Access Object)
public class MemberDAO {
public Member find(String memberId){...}
public void save(Member member){...}
}
// 1. 회원 등록용 SQL을 작성한다.
String sql="INSERT INTO MEMBER (MEMBER_ID, NAME) VALUES (?,?)";
// 2. 회원 객체의 값을 꺼내서 등록 SQL에 전달한다.
pstmt.setString(1, member.getMemberId());
pstmt.setString(2, member.getName());
// 3. JDBC API를 사용해서 SQL을 실행한다.
pstmt.executeUpdate(sql);
여기서 또 회원을 수정하고 삭제하는 기능이 추가됐다. 앞서 했던 작업을 반복해야 된다.
만약 회원 객체를 자바 컬렉션에 보관한다면 단 한 줄로 객체를 저장할 수 있다.
list.add(member);
하지만 데이터베이스는 객체 구조와 다른 데이터 중심의 구조를 가지므로 객체를 데이터베이스에 직접 저장하거나 조회할 수는 없다.
따라서 개발자가 객체 지행 애플리케이션과 데이터베이스 중간에서 SQL과 JDBC API를 사용해서 변환 작업을 해줘야 한다.
이러면 너무 많은 SQL과 JDBC API를 코드로 작성을 해야 한다.
1.1.2. SQL에 의존적인 개발
앞선 상황에서 고객이 회원 연락처도 함께 저장해달라는 요구사항이 추가되었다.
회원 클래스에 연락처 필드 추가
public class Member {
private String memberId;
private String name;
private String tel; // 추가된 필드
...
}
연락처를 저장할 수 있도록 ISERT SQL 수정
String sql="INSERT INTO MEMBER (MEMBER_ID, NAME, TEL) VALUES (?,?,?)";
// 그 다음 회원 객체의 연락처 값을 꺼내서 등록 SQL에 전달
pstmt.setString(3, member.getTel());
등록말고 조회 코드도 변경해야 한다
SELECT MEMBER_ID, NAME, TEL FROM MEMBER WHERE MEMBER_ID=?
또한 연락처의 조회 결과를 Member객체에 추가로 매핑해야 한다.
String tel=rs.getString("TEL");
member.setTel(tel);
그리고 수정 코드도 변경해야 한다.
만약, 회원 객체를 데이터베이슥다 아닌 자바 컬렉션에 보관했다면 필드를 추가한다고 해서 이렇게 많은 코드를 수정할 필요는 없었을 것이다.
list.add(member); // 등록
Member member=list.get(xxx); // 조회
member.setTel("xxx") // 수정
고객이 회원은 어떤 한 팀에 필수로 소속되어야 한다는 요구사항을 추가했다.
회원 클래스에 연관된 팀 추가
public class Member {
private String memberId;
private String name;
private String tel;
private Team team; // 추가
...
}
// 추가된 팀
class Team{
...
private String teamName;
...
}
다음 코드를 추가해서 화면에 팀 이름을 출력했다.
//이름
member.getNaem();
//소속팀
member.getTeam().getTeamName();
MemberDAO에 findWithTeam()추가
public class MemberDAO{
public Member find(String memberId){...}
public Member findWithTeam(String memberId){...}
}
findWithTeam()메소드는 다음 SQL로 회원과 연관된 팀을 함께 조회
SELECT M.MEMBER_ID, M.NAME, M.TEL, T.TEAM_ID, T.TEAM_NAME
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID=T.TEAM_ID
SQL과의 문제점은 Member 객체가 연관된 Team 객체를 사용할 수 있을지 없을지는 전적으로 사용하는 SQL에 달려 있다.
이런 방식의 가장 큰 문제점은 DAO를 사용해서 어떤 SQL이 실행되는지를 확인해야 한다는 점이다.
Member나 Team 처럼 비즈니스 요구사항을 모델링한 객체를 엔티티라고 하는데 SQL과 엔티티가 강한 의존 관계를 맺고 있다.
또한 회원 객체에 필드 하나를 추가할 때도 DAO의 CRUD 코드와 SQL 대부분을 변경해야 한다.
문제점 요약
- 진정한 의미의 계층 분할이 어렵다.
- 엔티티를 신뢰할 수 없다.
- SQL에 의존적인 개발을 피하기 어렵다.
1.1.3. JPA와 문제 해결
JPA를 사용하면 객체를 데이터베이스에 저장하고 관리할 때, 개발자가 직접 SQL을 작성하는 것이 아니라 JPA가 제공하는 API를 사용하면 된다.
JPA가 개발자 대신에 적절한 SQL을 생성해서 데이터베이스에 전달한다.
JPA가 제공하는 CRUD API를 알아보자.
// 저장 기능
jpa.persist(member);
// 조회 기능
String memberId="helloId";
Member member=jpa.find(Member.class, memberId);
// 수정 기능
Member member=jpa.find(Member.class, memberId);
member.setName("이름변경")
// 연관된 객체 조회
Member member=jpa.find(Member.class, memberId);
Team team=member.getTeam();
1.2. 패러다임의 불일치
객체지향 프로그래밍 -> 추상화, 캡술화, 정보은닉, 상속, 다형성의 장점 -> 현대의 복잡한 애플리케이션은 대부분 객체지향 언어로 개발
비즈니스 요구사항을 정의한 도메인 모델도 객체로 모델링하면 객체지향 언어가 자진 장점을 활용 가능
문제는 정의한 도메인 모델을 저장할 때 객체 인스턴스를 생성한 후에 이객체를 메모리가 아닌 어딘가에 영구 보관해야 함
객체는 속성(필드)과 메소드(기능)를 가짐
부모 객체를 상속받았거나, 다른 객체를 참조하고 있다면 객체의 상태를 저장하기 어려움
예시로, 회원 객체가 팀 객체를 참조하고 있다면, 회원 객체를 저장할 때 팀 객체도 함께 저장해야 한다. 단순히 회원 객체만 저장하면 참조하는 팀 객체를 잃어버린다.
이러한 문제점에 대한 현실적인 대안은 관계형 데이터베이스에 객체를 저장하는 것인데, 관계형 데이터베이스는 데이터 중심으로 구조화가 되어 있고 객체 지향의 추상화, 상속, 다형성 같은 개념이 없다.
따라서 객체 구조를 테이블 구조에 저장하는 데는 한계가 있다.
애플리케이션은 자바라는 객체지향 언어로 개발하고 데이터는 관계형 데이터베이스에 저장해야 한다면, 패러다임의 불일치 문제를 개발자가 중간에서 해결해야 한다.
1.2.1. 상속
객체는 상속 기능이 있지만 테이블은 상속 기능이 없다. 그나마 슈퍼타입과 서브타입의 관계를 사용해서 객체 상속과 가장 유사한 형태로 테이블 설계가 가능하다.
1.2.2. 연관관계
객체는 참조를 사용해서 다른 객체와 연관 관계를 가지고 참조에 접근해서 연관된 객체를 조회한다.
반면에, 테이블은 외래 키를 사용해서 다른 테이블과 연관관계를 가지고 조인을 사용해서 연관된 테이블을 조회한다.
객체는 참조가 있는 방향으로만 조회할 수 있다.