본문 바로가기

Database/JPA

[JPA] 객체와 테이블의 전격 비교 분석

반응형

 

오늘은 JAVA의 객체와 DB의 테이블의 구조가 어떻게 다른지 살펴보도록 한다 !!

 

객체는 속성(필드)과 기능(메소드)을 가진다. 

객체의 기능은 클래스에 정의되어 있으므로 객체 인스턴스의 상태인 속성만 저장했다가 필요할 때 불러와서 복구하면 된다.

 

그러나 객체가 단순하면 객체의 모은 속성 값을 꺼내서 파일이나 데이터어베이스에 저장하면 되지만, 

부모 객체를 상속받았거나 다른 객체를 참조하고 있다면 객체의 상태를 저장하기는 쉽지 않다.

 

그래서 자바는 이런 문제를 고려해 객체를 파일로 저장하는 직렬화 기능과 저장된 파일을 객체로 복구하는 역 직렬화 기능을 지원한다.

하지만 이 방법은 직렬화된 객체를 검색하기 어렵다는 문제가 있어 현실성이 어렵다.

 

현실적인 대안은 관계형 데이터베이스에 객체를 저장하는 것인데, 

관계형 데이터 베이스는 데이터 중심으로 구조화되어있고,집합적인 사고를 요구한다. 그리고 추상화, 상속, 다형성 같은 개념이 없다.

 

이는 객체와 관계형 데이터베이스는 지향하는 목적이 서로 다르므로 둘의 기능과 표현방법이 다른것이고,

이를 패러다임 불일치 문제라 한다.

따라서 객체 구조를 테이블 구조에 저장하는 데는 한계가 있다.

⌗ 상속 


JPA는 상속과 관련된 패러다임의 불일치 문제를 대신 해결해준다.

JPA에게 객체를 저장하면 상속된 객체에도 저장하게 된다.

 

예를 들어, Team에 상속한 member 객체를 저장해보자.

 

1 )

 jpa.persist(member);

 

2) 그러면 jpa는 TEAM, MEMBER 두 테이블에 나누어 저장한다.

INSERT INTO TEAM …
INSERT INTO MEMBER …

 

3) member 객체를 조회하면 

Member member = jpa.find(Member.class, “member_id”);

 

4) jpa는 TEAM과 MEMBER 두 테이블을 ㅈ인해서 필요한 데이터를 조회하고 그 결과를 반환한다.

SELECT team.*, member.*
FROM TEAM team
WHERE MEMEBER member ON team.id = member.id

⌗ 연관관계


객체는 참조를 사용해서 다른 객체와 연관관계를 가지고 참조에 접근해서 연관된 객체를 조회한다.

반면에 테이블은 외래 키를 사용해서 다른 테이블과 연관관계를 가지고 조인을 사용해서 연관된 테이블을 조회한다.

 

객체는 참조가 있는 방향으로만 조회할 수 있다.

예를 들어, member.getTeam()은 가능하지만, team.getMember()는 참조가 없으므로 불가능하다.

반면에 테이블은 외래키 하나로 MEMBER JOIN TEAM도 가능하지만, TEAM JOIN MEMBER도 가능하다.

⌗ 객체를 테이블에 맞추어 모델링


객체 모델은 외래키가 필요 없고 단지 참조만 있ㅇ면 된다.

반면에 테이블은 참조가 필요 없고, 외래키만 있으면 된다.

결국, 개발자가 중간에서 변환 역할을 해야한다.

 

예시 ) 개발자가 직접 연관관계 설정

Public Member find(String memberId) {
// SQL 조회
…
// 데이터베이스에 조회한 회원 관련 정보 모두 입력
Member member = new Member ();
…
// 데이터베이스에 조회한 팀 관련 정보를 모두 입력
Team team = new Team();
…
// 회원과 팀 관계 설정
member.setTeam(team);
return member
}

 

이러한 일련의 과정을 JPA로는 이런 비용이 들지 않는다.

개발자는 연관 관계를 설정하고, 저장할 객체를 저장하면 된다.

 

예시 ) JPA 사용

// 회원과 팀 연관 관계 설정
member.setTeam(team);
// 회원과 연관관계 함께 저장
jpa.persist(member);

 

객체를 조회할때 외래 키를 참조로 변환하는 일도 JPA가 처리해준다.

Member member = jpa.find(Member.class, “member_id”);
Team team = member.getTeam();

⌗ JPA와 객체 그래프 탐색


JPA를 사용하면 객체 그래프를 마음껏 탐색할 수 있다.

 

예시 )

member.getOrder().getOrderItem() …

 

이 기능은 실제 객체를 사용하는 시점까지 데이터베이스를 조회를 미룬다고 해서 “지연 로딩” 이라 한다.

JPA는 연관된 객체를 즉시 함께 조회할지 아니면 실제 사용되는 시점에 지연해서 조회할지를 간단한 설정으로 정의할 수 있다.

⌗ 비교


데이터베이스는 기본 키의 값으로 각 로우(row)를 구분한다.

반면에 객체는 동일성 비교와 동등성 비교라는 두 가지 비교 방법이 있다

 

  • 동일성 비교는 == 비교다. 객체 인스턴스의 주소 값을 비교한다.
  • 동등성 비교는 equals() 메소드를 사용해서 객체 내부의 값을 비교한다.

예를 들어, 기본 키 값이 같은 회원 객체를 두번 조회했다.

 

예시 ) 

Public Member getMember(String memberId) {
  // SQL 조회
  String sql = “SELECT * FROM MEMBER WHERE MEMBER_ID=?”;
  …
  return new Member (…);
}

String memberId = “1”;
Member m1 = sql.getMember (memberId); 
Member m2 = sql.getMember (memberId); 
m1 == m2; //false

 

데이터베이스의 같은 로우를 조회했지만, 객체의 동일성 비교에는 실패한다.

객체 측면에서 볼 때 둘은 다른 인스턴스기 때문이다.

 

만약 객체를 컬렉션에 보관했다면, 다음과 같이 동일성 비교에 성공했을 것이다.

 

예시 )

Member m1 = list.get(0);
Member m2 = list.get(0);
m1 == m2; //true

 

이러한 패러다임의 불일치 문제를 해결하기 위해

데이터베이스의 같은 로우를 조회할 때마다 같은 인스턴스를 반환하도록 구현하는 것은 쉽지 않다.

여기에 여러 트랜잭션이 동시에 실행되는 상황까지 고려하면 문제는 더 어려워진다.

 

그러나, JPA는 같은 트랜잭션일 때 같은 객체가 조회되는 것을 보장한다.

단, 객체 비교하기는 분산 환경이나 트랜잭션이 다른 상황까지 고려하면 더 복잡해지니 주의가 필요하다..

 

예시 )

String memberId = “1”;
Member m1 = jpa.find (Member.class, memberId); 
Member m2 =jpa.find (Member.class, memberId); 
m1 == m2; //true

 

 

반응형

❥ CHATI Github