[Project] JPA/1/JPA+Entity

Fortice·2021년 5월 24일
0

Project

목록 보기
3/8

JPA 란?

Java Persistance API로 자바 플랫폼에서 내부 관계형 데이터 관리를 위한 ORM 기술 표준이다. JPA는 인터페이스로 실제 구현을 해야하는데, 오픈소스로 구현한 예로 Hibernate가 있다.

Application과 JDBC API 사이에 위치하여, 개발자는 JPA를 사용만 하면 되고, 실제 데이터에 대한 접근은 JPA가 JDBC API의 SQL을 호출하여 DB에 접근하게 된다.

장점

  • 객체 지향적으로 데이터를 관리할 수 있어, 비즈니스 로직 구현에 집중할 수 있다.
  • DDL을 통해 테이블 관리가 쉽다.
  • 로직을 쿼리보다 객체에 집중할 수 있다.
  • 빠른 개발이 가능하다.

단점

  • 어렵고, 장점을 발휘하기 위해 알아야 할게 많다.
  • 이해가 부족하면 데이터 손실이 발생한다.(Persistence context)
  • 이해가 부족하면 성능에 문제가 발생할 수 있다.

Project 설정

Dependency 설정 (gradle)

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation group: 'mysql', name: 'mysql-connector-java', version: '8.0.25'
}

JPA 설정

application.property에 DB연결 및 JPA설정을 작성한다.

# Server Setting
server.address = localhost
server.port = 8080

# DataSource Setting
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
### MySQL URL 설정
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/dbname?characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=dbuser
spring.datasource.password=dbpassword

# JPA Setting
### DDL generation
spring.jpa.hibernate.ddl-auto=update

### @Entity 어노테이션이 붙은 클래스를 찾아서 ddl을 생성하고 실행
spring.jpa.generate-ddl=false

### SQL문을 Log에 출력
spring.jpa.show-sql=true

### DB를 어떤 것을 사용할지 지정
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.properties.hibernate.format_sql=true

# Logging Setting
logging.level.org.hibernate=info

설정 설명

  • DB URL 설정
    • createDatabaseIfNotExist: 데이터베이스가 존재하지 않으면 자동으로 생성하는 옵션입니다.
    • useUnicode: 유니코드 사용 여부를 설정합니다.
    • characterEncoding: 문자열 인코딩 종류를 설정합니다.
    • characterSetResult: 결과값의 인코딩 종류를 설정합니다.
    • useSSL: SSL 사용 여부에 대한 설정입니다.
  • DDL Generation
    • Entity Scan에 의해 찾은 @Entity 클래스들을 실제 DB에 적용시킬 지 정함
    • none : 아무것도 실행하지 않음
    • create-drop : SessionFactory가 시작될 때 drop및 생성을 실행, SessionFactory가 종료될 때 drop을 실행 (삭제 후 생성, 종료 시 삭제)
    • create : SessionFactory가 시작될 때 데이터베이스 drop을 실행하고 생성된 DDL을 실행. (삭제 후 생성)
    • update : 변경된 스키마를 적용 (변경 사항만 반영)
    • validate : 변경된 스키마가 있다면 변경됨을 출력하고 애플리케이션 종료 (매핑되었는지만 확인)
    • create, update, create-drop은 DB에 직접적인 영향이 있기 때문에 운영 DB에 사용 하면 안됨

JPA Mapping

ORM중 하나인 JPA의 어노테이션을 활용한 매핑 방식들을 알아본다.

@Entity

  • Entity 클래스
    • JPA가 관리하는 클래스로, 실제 DB의 테이블과 매핑되는 클래스이다.
    • JPA를 구현하는 라이브러리들이 Reflection같은 기술을 사용하기 위해 객체를 프록싱할 필요가 있어 기본 생성자가 무조건 필요하다.
      • 파라미터가 없는 public 또는 protected 생성자가 필요하다.
    • 최대한 외부에서 Entity 클래스의 getter method를 사용하지 않도록 해당 클래스 안에서 필요한 로직 method을 구현한다.
      • Domain Logic만 가지고 있어야 하고 Presentation Logic을 가지고 있어서는 안된다.
    • Entity 클래스는 DTO같이 VIEW와의 통신을 위한 Res/Req에 따라 자주 바뀌는 클래스와 다르게 DB의 테이블과 매핑되고, Entity 클래스는 변경 시 여러 클래스에 영향을 미치므로, 단순 영속성을 위해서만 사용된다.
      • 여기서 구현한 method는 주로 Service Layer에서 사용한다.
      • DTO는 Entity 클래스에 Presentation Logic을 추가한 형태로 사용된다.
  • @Entity 속성
    • String name
      • JPA에서 사용할 이름
      • DEFAULT : 클래스 이름
      • 대부분 이름이 중복되지 않는다면, name 지정 없이 사용한다.

@Table

  • Entity와 매핑할 테이블 지정
  • @Table 속성
[type] 속성설명
String name테이블 명
DEFAULT : snake_case의 Entity 명
String catalog데이터베이스 catalog 매핑
DEFAULT : default catalog
String schema데이터베이스 schema 매핑
DEFAULT : default schema for user
UniqueConstraint[] uniqueConstraintsDDL 생성 시에 유니크 제약 조건 생성
@Column,@JoinColumn에 지정된 제약 조건과
기본 키 매핑에 수반되는 제약 조건에 추가로 적용
@Table(uniqueConstraints = {@UniqueConstraint(name = "CONSTRAINT_NAME", columnNames = {"name", "phone"})})
DEFAULT : no additional constraint
Index[] index테이블의 인덱스
기본 키의 인덱스는 자동으로 생성됨

JPA/Hibernate Naming 전략

순서
1. 도메인 모델 매핑으로부터 적절한 논리적 이름을 정한다.
1-1. 논리적 이름은 @Column 또는 @Table등을 사용해서 사용자로 부터 이름을 부여 받거나 ImplicitNamingStrategy 전략을 통해서 Hibernate를 통해 암묵적으로 부여 받는다.
2. PhysicalNamingStrategy 전략을 통해서 논리적 이름을 물리적 이름으로 정의하는 것이다. (camelCase --> snake_case)

ImplicitNamingStrategy

  • @Column, @Table의 name 속성으로 지정한 이름으로 매핑
  • attributeName 속성 이름이 별도 지정하지 않은 경우 "attributeName"으로 매핑
  • application.properties 설정
    • spring.jpa.hibernate.naming.implicit-strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy

PhysicalNamingStrategy

  • 특별하게 column name을 명시하였거나 암묵적으로 결정 되었는지에 관련 없이 적용
  • attr_name 처럼 별도 물리적 컬럼 이름으로 대신하게 할 수 있다.
  • application.properties 설정
    • spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy

변수 이름 그대로 사용

  • spring.jpa.hibernate.naming.physical-strategy = org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

참고 블로그

@Column

  • 변수와 Column을 매핑
  • @Column 속성
[type] 속성설명
String name컬럼 명 지정
DEFAULT : camelCase의 필드명
(application.properties에 전략을 번경해주면 snake_case 형태로 바꿀 수 있다.)
boolean uniqueUnique 속성 지정
DEFAULT : False
boolean nullableNULLABLE 속성 지정
DEFAULT : True
boolean insertableJPA에 의해 만들어진 INSERT 명령에 포함되는지 지정
DEFAULT : True
boolean updatableJPA에 의해 만들어진 UPDATE 명령에 포함되는지 지정
DEFAULT : True
String columnDefinitionColumn에 대한 DDL 생성
@Column(columnDefinition = "varchar(100) default 'EMPTY'")
DEFAULT : If absent the column is assumed to be in the primary table
String table해당 Column을 포함하는 테이블 이름
int lengthString 타입의 컬럼에 길이 제한 지정
DEFAULT : 255
int precision10진수 숫자의 값을 가지는 열에 대한 전체 자리수
DEFAULT : 0
int scale10진수 숫자의 소수점 이후 자리수
DEFAULT : 0

@Id

  • 기본 키 매핑
    • JPA에서 Entity 클래스 안에 명시적으로 Primary Key를 알려야 한다.
    • Primary Key값 생성에 데드락이 걸리지 않도록, Java Long 자료형을 사용한다.

@GeneratedValue

  • @Id 어노테이션이 붙은 Primary Key에 대해 MySQL의 Auto Increment 특성을 걸어준다.
  • strategy 속성
    • DBMS마다 전략이 다르다.
    • MySQL : strategy = GenerationType.IDENTITY
    • ORACLE : strategy = GenerationType.SEQUENCE
      • @SequenceGenerator 어노테이션과 같이 이용한다.

@Embeddable

  • 보통은 단일의 PK를 가지지만, 두개 이상의 Attribute를 묶어서 복합키로 PK를 만들 때 사용한다.
  • Entity 클래스 내에서 정의하지 않고, 객체를 생성하고 해당 객체를 이용한다.
@Embeddable
public class TestKey implements Serializable {
    @Column(name = "first_key"
    private Long firstKey
    
    @Column(name = "second_key"
    private Long secondKey
}
@Entity
public class EmbeddableTest {
    @EmbeddedId
    protected TestKey testKey;
}

@Enumerated

  • Java에 enum 형태로 이미 정해져 있는 코드값을 사용할때 사용
  • type 속성 지정
    • EnumType.ORDINAL : enum 객체에 정의된 순서
    • EnumType.STRING : enum 객체에 정의된 이름 그 자체
enum TestTF {
    False, True
}
@Enumerated(EnumType.ORIDNAL)
@Column(name="ordinal_tf")
private TestTF ordinalTF; // 0, 1로 저장

@Enumerated(EnumType.STRING)
@Column(name="string_tf")
private TestTF stringTF; // "False", "True"로 저장

@Transient

  • DB에는 사룔하지 않는, Entity 객체에서 임시로 데이터를 담을 변수에 사용

@Temporal

  • 날짜 타입과 java.util.Date or Calendar에 매핑할 때 사용
  • 3가지 타입으로 나뉨 (TemporalType 속성)
    • TemporalType.DATE : 년-월-일
    • TemporalType.TIME : 시-분-초
    • TemporalType.TIMESTAMP : DATE + TIME

@Lob

  • varchar형을 넘어서는 큰 내용을 넣고 싶을 경우 사용
  • 속성은 없음

@Access

  • 필드 접근 방식, 프로퍼티 접근 방식을 지정한다.
    • AccessType.FIELD
    • AccessType.PROPERTY
  • @Id가 필드에 있으면 기본적으로 필드 접근 방식을 사용한다.
  • 특별한 계산 혹은 변환이 필요한 경우, 임시 데이터는 Transient로 저장하고, get 메소드에 프로퍼티 방식을 적용해 특수 컬럼을 저장하도록 한다.
@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;
    }
}

연관관계 매핑

profile
서버 공부합니다.

0개의 댓글