본문 바로가기

Web Sever 개발과 CS 기초/스프링

ORM과 JPA, 영속성 쉽게 이해하기

ORM(Object Relational Mapping)이란

ORM은 객체와 관계형 데이터 베이스를 연결한다. 객체 지향 프로그램은 클래스를 사용하지만, 데이터 베이스는 테이블을 사용한다. 이러한 차이 때문에, 어플리케이션에서 DB에 접근할 때는 객체 지향 언어를 포기하고 SQL을 사용해야 했다. 하지만, ORM은 어플리케이션의 객체 지향스러운 언어를 자동을 SQL로 변환하여 둘 사이를 연결한다.

자바 진영의 표준 ORM 기술인 JPA를 통해서 ORM의 역할을 구체적으로 알아보자.(JPA예시를 통해서 본 ORM의 역할)

JPA(Java Perisistence API)이란

JPA는 자바 진영의 표준 ORM 기술 인터페이스이다. 어플리케이션과 JDBC사이에 위치하여, 객체 정보와 객체 언어를 SQL로 변환하여 DB에 적용한다.

JPA예시를 통해서 본 ORM의 역할

자바 객체와 테이블 연관 관계 차이를 알아보고, 그 차이를 ORM이 어떻게 해결하는 지 알아보자.

 

객체는 참조를 통해서 연관 관계를 설정하지만, 테이블은 외래키(FK)를 사용한다.

그래서 테이블에 맞춰 객체를 저장하려면, 객체 참조를 포기하고, TeamId를 사용해야 한다.

class Member {
 String id; 
 Team team; 
 String username;
}

class Team {
 Long id; 
 String name;
}
=>

class Member {
 String id; 
 Long teamId; //TEAM_ID FK 
 String username;
}

class Team {
 Long id; 
 String name; 
}

그에 따라서 데이터를 저장하는 방식도 변하게 된다. 그리고 값을 가져오는 방식도 달라진다.

//객체 언어는 아래와 같이 참조를 통해 데이터 설정하고, 그대로 저장하고 싶다
Team kiaTeam = new Team(1,"기아타이거즈);
Member member25 = new Member(25, kiaTeam, "김성범")
db.save(member25)
=>
//하지만, Table 방식과 SQL언어를 사용해야 한다.
INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME) VALUES (25, 1, "김성범")

이러한 차이에서 오는 불편함을 ORM을 통해서 자바 컬랙션 저장하듯이 DB에 저장할 수 있게 된다.

/객체 참조 방식 유지할 수 있다.
@Entity
class Member {
 @Id
 String id; 
 @ManyToOne
 @JoinColumn(name="Team_ID")
 Team team; 
 String username;
}
@Entity
class Team {
 @Id
 Long id; 
 String name;
}
//SQL언어 종속적이지 않게, 마치 콜렉션 리스트 사용하듯이 회원 정보를 DB에 저장할 수 있다.
Team kiaTeam = new Team(1,"기아타이거즈);
Member member25 = new Member(25, kiaTeam, "김성범")
em.persist(member25 );

//JPA가 SQL언어로 자동 번역해서 전달한다.
//insert into team (id, team_name) values(1, "기아타이거즈")
//insert into member (id, team_id, name) values(25, 1, "김성범")

JPA 종류

JPA는 ORM의 역할만 지정해 놓은 인터페이스고, 구현체는 따로 존재한다. JPA 구현체로는 가장 많이 쓰는 HIbernate가 있고, EclipseLink와 DataNucleus가 있다.

JPA 영속성 이해하기

JPA에서는 Entity Manager를 통해서, 객체 저장, 수정, 가져오기, 쿼리 실행을 한다. EntityManger는 DB와 바로 연결되는 것이 아니라, PersistentContext(영속성 콘테스트)를 통해 엔티티 객체를 중간에서 관리한다. EntityManager 관리 정도에 따라 엔티티 객체의 생명 주기가 정해진다.

<엔티티 생명 주기>

비영속 (new/transient) :영속성 컨텍스트와 전혀 관계가 없는 새로운 상태

영속 (managed): 영속성 컨텍스트에 관리되는 상태

준영속 (detached): 영속성 컨텍스트에 저장되었다가 분리된 상태

삭제 (removed): 삭제된 상

<영속성의 특징과 장점>

단순히 JPA를 쓰기 위해서 영속성을 아는 것이 아니라, 영속성을 사용함으로서 얻는 장점과 그에 따른 영속성의 특징에 대해서 알아보자.

  • 영속, 준영속, 삭제 방법
Member member = new Member(1, "회원이름1")
EntityManager em = emf.createEntityManager();
//영속성 관리를 transaction.begin() 시작한다.
em.getTransaction().begin();
//객체를 저장한 상태(영속)
em.persist(member);
//회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태 
em.detach(member);
//객체를 삭제한 상태(삭제) 
em.remove(member);
//또는
  • (장점) 1차 캐시, 엔티티 조회
Member member = new Member(1, "회원이름1")
EntityManager em = emf.createEntityManager();

em.getTransaction().begin();
//DB가 아닌 1차 캐시에 저장된다.
em.persist(member);
//DB가 아닌 1차 캐시에서 조회한다.
Member findMember = em.find(Member.class, "회원이름1");
//DB가 아닌, 어플리케이션에서 찾기 때문에 == 가 가능하다.
Member findMeber2 =em.find(Member.class, "회원이름1");
System.out.println(findMembe == findMeber2 ); //동일성 비교 true
  • 트랜잭션을 지원하는 쓰기지연
Member member = new Member(1, "회원이름1")
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//트렌잭션 시작
transaction.begin();

em.persist(new Member(1, "회원이름1"));
em.persist(new Member(2, "회원이름2"));
//이 상태에서 DB Insert문을 보내지 않고, 1차 캐시에만 보관한다
//쓰기 지연 SQL 저장소에 저장한다. 
transaction.commit()
//커밋한 이후에 모안둔 Insert문을 보낸다.
  • 엔티티수정(update) 영속성을 이용한 변경 감지
//JPA 변경 감지를 이용한 엔티티 Update
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, 1);
// 영속 엔티티 데이터 수정
// 기존 영속성 memberA와 변화된 memberA를 감지하여, transaction.commit()하면 자동으로
// update 쿼리 보낸다.
memberA.setUsername("hi");
memberA.setAge(10);
transaction.commit();
//em.update(member) 이런 코드없이 update완료
  • 엔티티 삭제 방법
transaction.begin()
Member memberA = em.find(Member.class, 1);
em.remove(memberA); //엔티티 삭제
//commit후에 delete쿼리가 보내진다.
transaction.commit()

reference

https://gmlwjd9405.github.io/2019/02/01/orm.html

김영한 jdbc-basic 영속성관리

https://dbjh.tistory.com/77