본문 바로가기
공부/Spring Boot

SpringBoot에 Flyway적용해서 DB 형상 관리 하기 + JPA 옵션, flyway 옵션 정리

by son_i 2024. 8. 26.
728x90

Flyway란 ?

DB 형상관리 툴이다. == DB 변경 관리 툴

 

* 형상관리 : sw의 변경사항을 체계적으로 추적하고 통제하는 것.

 

Git은 소스코드 형상관리 툴이고 Flyway는 DB 형상관리 툴이다.

 

어떤 상황에서 ?

개발 도중 스키마가 변경 될 때

1. spring:jpa:hibernate:ddl-auto: create or update를 주로 사용한다.

 

문제점 -> create는 이전 데이터를 다 날려버리므로 배포 환경에서 사용할 수 없음이 명확하고 update는 테이블을 수정하지 않고 부족한 부분을 수정한다.

 

2. 각 배포 환경을 돌아다니면서 직접 schema 변경

 

문제점 -> 확인하는 것도 일이고 실수가 나기 쉽다.

 

이런 DB 가 변경될 상황들에서 변경 관리 이력을 추적하고 관리하기 위해 Flyway 가 사용된다.

 

Naming

V{버전}__{설명}.sql 형식으로 작성한다.

버전다음 언더바는 2개이다 !! 이걸 까먹고 하나로 작성해서 한참 삽질했다 ..

 

V다음에 오는 버전 명시 방법은 다양함 ex) 1, 1.1, 20190908

 

Location 

 

main/resources/db/migration : default 경로

properties 파일에 spring.flyway.locations=classpath:원하는경로입력

spring.flyway.locations=classpath:원하는경로/{vendor} 하면 db 종류에 따라 폴더 관리 가능.

 

유의할 점

1. 마이그레이션은 최신 버전보다 높은 경우에만 진행 됨.

  - 추가 작업은 새로운 버전을 만들어야 함.

 

2. 변경 기록 테이블이나 (flyway_schema_history) 마이그레이션 스크립트를 지우면 안 됨 !

 

테이블을 하나 생성해서 변경 이력을 쌓고 최신보다 높은 버전으로 변경사항 스크립트 파일을 작성하면 flyway가 그걸 반영해서 DB를 변경함.

구현 과정

1. 의존성 추가

//flyway - 아래 경우 외의 경우
    implementation 'org.flywaydb:flyway-core'
//flyway - MySQL 8.x 버전이거나 MariaDB를 사용하는 경우
    implementation 'org.flywaydb:flyway-mysql'

MySQL 8.x 버전이거나 MariaDB 사용 시 implemetation 'org.flywaydb:flyway-mysql'

그 외의 경우는 implementation 'org.flyway.db:flyway-core'

 

나는 로컬환경에서는 h2 DB를 사용하고 배포(운영)환경에서는 mariaDB를 사용하므로 두 가지 모두를 넣어주었다.

 

2. application.yml 

여기서 jpa hibernate 설정에 익숙하지 않아 뭐를 넣어주고 빼야할 지 몰라 엄청 애를 먹었다.

 

일단 flyway를 사용하기 위해 적어줘야 하는 것들은 아래와 같다.

spring:
	jpa:
    	properties:
      		hibernate:
        		jdbc:
          			lob:
            			non_contextual_creation: true
  flyway:
    enabled: true
    baseline-on-migrate: true
    locations: classpath:db/migration/h2
    fail-on-missing-locations: true

 

jpa 설정

spring:jpa:hibernate:ddl-auto: none으로 하거나 제거.

  초기 테이블 생성 옵션으로 create / create-drop / update / validate / none 옵션이 있고

  운영환경에서는 vaidate를 사용한다. validate를 할 경우 초기 테이블이 생성되어 있어야 한다.

  validate는 데이터베이스의 스키마와 jpa 엔티티가 불일치할 때 실행이 되지 않는다.

 

spring:jpa.show_sql: false로 하거나 제거. 

  hiberante에서 실행하는 SQL문을 출력할 지 여부.

  JPA가 테이블을 만들어주는 게 아닌 flyway가 만들어주는 것이기 때문에 true로 해도 아무 것도 나오지 않음.

 

spring:properties:hibernate:format_sql: false로 하거나 제거.

  show_sql이 활성화 되어있을 때 같이 사용하여 show_sql로 출력되는 쿼리를 포매팅 해주는 옵션인데 show_sql을 사용하지 않으니 필요 없음.

 

spring:jpa:defer-datasource-initialization: false로 하거나 제거.

  hibernate 기반의 초기화와 script 기반의 초기화가 함께 사용되어야 할 때 spring boot 2.5.x 버전 이후에 필수적인 옵션이다. true로 할 경우 hibernate 초기화 이후에 scipt 파일(data.sql ..etc)을 실행한다.


spring:generate-ddl: false
로 하거나 제거.

  true로 할 시 @Entity가 명시한 클래스를 찾아 서버 시작 시점에 DDL을 작성하여 DB에 적용함.

 

선택 spring:jpa:properties:hibernate:jdbc:lob:non_contextual_creation: true로 설정

  true로 설정할 경우 실행 시 warning을 제거한다

 

flyway 설정

필수 spring:flyway:enabled: true

  flyway를 활성화 하기 위한 설정. 기본 값은  true

 

spring:flyway:baseline-on-migrate: true

  기본 값 false. false일 경우 flyway_schema_history 테이블이 기존에 생성되어있어야 한다.

  true로 설정하여 flyway_schema_history 테이블이 자동 생성 되지 않는 경우 생성해준다.

 

spring:flyway:locations: classpath:/db/migration/{vendor}

  여러 DB를 사용해 스크립트 파일 폴더를 분리할 경우 필수 

  classpath 경로는 resources 경로를 가리킨다.

  나는 h2 DB와 mariaDB가 다른 스크립트 문법을 사용해야 하기 때문에 application.yml과 application-prod.yml에 각기 다른 경로를 지정해주었다.

 

spring:flyway:fail-on-missing-locations: true

  스크립트 파일의 위치를 찾지 못 할 때 에러를 발생시킨다.

 

3. 마이그레이션 파일 생성하기

초기 테이블을 생성하는 V1__init_table.sql을 작성하였다. 버전다음에 밑줄은 반드시 두 개 !! ㅠㅠ

 

main/resources/db/migration/h2/V1__init_table.sql

h2 DB는 ENUM 형태의 자료형이 없어서 ENUM 타입들의 속성 타입을 다 VARCHAR(100)으로 변경해주었다.

drop table if exists Member;
drop table if exists Notification;
drop table if exists FriendInfo;
drop table if exists FriendState;
drop table if exists Pet;
drop table if exists CareGiver;
drop table if exists Schedule;
drop table if exists PetSchedule;
drop table if exists ScheduleCategory;
drop table if exists CustomRepeatPattern;
drop table if exists Post;
drop table if exists Comment;
drop table if exists Sympathy;
drop table if exists PostImage;
drop table if exists PostCategory;

--Member table
CREATE TABLE Member (
	`member_id`	BIGINT	AUTO_INCREMENT NOT NULL PRIMARY KEY,
	`name`	VARCHAR(10)	NOT NULL,
	`email`	VARCHAR(50)	NOT NULL,
	`password`	VARCHAR(100) NULL,
	`phone`	VARCHAR(15)	NOT NULL,
	`image`	VARCHAR(100) NULL,
	`status`    VARCHAR(100)	NOT NULL,
	`created_at`	TIMESTAMP	NOT NULL,
	`updated_at`	TIMESTAMP	NOT NULL
);

--Notification table
CREATE TABLE `Notification` (
	`notice_id`	BIGINT AUTO_INCREMENT NOT NULL,
	`member_id` BIGINT NOT NULL,
	`type` VARCHAR(100) NOT NULL,
	`entity_id`	BIGINT NOT NULL,
	`is_read`BOOLEAN NOT NULL,
	`created_at` TIMESTAMP	NOT NULL,
	`updated_at` TIMESTAMP	NOT NULL,
	PRIMARY KEY (`notice_id`, `member_id`),
	CONSTRAINT `FK_Member_TO_Notification_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`)
);

--FriendState table
CREATE TABLE `FriendState` (
	`friend_status_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`status`	VARCHAR(50)	NOT NULL,
	PRIMARY KEY (`friend_status_id`)
);

--FriendInfo table
CREATE TABLE `FriendInfo` (
	`friend_info_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`friend_status_id`	BIGINT	NOT NULL,
	`friend_id`	BIGINT	NOT NULL,
	`created_at`	TIMESTAMP	NOT NULL,
	`updated_at`	TIMESTAMP	NOT NULL,
	PRIMARY KEY (`friend_info_id`, `member_id`, `friend_status_id`),
	CONSTRAINT `FK_Member_TO_FriendInfo_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`),
	CONSTRAINT `FK_FriendState_TO_FriendInfo_1` FOREIGN KEY (`friend_status_id`) REFERENCES `FriendState` (`friend_status_id`)
);

--Pet table
CREATE TABLE `Pet` (
	`pet_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`pet_name`	VARCHAR(30)	NOT NULL,
	`species`	VARCHAR(100)	NOT NULL,
	`breed`	VARCHAR(100)	NOT NULL,
	`pet_gender`	VARCHAR(100)	NOT NULL,
	`pet_age`	TINYINT	NOT NULL,
	`pet_image`	VARCHAR(100)	NULL,
	`memo`	TEXT	NULL,
	`created_at`	TIMESTAMP	NOT NULL,
	`updated_at`	TIMESTAMP	NOT NULL,
	`status`	VARCHAR(100)	NOT NULL,
	PRIMARY KEY (`pet_id`, `member_id`),
	UNIQUE (`pet_id`),
	CONSTRAINT `FK_Member_TO_Pet_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`)
);

--CareGiver table
CREATE TABLE `CareGiver` (
	`care_giver_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`pet_id`	BIGINT	NOT NULL,
	 PRIMARY KEY (`care_giver_id`, `member_id`, `pet_id`),
	 CONSTRAINT `FK_Member_TO_CareGiver_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`),
	 CONSTRAINT `FK_Pet_TO_CareGiver_1` FOREIGN KEY (`pet_id`) REFERENCES `Pet` (`pet_id`)
);

--ScheduleCategory table
CREATE TABLE `ScheduleCategory` (
	`category_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`category_name`	VARCHAR(255)	NOT NULL,
	PRIMARY KEY (`category_id`, `member_id`),
	CONSTRAINT `FK_Member_TO_ScheduleCategory_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`)
);

--Schedule table
CREATE TABLE `Schedule` (
	`schedule_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`category_id`	BIGINT	NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`schedule_title`	VARCHAR(20)	NOT NULL,
	`schedule_content`	TEXT	NULL,
	`schedule_at`	TIMESTAMP	NOT NULL,
	`repeat_type`	VARCHAR(100)	NOT NULL,
	`repeat_cycle`	VARCHAR(100)	NULL,
	`notice_yn`	BOOLEAN	NOT NULL,
	`notice_at`	BIGINT	NULL,
	`priority`	VARCHAR(100)	NULL,
	`status`	VARCHAR(100)	NOT NULL,
	`created_at`	TIMESTAMP	NOT NULL,
	`updated_at`	TIMESTAMP	NOT NULL,
	PRIMARY KEY (`schedule_id`, `category_id`, `member_id`),
	UNIQUE (`schedule_id`),
	CONSTRAINT `FK_ScheduleCategory_TO_Schedule_1` FOREIGN KEY (`category_id`, `member_id`) REFERENCES `ScheduleCategory` (`category_id`, `member_id`),
        CONSTRAINT `FK_Member_TO_Schedule_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`)
);

---PetSchedule table
CREATE TABLE `PetSchedule` (
    `pet_schedule_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`pet_id`	BIGINT	NOT NULL,
	`schedule_id`	BIGINT	NOT NULL,
	 PRIMARY KEY (`pet_schedule_id`, `pet_id`, `schedule_id`),
	 CONSTRAINT `FK_Pet_TO_PetSchedule_1` FOREIGN KEY (`pet_id`) REFERENCES `Pet` (`pet_id`),
     CONSTRAINT `FK_Schedule_TO_PetSchedule_1` FOREIGN KEY (`schedule_id`) REFERENCES `Schedule` (`schedule_id`)
);

--CustomRepeatPattern table
CREATE TABLE `CustomRepeatPattern` (
	`id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`frequency`	VARCHAR(100)	NOT NULL,
	`interval`	BIGINT	NULL,
	`daysOfWeek`	VARCHAR(100)	NULL,
	`daysOfMonth`	VARCHAR(255)	NULL,
	`endDate`	VARCHAR(255)	NULL,
	CONSTRAINT `PK_CUSTOMREPEATPATTERN` PRIMARY KEY (`id`)
);

--PostCategory table
CREATE TABLE `PostCategory` (
	`post_category_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`category_name`	VARCHAR(50)	NOT NULL,
	PRIMARY KEY (`post_category_id`)
);

--Post table
CREATE TABLE `Post` (
	`post_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`post_category_id`	BIGINT	NOT NULL,
	`post_title`	VARCHAR(50)	NOT NULL,
	`post_content`	TEXT	NOT NULL,
	`created_at`	TIMESTAMP	NOT NULL,
	`updated_at`	TIMESTAMP	NOT NULL,
	`status`	VARCHAR(100)	NOT NULL,
	PRIMARY KEY (`post_id`, `member_id`, `post_category_id`),
	UNIQUE(`post_id`),
	CONSTRAINT `FK_Member_TO_Post_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`),
	CONSTRAINT `FK_PostCategory_TO_Post_1` FOREIGN KEY (`post_category_id`) REFERENCES `PostCategory` (`post_category_id`)
);

--Comment table
CREATE TABLE `Comment` (
	`comment_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`post_id`	BIGINT	NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`content`	TEXT	NOT NULL,
	CONSTRAINT `PK_COMMENT` PRIMARY KEY (`comment_id`, `post_id`, `member_id`),
	CONSTRAINT `FK_Post_TO_Comment_1` FOREIGN KEY (`post_id`) REFERENCES `Post` (`post_id`),
    CONSTRAINT `FK_Member_TO_Comment_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`)
);

--Sympathy table
CREATE TABLE `Sympathy` (
	`sympathy_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`post_id`	BIGINT	NOT NULL,
	`type`	VARCHAR(100)	NOT NULL,
	PRIMARY KEY (`sympathy_id`,	`member_id`, `post_id`),
	CONSTRAINT `FK_Member_TO_Sympathy_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`),
	CONSTRAINT `FK_Post_TO_Sympathy_1` FOREIGN KEY (`post_id`) REFERENCES `Post` (`post_id`)
);

--PostImage table
CREATE TABLE `PostImage` (
	`post_image_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`post_id`	BIGINT	NOT NULL,
	`image`	VARCHAR(100)	NULL,
	PRIMARY KEY (`post_image_id`, `post_id`),
	CONSTRAINT `FK_Post_TO_Postmage_1` FOREIGN KEY (`post_id`) REFERENCES `Post` (`post_id`)
);

 

main/resources/db/migration/mariaDB/V1__init_table.sql

drop table if exists Member;
drop table if exists Notification;
drop table if exists FriendInfo;
drop table if exists FriendState;
drop table if exists Pet;
drop table if exists CareGiver;
drop table if exists Schedule;
drop table if exists PetSchedule;
drop table if exists ScheduleCategory;
drop table if exists CustomRepeatPattern;
drop table if exists Post;
drop table if exists Comment;
drop table if exists Sympathy;
drop table if exists PostImage;
drop table if exists PostCategory;

--Member table
CREATE TABLE Member (
	`member_id`	BIGINT	AUTO_INCREMENT NOT NULL PRIMARY KEY,
	`name`	VARCHAR(10)	NOT NULL,
	`email`	VARCHAR(50)	NOT NULL,
	`password`	VARCHAR(100) NULL,
	`phone`	VARCHAR(15)	NOT NULL,
	`image`	VARCHAR(100) NULL,
	`status`    ENUM	NOT NULL,
	`created_at`	TIMESTAMP	NOT NULL,
	`updated_at`	TIMESTAMP	NOT NULL
);

--Notification table
CREATE TABLE `Notification` (
	`notice_id`	BIGINT AUTO_INCREMENT NOT NULL,
	`member_id` BIGINT NOT NULL,
	`type` ENUM NOT NULL,
	`entity_id`	BIGINT NOT NULL,
	`is_read`BOOLEAN NOT NULL,
	`created_at` TIMESTAMP	NOT NULL,
	`updated_at` TIMESTAMP	NOT NULL,
	PRIMARY KEY (`notice_id`, `member_id`),
	CONSTRAINT `FK_Member_TO_Notification_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`)
);

--FriendState table
CREATE TABLE `FriendState` (
	`friend_status_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`status`	VARCHAR(50)	NOT NULL,
	PRIMARY KEY (`friend_status_id`)
);

--FriendInfo table
CREATE TABLE `FriendInfo` (
	`friend_info_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`friend_status_id`	BIGINT	NOT NULL,
	`friend_id`	BIGINT	NOT NULL,
	`created_at`	TIMESTAMP	NOT NULL,
	`updated_at`	TIMESTAMP	NOT NULL,
	PRIMARY KEY (`friend_info_id`, `member_id`, `friend_status_id`),
	CONSTRAINT `FK_Member_TO_FriendInfo_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`),
	CONSTRAINT `FK_FriendState_TO_FriendInfo_1` FOREIGN KEY (`friend_status_id`) REFERENCES `FriendState` (`friend_status_id`)
);

--Pet table
CREATE TABLE `Pet` (
	`pet_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`pet_name`	VARCHAR(30)	NOT NULL,
	`species`	ENUM	NOT NULL,
	`breed`	ENUM	NOT NULL,
	`pet_gender`	ENUM	NOT NULL,
	`pet_age`	TINYINT	NOT NULL,
	`pet_image`	VARCHAR(100)	NULL,
	`memo`	TEXT	NULL,
	`created_at`	TIMESTAMP	NOT NULL,
	`updated_at`	TIMESTAMP	NOT NULL,
	`status`	ENUM	NOT NULL,
	PRIMARY KEY (`pet_id`, `member_id`),
	UNIQUE (`pet_id`),
	CONSTRAINT `FK_Member_TO_Pet_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`)
);

--CareGiver table
CREATE TABLE `CareGiver` (
	`care_giver_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`pet_id`	BIGINT	NOT NULL,
	 PRIMARY KEY (`care_giver_id`, `member_id`, `pet_id`),
	 CONSTRAINT `FK_Member_TO_CareGiver_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`),
	 CONSTRAINT `FK_Pet_TO_CareGiver_1` FOREIGN KEY (`pet_id`) REFERENCES `Pet` (`pet_id`)
);

--ScheduleCategory table
CREATE TABLE `ScheduleCategory` (
	`category_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`category_name`	VARCHAR(50)	NOT NULL,
	PRIMARY KEY (`category_id`, `member_id`),
	CONSTRAINT `FK_Member_TO_ScheduleCategory_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`)
);

--Schedule table
CREATE TABLE `Schedule` (
	`schedule_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`category_id`	BIGINT	NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`schedule_title`	VARCHAR(20)	NOT NULL,
	`schedule_content`	TEXT	NULL,
	`schedule_at`	TIMESTAMP	NOT NULL,
	`repeat_type`	ENUM	NOT NULL,
	`repeat_cycle`	ENUM	NULL,
	`notice_yn`	BOOLEAN	NOT NULL,
	`notice_at`	BIGINT	NULL,
	`priority`	ENUM	NULL,
	`status`	ENUM	NOT NULL,
	`created_at`	TIMESTAMP	NOT NULL,
	`updated_at`	TIMESTAMP	NOT NULL,
	PRIMARY KEY (`schedule_id`, `category_id`, `member_id`),
	UNIQUE (`schedule_id`),
	CONSTRAINT `FK_ScheduleCategory_TO_Schedule_1` FOREIGN KEY (`category_id`, `member_id`) REFERENCES `ScheduleCategory` (`category_id`, `member_id`),
        CONSTRAINT `FK_Member_TO_Schedule_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`)
);

---PetSchedule table
CREATE TABLE `PetSchedule` (
    `pet_schedule_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`pet_id`	BIGINT	NOT NULL,
	`schedule_id`	BIGINT	NOT NULL,
	 PRIMARY KEY (`pet_schedule_id`, `pet_id`, `schedule_id`),
	 CONSTRAINT `FK_Pet_TO_PetSchedule_1` FOREIGN KEY (`pet_id`) REFERENCES `Pet` (`pet_id`),
     CONSTRAINT `FK_Schedule_TO_PetSchedule_1` FOREIGN KEY (`schedule_id`) REFERENCES `Schedule` (`schedule_id`)
);

--CustomRepeatPattern table
CREATE TABLE `CustomRepeatPattern` (
	`id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`frequency`	VARCHAR(100)	NOT NULL,
	`interval`	BIGINT	NULL,
	`daysOfWeek`	ENUM	NULL,
	`daysOfMonth`	VARCHAR(255)	NULL,
	`endDate`	VARCHAR(255)	NULL,
	CONSTRAINT `PK_CUSTOMREPEATPATTERN` PRIMARY KEY (`id`)
);

--PostCategory table
CREATE TABLE `PostCategory` (
	`post_category_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`category_name`	VARCHAR(50)	NOT NULL,
	PRIMARY KEY (`post_category_id`)
);

--Post table
CREATE TABLE `Post` (
	`post_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`post_category_id`	BIGINT	NOT NULL,
	`post_title`	VARCHAR(50)	NOT NULL,
	`post_content`	TEXT	NOT NULL,
	`created_at`	TIMESTAMP	NOT NULL,
	`updated_at`	TIMESTAMP	NOT NULL,
	`status`	ENUM	NOT NULL,
	PRIMARY KEY (`post_id`, `member_id`, `post_category_id`),
	UNIQUE(`post_id`),
	CONSTRAINT `FK_Member_TO_Post_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`),
	CONSTRAINT `FK_PostCategory_TO_Post_1` FOREIGN KEY (`post_category_id`) REFERENCES `PostCategory` (`post_category_id`)
);

--Comment table
CREATE TABLE `Comment` (
	`comment_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`post_id`	BIGINT	NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`content`	TEXT	NOT NULL,
	CONSTRAINT `PK_COMMENT` PRIMARY KEY (`comment_id`, `post_id`, `member_id`),
	CONSTRAINT `FK_Post_TO_Comment_1` FOREIGN KEY (`post_id`) REFERENCES `Post` (`post_id`),
    CONSTRAINT `FK_Member_TO_Comment_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`)
);

--Sympathy table
CREATE TABLE `Sympathy` (
	`sympathy_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`post_id`	BIGINT	NOT NULL,
	`type`	ENUM	NOT NULL,
	PRIMARY KEY (`sympathy_id`,	`member_id`, `post_id`),
	CONSTRAINT `FK_Member_TO_Sympathy_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`),
	CONSTRAINT `FK_Post_TO_Sympathy_1` FOREIGN KEY (`post_id`) REFERENCES `Post` (`post_id`)
);

--PostImage table
CREATE TABLE `PostImage` (
	`post_image_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`post_id`	BIGINT	NOT NULL,
	`image`	VARCHAR(100)	NULL,
	PRIMARY KEY (`post_image_id`, `post_id`),
	CONSTRAINT `FK_Post_TO_Postmage_1` FOREIGN KEY (`post_id`) REFERENCES `Post` (`post_id`)
);

 

이렇게 하고 아래의 트러블 슈팅을 거쳐서 ! 다음과 같이 flyway가 만들어주는 테이블이 생성되었다.

flyway_schema_history를 확인해보면 V1__init_table.sql 스크립트가 잘 실행된 것을 확인할 수 있다.

이제 앞으로 테이블에 변경사항이 생기면 버전을 현재 최신버전보다 큰 숫자로 새 스크립트를 만들어주면 된다 !

 


Trouble Shooting

1. Flyway가 테이블을 만들지 못 해 아래와 같은 오류 발생

Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [member]
	at org.hibernate.tool.schema.internal.AbstractSchemaValidator.validateTable(AbstractSchemaValidator.java:135) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.tool.schema.internal.GroupedSchemaValidatorImpl.validateTables(GroupedSchemaValidatorImpl.java:46) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.tool.schema.internal.AbstractSchemaValidator.performValidation(AbstractSchemaValidator.java:98) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.tool.schema.internal.AbstractSchemaValidator.doValidation(AbstractSchemaValidator.java:76) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:289) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.lambda$process$5(SchemaManagementToolCoordinator.java:144) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at java.base/java.util.HashMap.forEach(HashMap.java:1421) ~[na:na]
	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:141) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.boot.internal.SessionFactoryObserverForSchemaExport.sessionFactoryCreated(SessionFactoryObserverForSchemaExport.java:37) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:322) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:457) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1506) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) ~[spring-orm-6.1.11.jar:6.1.11]
	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) ~[spring-orm-6.1.11.jar:6.1.11]
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.1.11.jar:6.1.11]
	... 119 common frames omitted

 

해결 resources/db/migratrion/ 의 파일 명이 잘못됐었다....

 

V1_init_table.sql로 했었는데

V1__init_table.sql로 버전 다음에 밑줄이 두 개여야 한다 ..........................

 


2. 당연하겠지만 외래키로 사용되는 필드가 속한 테이블의 CREATE 문이 먼저 실행되어야 한다.

스켸줄에서 스켸줄 카테고리를 외래키로 사용하고 있는데 스켸줄 카테고리 테이블의 생성문이 스켸줄 테이블 생성문보다 아래에 있어서 발생한 오류

Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "SCHEDULECATEGORY" not found; SQL statement:
--Schedule table
CREATE TABLE `Schedule` (
	`schedule_id`	BIGINT	NOT NULL,
	`category_id`	BIGINT	NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`schedule_title`	VARCHAR(20)	NOT NULL,
	`schedule_content`	TEXT	NULL,
	`schedule_at`	TIMESTAMP	NOT NULL,
	`repeat_type`	VARCHAR(100)	NOT NULL,
	`repeat_cycle`	VARCHAR(100)	NULL,
	`notice_yn`	BOOLEAN	NOT NULL,
	`notice_at`	BIGINT	NULL,
	`priority`	VARCHAR(100)	NULL,
	`status`	VARCHAR(100)	NOT NULL,
	`created_at`	TIMESTAMP	NOT NULL,
	`updated_at`	TIMESTAMP	NOT NULL,
	PRIMARY KEY (`schedule_id`, `category_id`, `member_id`),
	CONSTRAINT `FK_ScheduleCategory_TO_Schedule_1` FOREIGN KEY (`category_id`) REFERENCES `ScheduleCategory` (`category_id`),
    CONSTRAINT `FK_Member_TO_Schedule_1` FOREIGN KEY (`member_id`) REFERENCES `Member [42102-224]

 

 


3. h2 database가 SQL 쿼리를 실행하는 동안 해당 속성에 대해 기본 키나 유니크 제약 조건을 찾지 못 해 발생하는 오류다.

보통 외래 키 참조 시 참조 대상이 되는 테이블의 컬럼에 기본 키 또는 유니크 제약 조건이 걸려있지 않을 때 나타난다고 한다.

 

에러 메세지는 아래와 같다.

SQL State  : 90057
Error Code : 90057
Message    : Constraint "PRIMARY KEY | UNIQUE (SCHEDULE_ID)" not found; SQL statement:
---PetSchedule table
CREATE TABLE `PetSchedule` (
    `pet_schedule_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`pet_id`	BIGINT	NOT NULL,
	`schedule_id`	BIGINT	NOT NULL,
	 PRIMARY KEY (`pet_schedule_id`, `pet_id`, `schedule_id`),
	 CONSTRAINT `FK_Pet_TO_PetSchedule_1` FOREIGN KEY (`pet_id`) REFERENCES `Pet` (`pet_id`),
     CONSTRAINT `FK_Schedule_TO_PetSchedule_1` FOREIGN KEY (`schedule_id`) REFERENCES `Schedu [90057-224]
Location   : db/migration/h2/V1__init_table.sql (C:\spring_prac\Petory\out\production\resources\db\migration\h2\V1__init_table.sql)
Line       : 123
Statement  : ---PetSchedule table
CREATE TABLE `PetSchedule` (
    `pet_schedule_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`pet_id`	BIGINT	NOT NULL,
	`schedule_id`	BIGINT	NOT NULL,
	 PRIMARY KEY (`pet_schedule_id`, `pet_id`, `schedule_id`),
	 CONSTRAINT `FK_Pet_TO_PetSchedule_1` FOREIGN KEY (`pet_id`) REFERENCES `Pet` (`pet_id`),
     CONSTRAINT `FK_Schedule_TO_PetSchedule_1` FOREIGN KEY (`schedule_id`) REFERENCES `Schedule` (`schedule_id`)
)

보면 petSchedule테이블은 schedule테이블의 기본키인 schedule_id를 외래키로 갖고 있는데

 

schedule테이블은 아래와 같이 schedule_id와 scheduleCategory테이블의 category_id, member_id를 함께 복합 기본키 가진다.

--Schedule table
CREATE TABLE `Schedule` (
	`schedule_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`category_id`	BIGINT	NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`schedule_title`	VARCHAR(20)	NOT NULL,
	`schedule_content`	TEXT	NULL,
	`schedule_at`	TIMESTAMP	NOT NULL,
	`repeat_type`	VARCHAR(100)	NOT NULL,
	`repeat_cycle`	VARCHAR(100)	NULL,
	`notice_yn`	BOOLEAN	NOT NULL,
	`notice_at`	BIGINT	NULL,
	`priority`	VARCHAR(100)	NULL,
	`status`	VARCHAR(100)	NOT NULL,
	`created_at`	TIMESTAMP	NOT NULL,
	`updated_at`	TIMESTAMP	NOT NULL,
	PRIMARY KEY (`schedule_id`, `category_id`, `member_id`),
	CONSTRAINT `FK_ScheduleCategory_TO_Schedule_1` FOREIGN KEY (`category_id`, `member_id`) REFERENCES `ScheduleCategory` (`category_id`, `member_id`),
        CONSTRAINT `FK_Member_TO_Schedule_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`)
);

 

 

 

외래키가 참조하는 컬럼은 반드시 해당 테이블에서 기본키, 또는 유니크 키 제약 조건이 설정되어있어야 한다.

 

== PetSchedule테이블이 참조하는 Schedule테이블의 schedule_id가 기본키이거나 유니크 키여야 한다는 말이다.

 

해결 방법

1. schedule_id 컬럼에 유니크 제약 조건 추가하기 UNIQUE(`schedule_id`)

--Schedule table
CREATE TABLE `Schedule` (
	`schedule_id`	BIGINT	AUTO_INCREMENT NOT NULL,
	`category_id`	BIGINT	NOT NULL,
	`member_id`	BIGINT	NOT NULL,
	`schedule_title`	VARCHAR(20)	NOT NULL,
	`schedule_content`	TEXT	NULL,
	`schedule_at`	TIMESTAMP	NOT NULL,
	`repeat_type`	VARCHAR(100)	NOT NULL,
	`repeat_cycle`	VARCHAR(100)	NULL,
	`notice_yn`	BOOLEAN	NOT NULL,
	`notice_at`	BIGINT	NULL,
	`priority`	VARCHAR(100)	NULL,
	`status`	VARCHAR(100)	NOT NULL,
	`created_at`	TIMESTAMP	NOT NULL,
	`updated_at`	TIMESTAMP	NOT NULL,
	PRIMARY KEY (`schedule_id`, `category_id`, `member_id`),
	UNIQUE (`schedule_id`),
	CONSTRAINT `FK_ScheduleCategory_TO_Schedule_1` FOREIGN KEY (`category_id`, `member_id`) REFERENCES `ScheduleCategory` (`category_id`, `member_id`),
        CONSTRAINT `FK_Member_TO_Schedule_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`)
);

 

 

2. Schedule 테이블에서 복합 키 대신 단일 기본 키로 사용하기

CREATE TABLE `Schedule` (
    `schedule_id`    BIGINT    NOT NULL PRIMARY KEY,  -- schedule_id를 단일 기본 키로 설정
    `category_id`    BIGINT    NOT NULL,
    `member_id`    BIGINT    NOT NULL,
    -- 기타 컬럼 정의들...
    CONSTRAINT `FK_ScheduleCategory_TO_Schedule_1` FOREIGN KEY (`category_id`, `member_id`) REFERENCES `ScheduleCategory` (`category_id`, `member_id`),
    CONSTRAINT `FK_Member_TO_Schedule_1` FOREIGN KEY (`member_id`) REFERENCES `Member` (`member_id`)
);

-> 이렇게 할 경우 member_id와 category_id를 함께 기본키로 설정해줄 수 없어 index를 따로 지정해줘야 한다.

 

 

3. PetSchedule 테이블에서 외래키를 복합키로 변경

-- 복합 키를 참조하도록 PetSchedule 테이블 수정
CREATE TABLE `PetSchedule` (
    `pet_schedule_id` BIGINT AUTO_INCREMENT NOT NULL,
    `pet_id` BIGINT NOT NULL,
    `member_id` BIGINT NOT NULL,  -- Pet 테이블의 복합 키 참조를 위해 추가
    `schedule_id` BIGINT NOT NULL,
    `category_id` BIGINT NOT NULL,  -- Schedule 테이블의 복합 키 참조를 위해 추가
    PRIMARY KEY (`pet_schedule_id`, `pet_id`, `member_id`, `schedule_id`, `category_id`),
    CONSTRAINT `FK_Pet_TO_PetSchedule_1` FOREIGN KEY (`pet_id`, `member_id`) REFERENCES `Pet` (`pet_id`, `member_id`),
    CONSTRAINT `FK_Schedule_TO_PetSchedule_1` FOREIGN KEY (`schedule_id`, `category_id`, `member_id`) REFERENCES `Schedule` (`schedule_id`, `category_id`, `member_id`)
);

-> 이렇게 할 경우 불필요한 category_id와 member_id도 컬럼으로 추가해야해서 기각 .

 

1번을 선택해서 schedule_id에 UNIQUE 제약조건을 걸어주어 해결했다.

* 유니크 제약 조건이란?)
데이터 베이스 테이블에서 특정 열(컬럼)에 저장된 값들이 서로 중복되지 않도록 보장하는 제약조건.
 유니크 제약 조건을 설정한 컬럼은 각 행에서 그 컬럼에 저장된 값이 유일해야 하며, 동일한 값이 두 개 이상의 행에 나타날 수 없다.
-> 이를 통해 데이터부결성을 유지하고, 중복 데이터를 방지할 수 있따. 

 


4. 배포서버 실행 시 오류 발생 !

SQL State  : 42000
Error Code : 1064
Message    : (conn=59723) You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '--Member table
CREATE TABLE Member (
	member_id	BIGINT NOT NULL AUTO_INCR...' at line 1
Location   : db/migration/mariaDB/V1__init_table.sql (C:\spring_prac\Petory\out\production\resources\db\migration\mariaDB\V1__init_table.sql)
Line       : 18
Statement  : --Member table
CREATE TABLE Member (
	member_id	BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
	name	VARCHAR(10)	NOT NULL,
	email	VARCHAR(50)	NOT NULL,
	password	VARCHAR(100) NULL,
	phone	VARCHAR(15)	NOT NULL,
	image	VARCHAR(100) NULL,
	status    ENUM	NOT NULL,
	created_at	TIMESTAMP	NOT NULL,
	updated_at	TIMESTAMP	NOT NULL,
)

	at org.flywaydb.core.internal.sqlscript.DefaultSqlScriptExecutor.handleException(DefaultSqlScriptExecutor.java:252) ~[flyway-core-10.10.0.jar:na]
	at org.flywaydb.core.internal.sqlscript.DefaultSqlScriptExecutor.executeStatement(DefaultSqlScriptExecutor.java:214) ~[flyway-core-10.10.0.jar:na]
	at org.flywaydb.core.internal.sqlscript.DefaultSqlScriptExecutor.execute(DefaultSqlScriptExecutor.java:133) ~[flyway-core-10.10.0.jar:na]
	at org.flywaydb.core.internal.resolver.sql.SqlMigrationExecutor.executeOnce(SqlMigrationExecutor.java:65) ~[flyway-core-10.10.0.jar:na]
	at org.flywaydb.core.internal.resolver.sql.SqlMigrationExecutor.lambda$execute$0(SqlMigrationExecutor.java:57) ~[flyway-core-10.10.0.jar:na]
	at org.flywaydb.core.internal.database.DefaultExecutionStrategy.execute(DefaultExecutionStrategy.java:27) ~[flyway-core-10.10.0.jar:na]
	at org.flywaydb.core.internal.resolver.sql.SqlMigrationExecutor.execute(SqlMigrationExecutor.java:56) ~[flyway-core-10.10.0.jar:na]
	at org.flywaydb.core.internal.command.DbMigrate.doMigrateGroup(DbMigrate.java:374) ~[flyway-core-10.10.0.jar:na]
	... 133 common frames omitted
Caused by: java.sql.SQLSyntaxErrorException: (conn=59723) You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '--Member table
CREATE TABLE Member (
	member_id	BIGINT NOT NULL AUTO_INCR...' at line 1

 

증말 ,,, 맨처음 주석에 -- 쓰고 한 칸 띄운 뒤 문구를 적어야 한다 ㅡㅡ

 

그리고 ENUM 타입의 경우 사용할 수 있는 값들을 ENUM('ACTIVE', 'INACTIVE')와 같이 적어주어야 한다 ! 아님 오류남


5. 아직 아무 테이블이 없는 상태에서

ddl-auto: validate로 설정 시 Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [테이블명] 오류

 

운영 DB에서는 validate나 none을 사용해야 한다길래 아직 테이블 생성하기 전임에도 validate로 해놨었는데 아래와 같은 오류가 났다.

Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [member]
	at org.hibernate.tool.schema.internal.AbstractSchemaValidator.validateTable(AbstractSchemaValidator.java:135) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.tool.schema.internal.GroupedSchemaValidatorImpl.validateTables(GroupedSchemaValidatorImpl.java:46) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.tool.schema.internal.AbstractSchemaValidator.performValidation(AbstractSchemaValidator.java:98) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.tool.schema.internal.AbstractSchemaValidator.doValidation(AbstractSchemaValidator.java:76) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:289) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.lambda$process$5(SchemaManagementToolCoordinator.java:144) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at java.base/java.util.HashMap.forEach(HashMap.java:1421) ~[na:na]
	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:141) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.boot.internal.SessionFactoryObserverForSchemaExport.sessionFactoryCreated(SessionFactoryObserverForSchemaExport.java:37) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:322) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:457) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1506) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
	at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) ~[spring-orm-6.1.11.jar:6.1.11]
	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) ~[spring-orm-6.1.11.jar:6.1.11]
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.1.11.jar:6.1.11]
	... 119 common frames omitted

내가 아는 validate 속성의 특징은 엔티티 클래스와 테이블이 정상적으로 매핑 됐는지만 확인하는 것이었다.

근데 오류가 나길래 한 번 찾아봤는데 기존 알고 있었던 것 외에도 특징이 더 있었다 !

validate는 다른 속성들과 다르게 DDL을 작성하여 테이블을 생성하거나 수정하지 않고, 엔터티 클래스와 테이블이 정상적으로 매핑되는지만 검사한다. 만약 테이블이 아예 존재하지 않거나, 테이블에 엔터티의 필드에 매핑되는 컬럼이 존재하지 않으면 예외를 발생시키면서 애플리케이션을 종료한다.

 

나는 아예 flyway 초기 실행 스크립트도 되지 않았기 때문에 테이블이 없었다 ! 그래서 오류가 발생한 것.

처음에는 ddl-auto 옵션을 none으로 해놓고 테이블 생성되면 validate로 수정하면 된다 !

 

테이블 생성돼있음에도 같은 오류가 발생한다. 아래 오류랑 같은 원인이기 때문에 해결 방법은 6번 해결을 아래를 참고하면 된다.


6. 테이블 찾지 못 하는 오류 !

발생 상황:  ddl-auto:none으로 해놓고 엔티티에 @Table(name = "Member") 해주고 회원가입 할 때 발생

 

2024-08-27T00:13:57.381+09:00  WARN 34728 --- [nio-8080-exec-1] o.m.jdbc.message.server.ErrorPacket      : Error: 1146-42S02: Table 'petory.member' doesn't exist
2024-08-27T00:13:57.383+09:00  WARN 34728 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1146, SQLState: 42S02
2024-08-27T00:13:57.385+09:00 ERROR 34728 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper   : (conn=60342) Table 'petory.member' doesn't exist
2024-08-27T00:13:57.407+09:00 ERROR 34728 --- [nio-8080-exec-1] c.s.P.exception.GlobalExceptionHandler   : org.springframework.dao.InvalidDataAccessResourceUsageException : JDBC exception executing SQL [select m1_0.member_id from member m1_0 where m1_0.email=? limit ?] [(conn=60342) Table 'petory.member' doesn't exist] [n/a]; SQL [n/a]

 

flyway가 만들어주는 테이블 이름들은 이렇다.

그런데 jpa에서 조회하거나 할 때 member 소문자로 테이블을 찾게 되는데 매핑되는 테이블을 찾지 못 해서 발생한 오류이다.

Member Entity에 @Table(name = "Member")로 해당 엔티티를 Member클래스 엔티티와 Flyway가 만들어준 테이블 Member를 연결해줬는데도 안 됐다.

 

 

MariaDB의 경우 테이블 이름의 대소문자 구분은 lower_case_table_names 설정에 의해 영향을 받을 수 있다고 한다.

설정이 0이면 대소문자 구분, 1이면 구분하지 않음. 확인해보니까 0으로 되어있었다.

lower_case_table_names설정을 1로 바꿔주기 위해 

 

/etc/my.cnf에 다음과 같이 추가했는데도 안 됐다.

[mysqld]
lower_case_table_names=1

 

그래서  RDS 파라미터 그룹 수정에서 옵션을 1로 변경해주고 SHOW VARIABLES LIKE 'lower_case_table_names';

명령어로 잘 변경된 것을 확인했다.

 

그리고 다시 테이블을 싹 밀고 프로젝트를 재 실행시키니 모두 소문자로 테이블이 잘 생성이 되었다 !

 

그리고 member테이블에 회원 가입시 아래와 같이 잘 저장된 것을 확인할 수 있다 ~~~!!!

 

그러면 5번에서 발생한 ddl-auto를 validate로 뒀을 때의 오류도 나지 않아야한다 !

 

잘 실행 되었으나 아래와 같이 타입 오류가 났다. 

이건 아주 사소한 오류로 내가 Pet 엔티티 클래스에는 Long으로 해놓고 DB 테이블을 만들 때는 BIGINT로 해서 그렇다.

Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: wrong column type encountered in column [pet_age] in table [pet]; found [tinyint (Types#TINYINT)], but expecting [bigint (Types#BIGINT)]

 

Flyway를 잘 적용해보기 위해 V2 스크립트를 아래와 같이 만들었다.

 

스크립트 실행 전  <>  후 

tinyint가 bigint로 잘 바뀌었다 !

 

 

이번엔 이 오류가 났는데 위에서 발생한 것과 똑같은 missing table오류이긴 하지만 이번엔 다르게 해결해줘야 한다.

Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [post_category]

 

flyway가 만든 테이블명은 postcategory인데 jpa는 post_category 테이블을 찾으려고 해서 발생한 오류이다.

 

PostCategory 엔티티 클래스 위에 다음과 같이 @Table(name = "postcategory")를 붙여주면 된다.

이 의미는 PostCategory 엔티티 클래스를 postcategory로 인식해라 ~ 이런 의미이다.

 

그리고 정상동작 ~

 

이 TS로 또 깨달은 점이 ddl-auto:validate 옵션은 실제 만들어져있는 테이블과 내 엔티티가 매핑되는 지를 확인할 수 있어서 정말 좋은 옵션 같다 !!! 

 

해결 정리 :

 

1. hibernate와 flyway 매핑 오류로 lower_case_table_names 옵션을 RDS 파라미터 그룹에서 1로 수정해주고 테이블을 새로 생성했다. 모두 소문자 이름으로 생성이 되어 jpa가 테이블에 접근할 때 잘 매핑이 된다.

 

2. PostCategory 엔티티 처럼 카멜 케이스로 이루어진 테이블의 경우 flyway는 postcategory로 만들기 때문에 jpa가 찾지 못 한다. PostCategory 엔티티 클래스 위에 @Table(name = "postcategory")를 붙여주어 해당 엔티티를 실제 테이블인 postcategory와 매핑 시켜줘야 한다.


 

+ 이미 jpa가 생성한 테이블이 있고 flyway_schema_history에 스크립트 정상 실행이 찍혔는데 스크립트에 작성된 나머지 테이블들은 안 만들어 질 때 

 

기존 jpa가 생성한 테이블들을 모두 제거해주니까 영향을 받는다 ,,,,

 

 


참고

 

마이그레이션 파일 작성 고

https://shawn-dev.oopy.io/322dabb3-f411-4f00-b02e-1157d9922fd9#fef71651-a056-410c-8c91-7260e72995c9

 

[springboot] flyway 로 DB 마이그레이션 하기

flyway 란?

shawn-dev.oopy.io

 

flyway yml 설정 참고

https://ecsimsw.tistory.com/entry/Flyway%EB%A1%9C-DB-Migration

 

우리 팀에서 Flyway를 사용하는 이유

Flyway 소개하기https://www.youtube.com/watch?v=pxDlj5jA9z4  DB MigrationDB Migration의 필요를 모를 수 있다. 솔직히 나는 몰랐다. 배포 후 데이터를 관리해본 경험이 없었고, 유지 보수 중 스키마 구조가 바뀌

www.blog.ecsimsw.com

 

yml 옵션 들 의미 참고

https://mindybughunter.com/spring-boot-flyway-%EC%A0%81%EC%9A%A9%EA%B8%B0/

 

Spring Boot Flyway 적용기

서론 프로젝트 적용 속도가 빨랐을 당시에는, 데이터베이스에 대한 형상 관리에 대해서 생각해보지 않았었다. 변화하는 테이블이 그리 많지 않았었고 속성이 몇 개 정도 추가되는 거라 DBeaver이

mindybughunter.com


Error Creating bean 오류 해결

defer-datasoure-initialization=false로 해결

https://velog.io/@idonymyeon/Flyway%EB%A5%BC-%EB%8F%84%EC%9E%85%ED%96%88%EB%8B%A4

 

Flyway를 도입했다

데이터베이스 형상관리 툴

velog.io

 

728x90