アスクルの ユウです。
アスクルではO/R MappingツールであるMyBatisを利用しています。
マッピングされた結果、nullが返却されるか忘れがちなので備忘録的な感じでまとめてみました。
検証環境
- Spring Boot 2.0.5.RELEASE
- Kotlin 1.3.30
- MyBatis 3.4.6
- MyBatis-Spring 1.3.2
下準備
テーブル:test_code
code | code_name |
---|---|
1 | テストコード1 |
2 | テストコード2 |
Map定義
<resultMap id="TestDto" type="test.dto.TestDto"> <result column="code" property="code"/> <result column="code_name" property="name"/> </resultMap>
DTO定義
data class TestDto( val code: String = "", val name: String = "" )
ケース1:resultMapで取得結果が0件だった場合
<select id="findTest1" resultMap="TestDto"> SELECT code, code_name FROM test_code WHERE code = '3' </select>
取得結果は0件になります。
このとき、mappingされるのはnullなのか、emptyListなのか・・・
Kotlin実装
@Mapper interface TestMapper { fun findTest1(): List<TestDto>? } fun test1() { val test1 = testMapper.findTest1() println("test1->$test1") }
結果
test1->[]
nullは返却されず、emptyListが返却されます。
nullable typeにする必要はありませんね。
ケース2:count(*)で件数取得する場合
count(*)で件数のみを取得する場合は、必ず件数が取得されます。
そのため、resultTypeをprimitiveで指定すれば良いですね。
アンダースコアをつけるとprimitiveになります。
<select id="findTest2" resultType="_int"> SELECT COUNT(*) FROM test_code WHERE code = '3' </select>
Kotlin実装
@Mapper interface TestMapper { fun findTest2(): Int } fun test2() { val test2 = testMapper.findTest2() println("test2->$test2") }
結果
test2->0
ケース3 non-null typeを使用しているが、nullが返却されるケース
<select id="findTest3" resultType="TestDto"> SELECT code, code_name FROM test_code WHERE code = '3' </select>
Kotlin実装
@Mapper interface TestMapper { // non-null fun findTest3(): TestDto } fun test3() { val test3: TestDto = testMapper.findTest3() println("test3->$test3") }
結果
test3->null
non-null typeなのにexceptionがthrowされることなくnullが返却されているようです。
non-null typeだから安全だと思っていると、思わぬところでバグを踏んでしまいそうです。
単一行のselectの場合は、必ずnullable typeにするなどの取り決めを作った方が良さそうですね・・・。
ちなみに、以下のコードはIllegalStateExceptionをthrowします。
// JAVAコード public static TestDto getNull() { return null; }
fun test4() { // 代入するタイミングでIllegalStateExceptionがthrowされる val test4: TestDto = TestJava.getNull() println("test4->$test4") }
JAVAにdecompileして差分をみてみると以下のようになっているようです。
public void test3() { TestDto test3 = this.testMapper.findTest3(); String var2 = "test3->" + test3; boolean var3 = false; System.out.println(var2); } public void test4() { TestDto var10000 = NullTest.getNull(); // nullチェック Intrinsics.checkExpressionValueIsNotNull(var10000, "TestJava.getNull()"); TestDto test4 = var10000; String var2 = "test4->" + test4; boolean var3 = false; System.out.println(var2); }
test4ではIntrinsics.checkExpressionValueIsNotNullをしているの対し、
test3ではしていないことがわかりますね。
まとめ
上記内容を踏まえておけば、non-nullなのにnullチェックをするなどの無駄な処理をする必要はなさそうですね。
それではまた!