Jpa 와 객체 필드명(예약어 문제) - milzipmoza-developers/tecobrary-wiki GitHub Wiki
기존의 WishBook 과 LibraryBook 도메인의 필드에 해당하는 객체인 BookInfo 는 아래와 같은 코드이다.
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
@Embeddable
public class BookInfo {
@Column(name = "title",
nullable = false)
private String title;
@Column(name = "author",
nullable = false)
private String author;
@Column(name = "publisher",
nullable = false)
private String publisher;
@Column(name = "isbn",
unique = true,
nullable = false)
private String isbn;
@Column(name = "desc",
columnDefinition = "varchar(255) default '내용 없음'")
private String desc;
@Builder
public BookInfo(String title, String author, String publisher, String isbn, String description) {
this.title = title;
this.author = author;
this.publisher = publisher;
this.isbn = isbn;
this.description = description;
}
}
여기서 눈 여겨 보아야 할 필드는 desc 필드이다. 기존의 Javascript 진영의 ORM 에서 문제없이 사용하던 naming 이었기 때문에 그대로 가져와서 스프링에서 이용하려고 하였으나 spring.jpa.hibernate.ddl-auto: create
옵션을 주고 계속 스프링 부트 어플리케이션을 재실행 하여도 다음과 같은 똑같은 오류가 계속해서 발생하였다.
01:03:41.409 [DEBUG] [main] [org.hibernate.SQL] -
create table LibraryBooks (
id bigint not null auto_increment,
image varchar(255),
author varchar(255) not null,
desc varchar(255) default '내용 없음',
isbn varchar(255) not null,
publisher varchar(255) not null,
title varchar(255) not null,
primary key (id)
) engine=InnoDB
Hibernate:
create table LibraryBooks (
id bigint not null auto_increment,
image varchar(255),
author varchar(255) not null,
desc varchar(255) default '내용 없음',
isbn varchar(255) not null,
publisher varchar(255) not null,
title varchar(255) not null,
primary key (id)
) engine=InnoDB
01:03:41.436 [WARN ] [main] [o.h.t.s.i.ExceptionHandlerLoggedImpl] - GenerationTarget encountered exception accepting command : Error executing DDL "
create table LibraryBooks (
id bigint not null auto_increment,
image varchar(255),
author varchar(255) not null,
desc varchar(255) default '내용 없음',
isbn varchar(255) not null,
publisher varchar(255) not null,
title varchar(255) not null,
primary key (id)
) engine=InnoDB" via JDBC Statement
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "
create table LibraryBooks (
id bigint not null auto_increment,
image varchar(255),
author varchar(255) not null,
desc varchar(255) default '내용 없음',
isbn varchar(255) not null,
publisher varchar(255) not null,
title varchar(255) not null,
primary key (id)
) engine=InnoDB" via JDBC Statement
at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:67)
at org.hibernate.tool.schema.internal.SchemaCreatorImpl.applySqlString(SchemaCreatorImpl.java:440)
at org.hibernate.tool.schema.internal.SchemaCreatorImpl.applySqlStrings(SchemaCreatorImpl.java:424)
at org.hibernate.tool.schema.internal.SchemaCreatorImpl.createFromMetadata(SchemaCreatorImpl.java:315)
at org.hibernate.tool.schema.internal.SchemaCreatorImpl.performCreation(SchemaCreatorImpl.java:166)
at org.hibernate.tool.schema.internal.SchemaCreatorImpl.doCreation(SchemaCreatorImpl.java:135)
at org.hibernate.tool.schema.internal.SchemaCreatorImpl.doCreation(SchemaCreatorImpl.java:121)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:155)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:72)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:310)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:467)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:939)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:377)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1837)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1774)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1105)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:742)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:389)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:311)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1213)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1202)
at com.woowacourse.tecobrary.TecobraryApplication.main(TecobraryApplication.java:21)
Caused by: java.sql.SQLSyntaxErrorException: 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 'desc varchar(255) default '내용 없음',
isbn varchar(255) not null,
' at line 5
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:120)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:782)
at com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:666)
at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:95)
at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java)
at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:54)
... 34 common frames omitted
도대체 무엇이 문제일까 하고 로그를 살펴보았는데
Caused by: java.sql.SQLSyntaxErrorException: 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 'desc varchar(255) default '내용 없음',
isbn varchar(255) not null,
' at line 5
이 부분을 발견하였다.
desc 는 SQL 혹은 JPQL 에서 내림차순으로 정렬하는 예약어였고, Hibernate(?) 는 객체를 이용해 JPQL 로 변환해 쿼리문을 실행할 때 필드명에 따옴표를 자동으로 씌워주지 않기 때문에 desc 라는 필드를 생성할 때, 계속해서 내림차순 이라는 예약어로 인식하여 오류를 발생시킨 것 같다.
가설을 검증하기 위해 필드명을 asc 로 변경하고 스프링 부트 어플리케이션을 실행해보니 동일하다.
즉, 이 말은 Hibernate 가 객체를 기반으로 쿼리문을 만들어 실행시킬 때, 예약어로 지정된 것들을 필드명으로 지정할 경우 필드명이 아닌 예약어로 인식하기 때문에 위와 같은 오류가 날 수 있다는 것을 알려주는 사례라고 볼 수 있을 것 같다.
그래서 desc 를 description 으로 변경한다.