Resolved [org.springframework.jdbc.BadSqlGrammarException
: Error selecting key or setting result to parameter object. Cause: org.h2.jdbc.JdbcSQLSyntaxErrorException
: Function "LAST_INSERT_ID"
not found; SQL statement:
H2 구문에는 MySQL 의 LAST_INSERT_ID() 라는 함수가 없다.
따라서 H2 연동 후, SELECT LAST_INSERT_ID()
질의 시 JdbcSQLSyntaxErrorException / BadSqlGrammarException 를 뱉는다.
테스트 케이스 수행 시 H2 를 연동하면 가볍고 편하다. ORM 방식의 JPA 를 사용하는 경우는 자동적으로 벤더에 맞게 쿼리를 수행하기 때문에 별다른 문제없이 편하다. 하지만 SQL Mapper 방식의 MyBatis 의 경우 H2 로 가볍게 띄워서 테스트를 수행할 때, 구문 오류가 발생한다.
Mapper 내 작성된 특정 벤더 기준의 SQL을 H2에서 기본적으로 인식할 수 없기 때문에 마주하는 이슈이다.
그럼 H2 연동 시 MySQL, Oracle, MS-SQL 등 특정 벤더에서 사용하는 SQL 구문과 호환되게 하려면 어떻게?
방법 1 👉 그냥 참고로 알아둘 것, 벤더별 대체 함수 사용
SELECT LAST_INSERT_ID()
: MySQLSELECT SCOPE_IDENTITY()
: MS-SQL, H2 (둘 다 해당)CALL SCOPE_IDENTITY()
: H2<insert id="...">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT SCOPE_IDENTITY()
</selectKey>
...
</insert>
[방법 1] 은 SQL 벤더별 함수를 알아두는 의미에서 참고삼아 정리한 것이고, 실질적으론 [방법 2] 를 사용한다. (테스트 시 가볍게 인메모리 H2 돌리려는 상황에서, 방법 1과 같이 Mapper 내의 구문을 변경하는 건 번거롭고, 실제로 사용할 SQL 또한 아니므로 테스트 자체도 모순적)
방법 2 👉 결론
spring.datasource.url
에 ;MODE=MySQL
옵션을 추가한다.spring:
datasource:
url: jdbc:h2:mem:testdb;MODE=MySQL
driver-class-name: org.h2.Driver
예컨데 MyBatis Mapper 내의 쿼리가 MySQL 구문으로 작성되어 있다면 위와 같이
;MODE=MySQL
을 추가하면 된다.
MODE
: SQL 구문상 호환되기 원하는 벤더명 추가
- H2 연동 시, 특정 벤더 (MySQL, Oracle, MS-SQL, ...) 의 구문과 호환되도록 설정해주는 옵션이다.
- 가령 Mapper 내 작성된 SQL 에 ANSI SQL 이 아닌, 특정 벤더에 특화된 SQL이 포함되어 있을 것이다. 만약 테스트를 작성하고 수행하는 상황에서 가볍게 인메모리 H2 를 띠우려고 할 때, Mode 옵션을 활용하면 좋다.
- 또한 H2 콘솔 프롬프트에서 Mode 로 지정한 벤더 고유의 함수를 실행할 수 있다. (당연히 모든 함수를 지원하진 못할 것임 그렇게 무거울 필요도 없고)
방법 3 👉 selectKey 태그 대신 useGeneratedKeys 속성 사용
Ex)
<insert id="writeArticle" useGeneratedKeys="true" keyProperty="id">
...
</insert>