[Spring JPA] User Entity 생성 오류 해결

cozzin·2023년 2월 8일
0

⚠️ 글을 작성하는 당시 저는 Spring 초보임을 밝힙니다. 참고에 주의해주세요!

User Entity를 하나 만들어서 저장하려고 한다.

@Entity
data class User(
    @Id @GeneratedValue var userId: String? = null,
    @JsonProperty("device_id") val deviceId: String
)

User를 저장하고 제대로 저장되었는지 확인하는 테스트 코드를 작성했다.

@SpringBootTest
internal class UserServiceTest {

    @Autowired
    lateinit var service: UserService

    @Test
    fun saveUser() {
        val user = User(deviceId = "test_device_id")
        val savedUser = service.save(user)
        val savedUserId = savedUser.userId ?: ""

        assertThat(savedUserId).isNotBlank

        val foundUser = service.findById(savedUserId)
        assertThat(foundUser.get()).isEqualTo(savedUser)
    }
}

그런데 테스트는 에러를 일으킨다. 왜 그럴까?

Unknown integral data type for ids : java.lang.String
org.springframework.orm.jpa.JpaSystemException: Unknown integral data type for ids : java.lang.String
	at app//org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:321)
	at app//org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233)
	at app//org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:550)
	at app//org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
	at app//org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
	at app//org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152)
	at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at app//org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:163)
	at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at app//org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
	at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at app//org.springframework.data.repository.core.support.MethodInvocationValidator.invoke(MethodInvocationValidator.java:94)
	at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at app//org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:218)
	at app/jdk.proxy3/jdk.proxy3.$Proxy109.save(Unknown Source)
	at app//com.cozzin.todayquotes.service.UserService.save(UserService.kt:11)
	at app//com.cozzin.todayquotes.service.UserServiceTest.saveUser(UserServiceTest.kt:18)

Entity의 Id로 String을 넣는다고 해뒀는데, @GeneratedValue의 기본 strategy인 AUTO로 세팅해두면 숫자 형식의 데이터 타입으로 Id를 생성해준다고 추측할 수 있다. 그래서 이런식으로 변경했다.

@Id @GeneratedValue(strategy = GenerationType.UUID) var userId: String? = null,

이제 다른 에러가 나온다.

2023-02-08T22:09:49.282+09:00  WARN 75229 --- [    Test worker] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 42001, SQLState: 42001
2023-02-08T22:09:49.283+09:00 ERROR 75229 --- [    Test worker] o.h.engine.jdbc.spi.SqlExceptionHelper   : Syntax error in SQL statement "select u1_0.id,u1_0.device_id from [*]user u1_0 where u1_0.id=?"; expected "identifier"; SQL statement:
select u1_0.id,u1_0.device_id from user u1_0 where u1_0.id=? [42001-214]
2023-02-08T22:09:49.283+09:00  INFO 75229 --- [    Test worker] o.h.e.internal.DefaultLoadEventListener  : HHH000327: Error performing load command

org.hibernate.exception.SQLGrammarException: could not prepare statement
	at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:64) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final]
	at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:56) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final]
	at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:109) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final]

JPA가 SQL문을 만들어낼 때 뭔가 이상이 생기고 있다고 유추할 수 있다. SQL 문법 - expected "identifier" 오류 비슷한 문제를 가졌던 블로그를 보면 테이블 명을 바꿔서 해결했다. 나도 테이블명을 바꿔봤더니 같은 에러는 나오지 않았다. user 또한 키워드라고 유추할 수 있다. 이렇게 변경하고 실행하니 제대로 작동한다!

@Entity
@Table(name = "USER_TABLE")
data class User(

테스트를 위해 사용하고 있는 H2 database의 keyword 목록에서user를 확인할 수 있다. 그리고 SQL standard 이다. ⭐️ https://www.h2database.com/html/advanced.html#keywords 에서 확인할 수 있다 ⭐️ 앞으로도 꽤나 유용하게 참고할 수 있을 것 같다.

그런데 테이블 이름을 USER_TABLE로 부르는게 정말 좋을까? 아니면 users 같은 복수형으로 부를 수도 있지 않을까? convention을 한번 찾아보자.

테이블명은 일단 actor로 해보자. 그래서 나온 최종안. class 이름도 Actor로 바꿔야할지는 고민중이다.

@Entity(name = "actor")
data class User(
    @Id @GeneratedValue(strategy = GenerationType.UUID) var userId: String? = null,
    @JsonProperty("device_id") val deviceId: String
)
profile
Software Engineer

0개의 댓글