본문 바로가기
Back/JPA

[JPA] JPA 엔티티 매핑 기본 @Entity, @Id, @Table, @Column

by 은z 2021. 12. 14.

본 포스팅은 김영한 강사의 자바 ORM 표준 JPA 프로그래밍 '기본편' 강의를 수강하며 정리한 내용임을 밝힌다.

DB 테스트 환경은 H2 데이터베이스, IDE는 인텔리제이(community version)를 사용했다

 

 


 

1. 테이블 설계를 따른 엔티티 매핑

 

위 테이블을 생성하기 위해서 먼저 JAVA 클래스와 테이블을 매핑해야 한다.

 

테이블과 관련있는 4가지의 클래스를 만들고, 1개의 main 클래스를 만들어 실행해보려 한다.

 

 

 

 

1) Member.java

package hellojpa;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    private String name;
    private String city;
    private String street;
    private String zipcode;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getZipcode() {
        return zipcode;
    }

    public void setZipcode(String zipcode) {
        this.zipcode = zipcode;
    }


}

 

 

2) Order.java

package hellojpa;

import OrderStatus.OrderStatus;

import javax.persistence.*;
import java.time.LocalDateTime;

@Entity
@Table(name = "ORDERS")
public class Order {

    @Id @GeneratedValue
    @Column(name = "ORDER_ID")
    private Long id;

    @Column(name = "MEMBER_ID")
    private Long memberId;

    private LocalDateTime orderDate;

    @Enumerated(EnumType.STRING)
    private OrderStatus status;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getMemberId() {
        return memberId;
    }

    public void setMemberId(Long memberId) {
        this.memberId = memberId;
    }

    public LocalDateTime getOrderDate() {
        return orderDate;
    }

    public void setOrderDate(LocalDateTime orderDate) {
        this.orderDate = orderDate;
    }

    public OrderStatus getStatus() {
        return status;
    }

    public void setStatus(OrderStatus status) {
        this.status = status;
    }
}

 

2-1) OrderStatus.java    : 이 클래스는 Order.java에 선언된 Enum 타입의 클래스이다.

package OrderStatus;

public enum OrderStatus {
    ORDER, CANCEL
}

 

3) OrderItem.java

package hellojpa;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class OrderItem {

    @Id @GeneratedValue
    @Column(name = "ORDER_ITEM_ID")
    private Long id;

    @Column(name = "ORDER_ID")
    private Long orderId;

    @Column(name = "ITEM_ID")
    private Long itemId;

    private int orderPrice;

    private int count;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getOrderId() {
        return orderId;
    }

    public void setOrderId(Long orderId) {
        this.orderId = orderId;
    }

    public Long getItemId() {
        return itemId;
    }

    public void setItemId(Long itemId) {
        this.itemId = itemId;
    }

    public int getOrderPrice() {
        return orderPrice;
    }

    public void setOrderPrice(int orderPrice) {
        this.orderPrice = orderPrice;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}

 

 

4) Item.java

package hellojpa;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Item {

    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;

    private String name;
    private int price;
    private int stockQuantity;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public int getStockQuantity() {
        return stockQuantity;
    }

    public void setStockQuantity(int stockQuantity) {
        this.stockQuantity = stockQuantity;
    }
}

 

매핑을 하기 위한 어노테이션의 의미는 다음과 같다.

@Entity

이 클래스를 테이블과 매핑한다고 JPA에게 선언한다. 이 어노테이션을 붙여줘야 JPA가 관리할 수 있다. 붙이지 않는 클래스는 JPA와 상관없는 클래스이다. 엔티티 애노테이션이 사용된 클래스를 엔티티 클래스라고 한다.

@Table

엔티티 클래스에 매핑할 테이블 정보를 알려준다. 여기서는 name 속성을 이용하여 이 클래스를 MEMBER 테이블과 매핑했다. 위에서 Order 클래스에서 name="ORDERS"로 지정했기 때문에 Order테이블이 아니라 ORDERS 테이블이 생성된다. 이 어노테이션을 생략하면 디폴트가 클래스 이름을 테이블 이름으로 매핑한다.

@id

엔티티 클래스의 필드를 테이블의 Primary key로 매핑한다. 여기서는 엔티티 클래스의 id를 Primary key로 매핑했다.

@Column

필드를 컬럼에 매핑했다. 만약 매핑 정보를 주지 않는다면 필드명을 이용하여 컬럼과 매핑한다.

 

 

여기서 잠깐,

현재 예제는 스프링 부트를 이용하고 있지 않지만 스프링 부트를 이용하게 되면,

부트가 제공해주는 설정에 의해서 camel 표기법을 자동으로 snake 표기법으로 바꿔서 DB에 저장한다고 한다.

(예. memberId -> member_id)

 

 

5) main.class

package hellojpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class jpaMain {
    public static void main(String[] args) {
    
		//엔티티 매니저 팩토리 생성
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

		//엔티티 매니저 생성
        EntityManager em = emf.createEntityManager();

		//트랜잭션 기능 획득
        EntityTransaction tx = em.getTransaction();
        
        //트랜잭션 시작
        tx.begin();

        try {
        	//트랜잭션 커밋
            tx.commit();
        }catch (Exception e) {
        	//트랜잭션 롤백
            tx.rollback();
        }finally {
        
        	//엔티티 매니저 종료
            em.close();
        }
        
		//엔티티 매니저 팩토리 종료
        emf.close();


    }
}

 

로직 구현의 흐름을 살펴보자

엔티티 매니저 팩토리 생성 > 엔티티 매니저 생성 > 트렌잭션 획득 > 트렌잭션 시작 > 비즈니스 로직 시작 > 트렌잭션 커밋

1) 엔티티 매니저 팩토리 생성

JPA를 시작하려면 우선 persistence.xml의 설정 정보를 보고 엔티티 매니저 팩토리를 생성해야 한다.

 

2) 엔티티 매니저 생성

엔티티 매니저 펙토리에서 엔티티 매니저를 생성할 수 있다. JPA 기능의 거의 대부분을 엔티티 매니저가 제공한다. 엔티티를 데이터베이스에 CRUD 하는 것 또한 엔티티 매니저를 통해 할 수 있다.

엔티티 매니저는 데이터베이스 커넥션과 밀접한 관계가 있으므로 쓰레드끼리 공유, 재사용하면 안 된다.

 

3) 종료

사용이 끝난 엔티티 매니저는 반드시 종료해야 한다. -> em.close();

애플리케이션이 종료될 때 엔티티 매니저 팩토리도 종료해야 한다. -> emf.close();

 

4) 비즈니스 로직 CRUD

삭제는 엔티티 매니저의 remove(), 한 건 조회는 find() 사용하면 된다.

수정같은 경우는 영속객체의 값만 바꾸어주면 되는데, JPA는 어떤 엔티티가 변경되었는지 추적하는 기능이 있다.

그래서 값만 변경해주면 변한 것을 인식하고 업데이트 쿼리를 생성하여 DB로 쏴준다. (조심해야 한다)


 

 

2. persistence.xml 설정

JPA는 persistence.xml을 이용하여 필요한 설정 정보를 관리한다. 이 설정 파일이 META-INF/persistence.xml에 있으면 별도의 설정 없이 JPA가 인식할 수 있다. 꼭 META-INF 하위에 이 설정파일을 만들어야 한다.

 

 

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">

    <persistence-unit name="hello">
        <properties>
            <!-- 필수 속성 -->
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="javax.persistence.jdbc.user" value="sa"/>
            <property name="javax.persistence.jdbc.password" value=""/>
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>

            <!-- 옵션 -->
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.use_sql_comments" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="create" />
        </properties>
    </persistence-unit>
</persistence>

 위 코드를 하나씩 살펴보자면,

 

1) unit name

<persistence-unit name="">

위 mian클래스에서 createEntityManagerFactory("") 안에 넣어주는 것과 동일하게 맞춰주어야 한다.

즉 persistence-unit은 연결된 하나의 데이터베이스당 하나의 영속성 유닛을 등록한다.

엔티티 매니저 팩토리를 생성하면서 설정 정보를 읽어서 JPA를 동작시킬 기반 객체를 만들고, 데이터베이스 커넥션 풀도 생성하므로 생성 비용이 아주 크다. 따라서 엔티티 매니저 팩토리는 어플리케이션 전체에서 딱 한 번만 생성하고 공유하여 사용해야 한다.

 

2) DB 정보

  • property name=”javax.persistence.jdbc.driver” value=”org.h2.Driver”
  • property name=”javax.persistence.jdbc.user” value=”sa”
  • property name=”javax.persistence.jdbc.password” value=””
  • property name=”javax.persistence.jdbc.url” value=”jdbc:h2:tcp://localhost/~/test”

JPA 표준 속성은 javax로 시작한다. 위의 네 속성은 각각 드라이버, 유저명, 패스워드, db url이다. JPA 표준 속성은 구현체와 관계없이 어디서든 사용할 수 있다.

 

3) 하이버네이트 속성

 

  • property name=”hibernate.dialect” value=”org.hibernate.dialect.H2Dialect”

hibernate로 시작하는 속성은 하이버네이트 전용 속성이다.

dialect는 방언이라는 의미를 가지고 있는데,  JPA가 많은 데이터베이스를 지원해야 하는 문제 때문에 생긴 속성이다.

JPA는 DB 플렛폼에 종속되지 않는 기술로, 예를 들어 MySQL을 사용하든 오라클을 사용하든 알아서 커버해준다.

더보기
더보기
데이터 타입 : MySQL은 LIMIT, 오라클은 ROWNUM 사용

이런 DB마다 다른 점을 커버하기 위해 방언 속성이 추가되었다. JPA 표준 문법에 맞추어 JPA를 사용하면 특정 데이터베이스에 의존적인 SQL은 방언 클래스에서 알아서 수정해서 뿌려주게 된다.

 

 

  • <property name="hibernate.hbm2ddl.auto" value="create" />

데이터베이스 스키마 자동생성과 관련된 하이버네이트 속성인데, 이 속성은 다음 포스팅에 작성하려고 한다.

댓글