JPA는 스펙상 엔티티, 임베디드 값 타입에 리플렉션(Reflection), 프록시(Proxy) 기술을 적용하기 위해 public 또는 protected의 기본 생성자를 두는 것을 정의하고 있다.

 

 

리플렉션이란?

더보기

Java Reflection API

 

자바는 컴파일 시점에서 타입이 결정되는데 다음과 같이 구체적인 클래스로 생성하지 않을 경우 컴파일 시점에서 Object 타입으로 결정이 되기 때문에 Car 클래스의 메소드 등에 접근할 수가 없다.

 

Object car = new Car(); 

 

자바에서는 JVM이 실행되면 자바 코드가 컴파일러를 거쳐 바이트코드로 변환되어 static 영역에 저장되는데 리플렉션은 이 정보를 활용하여 구체적인 클래스 타입을 알지 못해도 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해준다. 하지만 런타임 시점에 동적으로 타입을 분석하기 때문에 JVM을 최적화할 수 없는 등 여러 문제점이 있어서 일반적으로는 사용하지 않는 것이 좋다.

리플렉션은 애플리케이션 개발보다 프레임워크나 라이브러리에서 많이 사용하는데 프레임워크나 라이브러리는 사용자가 어떤 클래스를 만들지 예측할 수가 없기 때문에 리플렉션을 활용한다.

JPA도 동적으로 객체 생성시 리플렉션을 활용하는데 리플렉션으로 가져올 수 없는 정보 중 하나가 생성자의 인자 정보이다. 그래서 기본 생성자가 반드시 있어야 객체를 생성할 수 있다. 기본 생성자로 객체를 생성만 하면 필드 값 등을 리플렉션으로 넣어줄 수 있다.

 

 

Reflection API 간단히 알아보자.

Spring Framework를 학습하다 보면 Java Reflection API를 자주 접하게 된다. 하지만 Reflection API…

tecoble.techcourse.co.kr

 

 

하이버네이트 같은 구현체들은 기본 생성자가 없어도 라이브러리들을 통해 이러한 문제를 어느정도 회피하지만 완벽한 해결책이 아니기 때문에 기본 생성자를 필수로 두는 것이 좋다. 기본 생성자를 public으로 열어두면 의도치 않은 무분별한 생성이 될 수 있기 때문에  protected로 선언하여 다른 패키지에서 기본 생성자를 생성할 수 없도록 제한한다.

 

그리고 엔티티는 생성자나 Setter를 사용하는 것보다는 빌더(Builder) 패턴 또는 정적 팩토리 메서드(static factory method)를 사용하는 것이 좋은데 빌더 패턴은 필요한 데이터만 설정할 수 있고 유연성, 가독성이 뛰어나며 변경 가능성을 최소화 할 수 있다.

 

[Java] 빌더 패턴(Builder Pattern)을 사용해야 하는 이유

객체를 생성하기 위해서는 생성자 패턴, 정적 메소드 패턴, 수정자 패턴, 빌더 패턴 등을 사용할 수 있습니다. 개인적으로 객체를 생성할 때에는 반드시 빌더 패턴을 사용해야 한다고 생각하는

mangkyu.tistory.com

 

정적 팩토리 메서드(static factory method)는 객체 생성을 캡슐화하는 기법으로 객체를 생성하는 메소드를 만들고 static으로 선언하여 사용한다. 메서드명을 의미 있게 지을 수 있어 생성자에 비해 가독성이 좋아지고 호출할 때마다 새로운 객체를 생성할 필요가 없다. 그리고 하위 타입 객체를 반환할 수 있으며 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다. 

 

public static User createUser(String name, int age) {
    OrderItem orderItem = new OrderItem();
    this.name = name;
    this.age = age;
    
    return user;
}

 

파라미터가 많거나 변경 가능성이 많은 경우에는 빌더 패턴을, 그렇지 않은 경우에는 정적 팩토리 메소드를 사용하는 것이 좋다.

@Entity 애노테이션이 붙은 클래스는 JPA가 관리하는 엔티티로 JPA를 사용해서 테이블과 매핑할거면 필수로 입력해야 한다.

파라미터가 없는 public 또는 protected 생성자가 필수이며 final 클래스나 ENUM, 인터페이스, Inner 클래스는 엔티티로 등록할 수가 없다. (저장할 필드에 final 사용 x)

 

@Table 애노테이션은 Entity와 매핑할 테이블을 지정할 수 있다. (기본값 엔티티명)

 

ddl-auto 기능을 사용하면 DDL(Database Definition Language)을 애플리케이션 실행 시점에 자동으로 적절하게 생성해주는데 개발 단계에서만 사용하고 운영 서버에서는 사용하지 않는 것이 좋다.

 

  • create : 기존 테이블 삭제 후 재생성 (drop + create)
  • create-drop : 종료 시점에 테이블 (drop)
  • update : 변경부분만 반영
  • validate : 엔티티와 테이블 정상 매핑 확인
  • none : 사용 x(default)

 

개발 초기에는 create, update를 사용하고 테스트 서버는 update, validate, 스테이징과 운영 서버는 validate 또는 none을 주로 사용한다. DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.

 

 

필드 매핑 애노테이션

  • @Column : 기본 Column
  • @Temporal : 날짜 타입 (LocalDate, LocalDateTime 사용 시 생략 가능)
  • @Enumerated : ENUM 타입 (기본 설정인 ORDINAL 대신 STRING 사용)
  • @Lob : 데이터베이스 BLOB, CLOB과 매핑, 문자면 CLOB 나머지는 BLOB 매핑
  • @Transient : 매핑 무시 (메모리상에 임시로 값 보관할 때 사용)

 

기본 키 매핑 애노테이션은 @Id(Private Key), @GeneratedValue가 있는데 GeneratedValue는 다음과 같이 설정을 할 수있다.

 

IDENTITY

  • 데이터베이스에 위임 (MySQL의 AUTO_INCREMENT, PostgreSQL ..)
  • AUTO_INCREMENT는 INSERT를 한 이후에 ID 값을 알 수 있어서 em.persist() 시점에 즉시 INSERT SQL이 실행된다.

 

SEQUENCE

데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트로 @SequenceGenerator 선언이 추가로 필요하다. (ORACLE, PostgreSQL, H2..) 

 

@SecuenceGenerator

  • name : 식별자 생성기 이름
  • sequenceName : 데이터베이스에 등록되어 있는 시퀀스 이름
  • initialValue : DDL 생성시에만 사용, 시퀀스 DDL 생성할 때 처음 시작하는 수를 지정 (기본값 1)
  • allocationSize : 시퀀스 한번 호출시 증가하는 수, 이 값이 1이면 매번 DB에 접근해서 시퀀스 값을 받아온다. (기본값 50)

기타)

TABLE : 키 생성용 테이블, 모든 DB에서 사용, @TableGenerator 필요 (성능상 잘 쓰진 않음)

AUTO : 데이터베이스에 따라 자동 지정

 

 

 

[참고] 인프런 김영한님 강의를 공부한 내용입니다.

+ Recent posts