엔티티 매핑

@Entity : JPA를 사용해 테이블과 매핑할 클래스로 지정하는 어노테이션.

  • 기본생성자가 반드시 필요하다.
  • final, enum, interface, inner 클래스에 사용할 수 없다.
  • 저장할 필드에 final을 사용할 수 없다.
  • name : 기본값으로 클래스명이 사용된다. 다른 패키지에서 중복 클래스명이 엔티티로 사용된다면 충돌을 막기위해 값 설정이 필요하다.

@Table : 엔티티와 매핑할 테이블을 지정하는 어노테이션.

  • name : 테이블명을 지정할 때 사용된다.(default 엔티티명)
  • uniqueConstraints : DDL 생성 시에 유니크 제약조건을 만든다. 복합 유니크 제약조건도 가능하다. 스키마 자동 생성 기능을 사용해서 DDL을 만들 때만 사용된다.
@Table(name = "t_user", indexes = { @Index(name = "idx_u_username", columnList = "username") }, 
uniqueConstraints = @UniqueConstraint(name="uk_user", columnNames = {"email", "username" }))

스키마 자동 생성

  • 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성한다.
  • DDL은 운영환경에서 사용할 만큼 완벽하지는 않으므로 개발 환경에서 사용하거나 매핑을 어떻게 해야하는지 참고하는 정도로만 사용한다.
  • 자동 생성되는 DDL은 지정한 데이터베이스 방언에 따라 달라진다. ex) mysql : varchar, integer / oracle : varchar2, number

스키마 자동 생성 종류

create : 기존 테이블 삭제후 다시 생성. drop + create
create-drop : 위 과정에 추가로 앱 종료시 DDL 제거. drop + create + drop
update : DB 테이블과 엔티티 매핑정보를 비교해 변경사항만 수정.
validate : DB 테이블과 엔티티 매핑정보를 비교해 차이가 있으면 경고. 앱 실행X. DDL 변경X.
none : 자동생성기능 사용안함.

JPA 기본 환경설정

@Configuration
public class DataConfig {

    @Autowired
    private DatabaseProperties databaseProperties;

    @Bean
    public DataSource dataSoure(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(databaseProperties.getDriver());
        dataSource.setUrl(databaseProperties.getUrl());
        dataSource.setUsername(databaseProperties.getUsername());
        dataSource.setPassword(databaseProperties.getPassword());
        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(){

        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
        factoryBean.setDataSource(dataSoure());
        factoryBean.setPackagesToScan("com.example.model");
        factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());

        // JPA 상세설정
        Properties jpaProperties = new Properties();
        jpaProperties.put(AvailableSettings.SHOW_SQL, true); // SQL 보기
        jpaProperties.put(AvailableSettings.FORMAT_SQL, true); // SQL 포맷팅하기
        jpaProperties.put(AvailableSettings.USE_SQL_COMMENTS, true); // SQL 코멘트 보기
        jpaProperties.put(AvailableSettings.HBM2DDL_AUTO, databaseProperties.getHbm2ddlAuto()); // DDL 자동생성 종류
        jpaProperties.put(AvailableSettings.DIALECT, databaseProperties.getDialect()); // 방언 설정
        jpaProperties.put(AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, true); // 키 생성 전략 사용시 설정
        jpaProperties.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy"); // 자바의 카멜 표기법을 테이블의 언더스코어 표기법으로 매핑
        factoryBean.setJpaProperties(jpaProperties);

        return factoryBean;
    }
}

기본키 매핑

  • 직접 할당 : @Id & Setter
    Board board = new board();
    board.setId("id1");
    em.persist(board); 
    // @Id로 지정한 id 필드에 Set메소드로 직접 할당하는 방법
  • 자동 생성 : 대리키 사용 방식. @GeneratedValue

영속성 컨텍스트는 엔티티를 영속 상태로 만드려면 식별자 값이 반드시 있어야 한다.
@Id, @GeneratedValue(strategy = GenerationType.매핑전략, generator = 식별자 생성기 이름)

GenerationType.IDENTITY

  • 기본키 생성을 DB에 위임하는 전략.
  • DB 종속적. Mysql, PostgreSQL, SQL SERVER
  • DB 저장시 @Id 컬럼을 비워두면 DB가 순서대로 값을 채워준다.
  • 전략 사용시 JPA는 기본키 값을 얻어오기 위해 DB를 추가로 조회한다.
  • statement.getGerenatedkey() 사용하면 데이터를 저장하면서 동시에 기본키 값을 얻어와서 DB를 한번만 조회한다.
  • 트렌잭션을 지원하는 쓰기 지연이 동작하지 않는다.

GenerationType.SEQUENCE

  • DB 시퀀스를 사용해 기본키를 할당한다.
  • DB 종속적. Oracle, H2, DB2
  • 엔티티나 컬럼에 @SequenceGenerator 표기
    • name : 식별자 생성기 이름
    • sequenceName : DB 시퀀스명
    • initialValue : 시퀀스 시작 값 (default 1)
    • allocationSize : 시퀀스 호출 시 증가 수 (default 50)
  • DB 시퀀스를 사용해 식별자를 조회해 엔티티에 할당 후, 엔티티를 영속성 컨텍스트에 저장하고 트랜잭션을 커밋하면 DB에 저장된다.

GenerationType.TABLE

  • 엔티티에 @TableGenerator 표기
    • name : 식별자 생성기 이름 (필수)
    • table : 키 생성 테이블명
    • pkColumnName : 시퀀스 컬럼명
    • valueColumnName : 시퀀스 값 컬럼명
  • 키 생성 전용 테이블 만들고 이름과 같은 값으로 사용할 컬럼을 생성한다.
  • 내부 동작은 SEQUENCE와 같다.
Sequence 테이블 생성 DDL

CREATE TABLE MY_SEQUENCE{
  SEQUENCE_NAME VARCHAR(255) NOT NULL,
  NEXT_VAL BIGINT,
  PRIMARY KEY (SEQUENCE_NAME)
}
TABLE 전략 매핑 코드

@Entity
@TableGenerator(
 name = "BOARD_SEQ_GENERATOR",
 table = "MY_SEQUENCE",
 pkColumnValue = "BOARD_SEQ",
 allocationSize = 1)
public class Board {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "BOARD_SEQ_GENERATOR")
    private Long id;
}

GenerationType.AUTO

  • DB 방언에 따라 위의 전략 중 하나를 자동으로 선택한다. ex) MySQL : IDENTITY
  • DB 변경시 코드를 수정할 필요가 없다.
  • SEQUENCE 전략이 사용되면 하이버네이트가 기본값을 사용해서 적절한 시퀀스를 만들어준다.

권장 식별자 선택 전략

  • 기본키 조건 : NOT NULL, 유일 값, 불변
  • 기본키 선택전략
    • 자연키 : 비즈니스에 의미가 있는 키. ex) email, 주민등록번호 등
    • 대리키(대체키) : 비즈니스에 무관한 임의의 키. ex) uuid, sequence, auto_increment 등

필드와 컬럼 매핑

@Column : 객체 필드를 테이블 컬럼과 매핑시킨다.

  • name(default 필드명) : 필드와 매핑할 컬럼명
  • nullable(default true) : null 허용 여부.
  • unique(default false) : 컬럼에 유일값 허용 여부.
  • length(default 255) : 문자길이 제약조건. String 필드에만 사용한다.
  • columnDefinition : 직접 정의 "varchar(10) default 'test'". 사용하는 DB 종류에 종속될 수 있으므로 지양한다.
  • insertable,updatable(default true) : 읽기전용시 false로 설정하여 DB에 저장/수정이 불가하도록 지정한다.자바 기본(primitive)타입에 @Column을 사용하면 nullable = false로 지정하는 것이 안전하다.

@Enumerated : 열거 타입 매핑

  • EnumType.ORDINAL(default) - enum 순서를 DB에 저장하기 때문에 순서가 변경될 경우 안전하지 못하다.
  • EnumType.STRING - enum 이름을 DB에 저장하기 때문에 안전하다.

@Temporal : 날짜 타입 매핑

  • TemporalType.DATE > 2013-10-11
  • TemporalType.TIME > 11:11:11
  • TemporalType.TIMESTAMP(default) > 2013-10-11 11:11:11

@Lob : CLOB,BLOB 타입 매핑

  • CLOB : 문자. String, char[], CLOB (mysql : longtext / oracle : clob)
  • BLOB : 나머지. byte[], BLOB (mysql : longblob / oracle : blob)

@Transient : 특정 필드 매핑 제외

  • 컬럼과 매핑하지 않으므로 DB에 저장하지 않는다.
  • 객체 필드에 임시로 데이터를 보관하는 용도. ex) 비밀번호/확인비밀번호 값을 객체 필드로 각각 입력받아 동일한 값인지 확인

@Access : JPA가 엔티티에 접근하는 방식 지정. @Id 대체

  • AccessType.FIELD : 필드에 직접 접근한다. 필드 접근 권한이 private 이어도 접근이 가능하다.
  • AccessType.PROPERTY : 접근자 Getter를 사용해 접근한다.
    @Id가 필드에 있냐 프로퍼티에 있냐에 따라 access 방식이 달라진다.

  • AccessType이 FIELD로 정의된 경우 영속화 과정에서 필드에 데이터를 설정하거나 읽어올 때, 
    메소드를 통하지 않고 직접 필드에 접근해서 읽어오기 때문에 Get/Set 메소드에 별도의 로직이 존재하는 경우 동작하지 않는다.

  • 반대로 필드명과 Get메소드의 이름이 다를 때, 별도의 로직을 통해 변환된 값을 DB에 저장하고 싶다면@Transient 어노테이션을 필드에 부여해 영속화에서 제외시키고 Get메소드에 @Access(AccessType.PROPERTY)를 설정하면 된다.
@Entity
public class Member {

    @Id
    private String id;

    @Transient
    private String firstName;

    @Transient
    private String lastName;

    @Access(AccessType.PROPERTY)
    public String getFullName(){
        return this.firstName + this.lastName;
    }
 }
// @Id가 필드에 있으므로 필드 접근 방식을 사용하고 getFullName만 프로퍼티를 사용한다.
// Member 엔티티에 fullName 컬럼이 생성되고 firstName + lastName의 결과가 저장된다.


참고서적 - 자바 ORM 표준 JPA 프로그래밍 (김영한)

'Development > JPA' 카테고리의 다른 글

영속성 관리  (0) 2017.01.21
JPA 시작  (0) 2016.11.20
JPA 소개  (0) 2016.05.04

+ Recent posts