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 으로 변경한다.

⚠️ **GitHub.com Fallback** ⚠️