Kotlin-MyBatis-Spring 取得結果についての備忘録

アスクルの ユウです。
アスクルでは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チェックをするなどの無駄な処理をする必要はなさそうですね。
それではまた!

アスクルではエンジニアを募集しています!!

ASKUL Engineering BLOG

2021 © ASKUL Corporation. All rights reserved.