初めに
Mybatisを使用して、SQLのWEREE条件にNullチェックをした際に下記のようなエラーではまりました。
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'NULL' not found. Available parameters are [id, param1]
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:96)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:441)
at com.sun.proxy.$Proxy72.selectOne(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:160)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:87)
at org.apache.ibatis.binding.MapperProxy$PlainMethodInvoker.invoke(MapperProxy.java:145)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86)
at com.sun.proxy.$Proxy86.selectNullError(Unknown Source)
at jp.com.app.mapper.NullErrorMapperTest.Error_Test(NullErrorMapperTest.java:24)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'NULL' not found. Available parameters are [id, param1]
at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:212)
at org.apache.ibatis.scripting.xmltags.DynamicContext$ContextAccessor.getProperty(DynamicContext.java:120)
at org.apache.ibatis.ognl.OgnlRuntime.getProperty(OgnlRuntime.java:3356)
at org.apache.ibatis.ognl.ASTProperty.getValueBody(ASTProperty.java:121)
at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:258)
at org.apache.ibatis.ognl.ASTNotEq.getValueBody(ASTNotEq.java:51)
at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:258)
at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:586)
at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:550)
at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java:46)
at org.apache.ibatis.scripting.xmltags.ExpressionEvaluator.evaluateBoolean(ExpressionEvaluator.java:32)
at org.apache.ibatis.scripting.xmltags.IfSqlNode.apply(IfSqlNode.java:34)
at org.apache.ibatis.scripting.xmltags.MixedSqlNode.lambda$apply$0(MixedSqlNode.java:32)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:32)
at org.apache.ibatis.scripting.xmltags.TrimSqlNode.apply(TrimSqlNode.java:55)
at org.apache.ibatis.scripting.xmltags.MixedSqlNode.lambda$apply$0(MixedSqlNode.java:32)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:32)
at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:39)
at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:305)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:87)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:151)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:145)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:76)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:427)
... 72 more
結論
<if test=""></if> や <when test=""></when> などの条件は 「大文字」 ではなく 「小文字」 でかきましょう!
OKパターン(nullが小文字)
<if test="id != null ">
AND id = #{id}
</if>
NGパターン(NULLが大文字)
<if test="id != NULL ">
AND id = #{id}
</if>
実験(興味のある人だけ参考に・・・)
users_infomaion テーブルからidが1のユーザを取得するSQLの実行を想定します。
テーブルは以下を想定します。
Create文
CREATE TABLE users_infomaion (
id integer,
name VARCHAR(255)
);
以下のデータを追加します。
Insert文
INSERT INTO users_infomaion VALUES(1,'Tarou');
INSERT INTO users_infomaion VALUES(2,'Jirou');
INSERT INTO users_infomaion VALUES(3,'Saburou');
SQL結果取得用のBean(Entity)は以下です。
SQL結果取得用のBean
package jp.com.app.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SampleEntity1Response {
private Integer id;
private String name;
}
Mapper.java は以下です。それぞれ正常終了用(selectRunning)と異常終了用(selectNullError)を作成しています。
Mapper.java
package jp.com.app.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import jp.com.app.entity.SampleEntity1Response;
@Mapper
public interface NullErrorMapper {
SampleEntity1Response selectRunning(@Param("id")Integer id);
SampleEntity1Response selectNullError(@Param("id")Integer id);
}
Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="jp.com.app.mapper.NullErrorMapper">
<!-- 正常終了版 -->
<select id="selectRunning" resultType="jp.com.app.entity.SampleEntity1Response">
SELECT
id,
name
FROM
users_infomaion
<where>
<!-- ※ nullが小文字 -->
<if test="id != null ">
AND id = #{id}
</if>
</where>
</select>
<!-- 異常終了版 -->
<select id="selectNullError" resultType="jp.com.app.entity.SampleEntity1Response">
SELECT
id,
name
FROM
users_infomaion
<where>
<!-- ※ NULLが大文字 -->
<if test="id != NULL ">
AND id = #{id}
</if>
</where>
</select>
</mapper>
Junitで直接Mapperを呼び出して実行することとします。
Testクラス
package jp.com.app.mapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import jp.com.app.entity.SampleEntity1Response;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class NullErrorMapperTest {
@Autowired
NullErrorMapper mapper;
@Test
public void Runnning_Test()throws Exception{
Integer id = 1;
SampleEntity1Response res = mapper.selectRunning(id);
System.out.println("**************************************************************");
System.out.println(res);
System.out.println("**************************************************************");
}
@Test
public void Error_Test()throws Exception{
Integer id = 1;
SampleEntity1Response res = mapper.selectNullError(id);
System.out.println("**************************************************************");
System.out.println(res);
System.out.println("**************************************************************");
}
}
以下のようにResponseの値がコンソールに表示されたら成功です。
...省略
2022-08-30 15:06:29.502 INFO 25171 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
**************************************************************
SampleEntity1Response(id=1, name=Tarou)
**************************************************************
2022-08-30 15:06:29.714 INFO 25171 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
```...省略
以下のようなエラーが出たら失敗です。
実行結果(異常終了パターン)
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'NULL' not found. Available parameters are [id, param1]
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:96)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:441)
at com.sun.proxy.$Proxy72.selectOne(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:160)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:87)
at org.apache.ibatis.binding.MapperProxy$PlainMethodInvoker.invoke(MapperProxy.java:145)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86)
at com.sun.proxy.$Proxy86.selectNullError(Unknown Source)
at jp.com.app.mapper.NullErrorMapperTest.Error_Test(NullErrorMapperTest.java:26)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'NULL' not found. Available parameters are [id, param1]
at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:212)
at org.apache.ibatis.scripting.xmltags.DynamicContext$ContextAccessor.getProperty(DynamicContext.java:120)
at org.apache.ibatis.ognl.OgnlRuntime.getProperty(OgnlRuntime.java:3356)
at org.apache.ibatis.ognl.ASTProperty.getValueBody(ASTProperty.java:121)
at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:258)
at org.apache.ibatis.ognl.ASTNotEq.getValueBody(ASTNotEq.java:51)
at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:258)
at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:586)
at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:550)
at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java:46)
at org.apache.ibatis.scripting.xmltags.ExpressionEvaluator.evaluateBoolean(ExpressionEvaluator.java:32)
at org.apache.ibatis.scripting.xmltags.IfSqlNode.apply(IfSqlNode.java:34)
at org.apache.ibatis.scripting.xmltags.MixedSqlNode.lambda$apply$0(MixedSqlNode.java:32)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:32)
at org.apache.ibatis.scripting.xmltags.TrimSqlNode.apply(TrimSqlNode.java:55)
at org.apache.ibatis.scripting.xmltags.MixedSqlNode.lambda$apply$0(MixedSqlNode.java:32)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:32)
at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:39)
at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:305)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:87)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:151)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:145)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:76)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:427)
... 72 more
最後に
MybatisはResponseのBean(Entity)の変数名の型とカラムの名前が一致していないと値がとれなかったりなどがあるので名前には気をつけましょう。ちなみに、筆者は「resultMap」と「「resultType」を見間違えてはまってしまったこともあります。注意深く確認しましょう!