본문 바로가기

이유

JPA 기본 개념 - 우리가 JPA 를 사용하는 이유

JPA를 왜 사용할까?

 

 JPA 의 사용 목적을 알아보기 전에 자바에서 데이터 베이스를 어떻게 사용할까? 

 

보통 자바로 작성한 애플리케이션은 JDBC API 를 사용해서 SQL 을 데이터 베이스에 전달하고 받는다. 

이 API 를 사용하기 위해 우리는 JDBC 라이브러리를 항상 자바 애플리케이션에 추가한다. 

 

SQL 문을 코드에 작성하고 API로 데이터 베이스에 전달하는데 보통 이 경우에는 아래 그림처럼 애플리케이션과 데이터 베이스 사이에 JDBC API 를 두고 개발한다고 볼 수 있다. 

 

 

 

 

 

JDBC 사용 과정 예시) 

 

1. 회원 조회용 SQL 을 작성한다. 

 

 

String sql = SELECT MEMBER_ID, NAME FROM MEMBER M WHERE MEMBER_ID = ?

 

2. JDBC API 를 사용해서 SQL 을 실행하고 조회한다. 직접

 

 

ResultSet rs = stmt.executeQuery(sql); 

String memberId = rs.getString("Member_ID); 

String name = rs.getString("NAME");

Member member = new Member();

member.setMemberId(memberId); 

member.setName(name);

 

3. INSERT 가 필요하면 새로운 쿼리를 만든다

4. UPDATE 가 필요하면 새로운 쿼리를 만든다.

5. DELETE 가 필요하면 새로운 쿼리를 만든다.

6. 번거롭다..

 

 

  이 경우 직접 작성해서 코드에 추가하는 SQL 은 하드코딩이라고 할수도 있고 다른 SQL 문이 필요할때 항상 새로 만들어야 한다. 그렇다면 비슷한 구문도 많이 생겨 코드의 중복성 문제가 생긴다.

 

 우리가 자바를 통해서 데이터를 조회하거나 삭제할때는 어떻게 사용했을까? setter, getter 함수를 사용하거나 컬렉션에 데이터를 집어넣어서 필요할때마다  관리하고 사용할 수 있었다. 하지만 기존의 JDBC API 만을 사용할때는 쿼리를 그때마다 직접 생성하고 자바로 가져와 객체에 직접 매핑 하고. 쿼리가 조금만 수정되어도 새로 추가 해야 하는 등.. 굉장히 번거로운 일이 아닐 수 없다. 또한 SQL 문을 직접 작성 할때는 명시적인 제한 기능들이 없기 때문에 추후에 자칫 버그가 발견 되는 결과로 이어질 수도 있다.

 

 기본적으로 이러한 문제는 관계형 데이터 베이스의 테이블과 객체지향적인 자바 클래스의 구조적인 차이 때문에 문제가 발생하는데. 왜냐하면 위 문제들은 객체가 아닌것을 객체로 변환하고 객체에서 객체가 아닌것으로 변환해야 하는 과정에서 나타나기 때문이다. 이 과정들이 발생하는 원인을 패러다임의 불일치라고 한다.

 

 자바의 객체구조와 관계형 데이터 베이스의 패러다임 불일치는 어떤 것이 있는지 아래에서 살펴볼 수 있다.

 

1.2 패러다임의 불일치 (종류)

 

JDBC API 를 사용할때 우리는 SQL 을 직접 사용하고 데이터를 조회한다. 조회한 데이터를 자바의 객체로 가져와야하는데 이 지점에서 객체와 테이블의 패러다임의 불일치가 발생한다.

 

1.2.1 상속

 객체는 상속이라는 기능을 가지고 있지만 테이블은 상속이라는 기능이 없다. 

 

1.2.2 연관관계 

 객체는 참조를 사용해서 다른 객체와 연관관계를 가지고 참조에 접근해서 연관된 객체를 조회한다. 반면에 테이블은 외래 키를 사용해서 다른 테이블과 연관된 테이블을 조회한다.  참조를 사용해서 접근 한다는것은 아래의 코드처럼 객체의 주솟값을 지닌 참조 변수를 저장한다는 것이다.

 

class Member { 
	Team team;
    ...
    
    team setTeam(Team team) {
      this.team = team;
    }
    team getTeam() {
      return team;
    }
}

class Team {
	... 
}

member.getTeam();  // member -> team 접근

 

반면 테이블은 외래키 컬럼을 사용해서 TEAM 테이블과 관계를 맺고 데이터를 조회한다. 

 

 

SELECT M.*, T.*
	FROM MEMBER M 
    JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID

 

1.2.4 비교

 

 데이터베이스는 기본키의 값으로 각 로우row 를 구분한다. 반면에 객체는 동일성 identity 비교와 동등성 equality 비교 라는 두 가지 비교 방법이 있다. 

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

따라서 테이블의 로우를 구분하는 방법과 객체를 구분하는 방법에는 차이가 있다. 

 

1.2.5 정리하여. JPA 가 등장한다. 

 

 객체 모델과 관계형 데이터베이스 모들은 지향하는 패러다임이 서로 다르다. 문제는 이 패러다임의 차이를 극복하려고 개발자가 너무 많은 시간과 코드를 소개한다는 것이다. 자바 진영에서는 오랜기간 이 문제에 대한 숙제를 안고 있었고 패러다임의 불일치 문제를 해결하기 위해 많은 노력을 기울여 왔다. 그리고 바로 그 결과물이 JPA다. 

JPA 는 패러다임의 불일치 문제를 해결해 주고 정교한 객체 모델링을 유지하게 도와준다. 

 

1.3  그래서 JPA 란 무엇인가?

 JPA 를 알기전에 하이버 네이트(hibernate.org) 라는 오픈스스의 ORM 프레임워크를 알아야한다. 

과거 자바 진영은 엔터프라이즈 자바 진즈라는 기술 표준을 만들었는데. 그 안에는 엔티티 빈이라는 ORM 기술도 포한되어있었다. ORM 은 Object-Relational Mapping 이라는 뜻으로 위 패러다임을 해결하기 위해 객체와 관계형 데이터 베이스를 매핑하는 기술이다. 하지만 너무 복잡하고 자바 엔터프라이즈 (J2EE) 애플리케이션 서버에서만 동작했다. 

이때 하이버네이트라는 또다른 오픈소스 ORM 프레임 워크가 등장 했는데 EJB 의 ORJ 기술과 비교해서 가볍고 실용적인 데다 기술 성숙도도 매우 높았다. 또한 자바 엔터프라이즈 애플리케이션 서버 없이도 동작해서 많은 개발자가 사용하기 시작했다. 결국 EJB 3.0 에서 하이버 네이트를 기반으로 새로운 자바 ORM 기술 표준이 만들어 졌는데 이것이 바로 JPA 이다. 

 

 

 

 

 다시 말해 JPA Java Persistence API 는 자바 진영의 ORM 기술 표준이다. 쉽게 이야기 해서 ORM 기술을 사용할 수 있는 인터페이스를 모아둔 것이다. 현재 JPA 2.1을 구현한 ORM 프레임 워크는 하이버네이트, EclipseLink, DataNucleus 가 있는데. 이중 하이버네이트가 가장 대중적이다. 

 

JPA 는 아래의 그림처럼 애플리케이션의 JDBC 사이에서 동작한다. 

 

 

 

 

ORM 프레임워크는 객체와 테이블을 매핑해서 패러다임의 불일치 문제를 개발자 대신 해결해준다. 예를 들어 ORM프레임 워크를 사용하면 객체를 데이터베이스에 저장할때 INSERT SQL 을 직접 작성하는 것이 아니라 객체를 마치 자바 컬렉션에 저장하듯이 ORM 프레임 워크에 저장하면 된다.  그리고 이 ORM 의 매핑 기능을 사용하기 위해 JPA 인터페이스를 사용한다. 

 

 

 

JPA 구현체로 하이버네이트를 사용하기 위한 핵심 라이브러리는 다음과 같다.

 

  • hibernate-core : 하이버네이트 라이브러리
  • Hibernate-entitymanager : 하이버네이트가 JPA 구현체로 동작하도록 JPA 표준을 구현한 라이브러리
  • Hibernate-jpa-2.1-api : JPA 2.1 표준 API 를 모아둔 라이브러리

 

JPA  사용  예시) 

 

 

public static void logic (EntityManager em) {

	String id = "idl"; 
    Member member = new Member(); 
    member.setId(id); 
    member.setUsername("세진"); 
    member.setAge(2); 
    
    em.persiste(member);  // INSERT
    
    member.setAge(20);  // UPDATE
    
   Member findMember = em.find(Member.class, id); // SELECT ROW 1 
   
   em.remove // DELETE 
   }
}

 

1. DELETE 문을 사용하기 위해 DELETE 문을 추가하지 않아도 된다. 

2. UPDATE 문을 사용하기 위해 UPDATE 문을 추가하지 않아도 된다. 

3. INSERT 문을 사용하기 위해 INSERT 문을 추가하지 않아도 된다. 

 

 그저 우리가 클래스에 데이터를 추가하고 삭제하듯이 객체에 데이터를 추가하고 지워주면 JPA 가 알아서 인식하고 쿼리를 생성해서 JDBC API 를 사용해준다. 우리가 해야할 중요한 일은 객체를 데이터베이스에 있는 테이블과 매핑해 주는것이다. 이 매핑 규칙을 이용해서 JPA 가 올바른 쿼리를 생성해 주도록 도와주기 때문이다. 

 

예시) 매핑한 회원 엔티티

@Entity
public class Member {
	@Id
    @Column (name = "MEMBER_ID")
    private String id; 
    
    private String username; 
    
    //연관관계 매핑 
    @ManyToOne 
    @JoinColumn(name="TEAM_ID")
    private Team team; 
    
    // 연관관계 설정 
    public void setTeam(Team team) {
    	this.tesm = tesm; 
    }
    
    // Getter, Setter .. 
}

 

 

 매핑은 어노테이션을 통해서 진행 되는데 어노테이션의 종류는 @Entity,@Enumerated,@Id .. 등등 다양하며 

이 어노테이션을 붙임으로써 개발자는 객체와 테이블의 연관관계를 JPA 에게 알릴 수 있다. 그러면 JPA는 이 매핑 연관관계를 가지고 관계형 디비를 다루고 개발자는 그저 객체지향적인 데이터 처리를 하는것처럼 개발을 할 수 있는 것이다. 아래는 대표 어노테이션들이다. JPA 를 사용하는데 가장 중요한 일은 엔티티와 테이블을 정확히 매핑하는것이다. 

그래야만 내가 원하는 데이터를 얻고 DB 에서 발생할 수 있는 실수또한 줄일 수 있다.

그러니까 어노테이션 그냥 쓰지말고 잘 -  써야 겠다

 

  • 객체와 테이블 매핑 : @Entity, @Table 
  • 기본키 매핑 : @Id
  • 필드와 컬럼 매핑 : @Column 
  • 연관관계 매핑 : @ManyToOne, @JoinColumn

 

 

반응형