JPA에는 ddl-auto라는 설정이 있다.
해당 설정은 JPA를 이용해서 데이터베이스를 Initialize 할 수 있게 도와주는 설정이다.
Spring 공식 문서에서는 ddl-auto를 아래와 같이 설명하고 있다.
spring.jpa.hibernate.ddl-auto
(enum) is a Hibernate feature that controls the behavior in a more fine-grained way. See below for more detail
위 문장을 해석해보면, ddl-auto (enum)은 동작을 보다 세밀하게 제어하는 Hibernate 기능이라는 의미며 어떤 동작을 세밀하게 제어하는지
ddl-auto의 여러개의 속성들을 통해서, 함께 하나 하나 알아보자.
jpa:
hibernate:
ddl-auto: create
jpa:
hibernate:
ddl-auto: create-drop
jpa:
hibernate:
ddl-auto: create
jpa:
hibernate:
ddl-auto: validates
jpa:
hibernate:
ddl-auto: none
해당 ddl-auto를 위 지식 없이 뇌 빼고 사용하던 중 아래와 같은 질문이 생겨서 카카오 테크 캠퍼스 멘토님께 여쭈어보게 되었다.
질문
현재 배포 profile에서는 ddl-auto를 update로 사용하고 있습니다. 제가 ddl-auto를 더 공부하기 위해서 레퍼런스들을 찾아보던 중에 아래와 같은 말씀을 들었습니다.
spring:
config.activate.on-profile: prod
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
show_sql: true
format_sql: true
datasource:
driver-class-name: org.mariadb.jdbc.Driver
url: jdbc:mariadb://${DB_url}:${DB_port}/${DB_dataBaseName}?characterEncoding=UTF-8&serverTimezone=UTC
username: ${DB_name}
password: ${DB_password}
운영 서버에서는 최소한의 검증만을 위해서 ddl-auto를 사용하거나, 아예 사용하지 않아야 한다.
ddl-auto
속성들은 얼핏 보기엔 굉장히 편리해보이지만 validate
와 none
을 제외한 속성은 운영 DB에는 절대!!! 사용하면 안된다.
update
라고 하더라도 문제가 될 수 있는 것이, 만약 update
로 인해 새로 추가된 컬럼이 not null이라면 해당 변경사항이 반영되지 않은 버전을 배포하게 되었을 때 테이블에 데이터가 INSERT 되지 않을 수 있다. ← 이 구문이 정확히 어떤 상황을 이야기 하시는 건지가 이해가 안갑니다.
이 문장을 혹시 예시를 들어서 말씀해주실 수 있으실까요?
참고
[JPA] hibernate의 ddl-auto 속성의 종류와 주의해야할 점
새로 추가된 Not null 컬럼에는 필수값이 있어야 하는데 Update시 새롭게 컨텍스트를 로드해서 Update 하려고하는데, 해당 필드에 넣을 데이터가 없어서 Insert가 되지 않는다는 의미입니다.
그렇다면 실제 코드를 통해서 예시를 들면서 위 말씀을 복기해보자.
아래와 같은 엔티티가 하나 있다.
/**
* Walk(산책) 엔티티
*
* @author Kevin
* @version 1.0
*/
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class Walk {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@ManyToOne(fetch = FetchType.LAZY)
private Member walker;
@ManyToOne(fetch = FetchType.LAZY)
private Member master;
@Enumerated(value = EnumType.STRING)
private WalkStatus walkStatus;
private LocalDateTime startTime;
private LocalDateTime endTime;
}
이 때 jpa.hibernate의 ddl-auto를 update로 지정한다고 하고,
jpa:
hibernate:
ddl-auto: update
엔티티 코드를 아래와 같이 변경하고, 다시 애플리케이션을 실행한다고 하자.
/**
* Walk(산책) 엔티티
*
* @author Kevin
* @version 1.0
*/
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class Walk {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@ManyToOne(fetch = FetchType.LAZY)
private Member walker;
@ManyToOne(fetch = FetchType.LAZY)
private Member master;
@Enumerated(value = EnumType.STRING)
private WalkStatus walkStatus;
private LocalDateTime startTime;
private LocalDateTime endTime;
/* 해당 부분 추가 */
@Column(nullable = false)
private String walkReview;
}
산책의 후기를 저장하는 walkReview 필드를 추가했다고 할 때, 기존에 테이블 설계와 JPA 엔티티 설계간 차이가 생겼기에 update 설정으로 인해서 다시 스키마에 데이터들을 insert 한다. 이 때 insert 할 때 walkReview 필드는 NonNull 설정이기 때문에 insert 할 수 없게 된다.
그래서 테이블에 데이터가 들어가지 않는 치명적인 문제가 발생하게 된다.
매번 애플리케이션을 재배포할 때마다 DB 서버에 존재하던 데이터들을 삭제하는 것이 불편하여서 Update를 사용하였었는데, 카카오 크램폴린으로 배포할 때는 validate로 변경을 해야겠다.