# Doma2を使ってみる(その4)

# Doma2を使ってみる(その4)

## 検索2
### 単一レコードの検索
#### 期待結果が1つである場合

DAO
```kotlin
@Dao
@ConfigAutowireable
interface ProductDao {
    @Select
    fun selectByProductId(productId: ProductId): ProductEntity
}
```

main関数
```kotlin
fun main(args: Array<String>) {
    val context: ApplicationContext = AnnotationConfigApplicationContext(
            ShoppingApplication::class.java)
    val productDao: ProductDao = context.getBean(ProductDao::class.java)

    val product: ProductEntity = productDao.selectByProductId(ProductId("p01"))
    System.out.println("productId:" + product.id + "\nname:" + product.name + "\nprice:" + product.price +"\nstock:" + product.stock)
}
```

実行結果
```terminal
> Task :bootRun
13:51:10.407 [main] INFO org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory -- Starting embedded database: url='jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
13:51:10.493 [main] WARN org.seasar.doma.boot.autoconfigure.DomaAutoConfiguration -- StandardDialect was selected because no explicit configuration and it is not possible to guess from 'spring.datasource.url property'
productId:p01
name:消しゴム
price:100
stock:10
```

#### 検索結果が0件の場合

DAO
```kotlin
@Dao
@ConfigAutowireable
interface ProductDao {
    @Select
    fun selectByProductId(productId: ProductId): ProductEntity
}
```

main関数
```kotlin
fun main(args: Array<String>) {
    val context: ApplicationContext = AnnotationConfigApplicationContext(
        ShoppingApplication::class.java)
    val productDao: ProductDao = context.getBean(ProductDao::class.java)

    val product: ProductEntity = productDao.selectByProductId(ProductId("p011"))
    System.out.println("productId:" + product.id + "\nname:" + product.name + "\nprice:" + product.price +"\nstock:" + product.stock)
}
```

実行結果
```terminal
> Task :bootRun FAILED
13:57:54.858 [main] INFO org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory -- Starting embedded database: url='jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
13:57:54.946 [main] WARN org.seasar.doma.boot.autoconfigure.DomaAutoConfiguration -- StandardDialect was selected because no explicit configuration and it is not possible to guess from 'spring.datasource.url property'
4月 09, 2024 1:57:55 午後 com.okr.shopping.infla.doma.dao.ProductDaoImpl selectByProductId
情報: [DOMA2220] ENTER  : CLASS=com.okr.shopping.infla.doma.dao.ProductDaoImpl, METHOD=selectByProductId
4月 09, 2024 1:57:55 午後 com.okr.shopping.infla.doma.dao.ProductDaoImpl selectByProductId
情報: [DOMA2076] SQL LOG : PATH=[META-INF/com/okr/shopping/infla/doma/dao/ProductDao/selectByProductId.sql],
select * from t_product where id = 'p011'
4月 09, 2024 1:57:55 午後 com.okr.shopping.infla.doma.dao.ProductDaoImpl selectByProductId
情報: [DOMA2221] EXIT   : CLASS=com.okr.shopping.infla.doma.dao.ProductDaoImpl, METHOD=selectByProductId
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.okr.shopping.infla.doma.entity.ProductEntity.getId()" because "product" is null
	at com.okr.shopping.ShoppingApplicationKt.main(ShoppingApplication.kt:47)

Execution failed for task ':bootRun'.
> Process 'command '/Users/okurashoichi/.sdkman/candidates/java/17.0.10-amzn/bin/java'' finished with non-zero exit value 1

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/8.5/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
BUILD FAILED in 1s
10 actionable tasks: 2 executed, 8 up-to-date
```
DAOのInterfaceは非NULLで書くことは可能だが、実装クラスはNULLを返すため、NullPointerExceptionが発生する。  
```
戻り値の型が Optional でなく、結果件数が 0 の場合、null が返されます。
```
DAOのInterfaceでは`単一レコードの検索`の場合、戻り値の型をNullableにすることが望ましい。

DAO
```kotlin
@Dao
@ConfigAutowireable
interface ProductDao {
    @Select
    fun selectByProductId(productId: ProductId): ProductEntity?//<=Nullable
}
```

[【!ってなんだ】KotlinとJava、nullとPlatformType【NullableにNotNull】](https://qiita.com/RyotaMurohoshi/items/5fcc10d04fecd7304556)


#### 単一が期待結果だったが、検索結果が複数であった場合
`NonUniqueResultException`が発生する。

DAO
```kotlin
@Dao
@ConfigAutowireable
interface ProductDao {
    @Select
    fun selectByIds(ids: List<String>): ProductEntity
}
```
実行結果
エラー
```
> Task :bootRun FAILED
15:55:59.266 [main] INFO org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory -- Starting embedded database: url='jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
15:55:59.358 [main] WARN org.seasar.doma.boot.autoconfigure.DomaAutoConfiguration -- StandardDialect was selected because no explicit configuration and it is not possible to guess from 'spring.datasource.url property'
4月 09, 2024 3:55:59 午後 com.okr.shopping.infla.doma.dao.ProductDaoImpl selectByIds
情報: [DOMA2220] ENTER  : CLASS=com.okr.shopping.infla.doma.dao.ProductDaoImpl, METHOD=selectByIds
4月 09, 2024 3:55:59 午後 com.okr.shopping.infla.doma.dao.ProductDaoImpl selectByIds
情報: [DOMA2076] SQL LOG : PATH=[META-INF/com/okr/shopping/infla/doma/dao/ProductDao/selectByIds.sql],
select * from t_product where id in ('p01', 'p02', 'p03')
4月 09, 2024 3:55:59 午後 com.okr.shopping.infla.doma.dao.ProductDaoImpl selectByIds
情報: [DOMA2222] THROW  : CLASS=com.okr.shopping.infla.doma.dao.ProductDaoImpl, METHOD=selectByIds, EXCEPTION=org.seasar.doma.jdbc.NonUniqueResultException
Exception in thread "main" org.seasar.doma.jdbc.NonUniqueResultException: [DOMA2001] The SQL result is not a single row.
PATH=[META-INF/com/okr/shopping/infla/doma/dao/ProductDao/selectByIds.sql].
SQL=[].
	at org.seasar.doma.internal.jdbc.command.AbstractSingleResultHandler.lambda$handle$0(AbstractSingleResultHandler.java:34)
	at org.seasar.doma.internal.jdbc.command.ResultSetIterator.next(ResultSetIterator.java:58)
	at org.seasar.doma.internal.jdbc.command.AbstractIterationHandler.iterate(AbstractIterationHandler.java:52)
	at org.seasar.doma.internal.jdbc.command.AbstractIterationHandler.handle(AbstractIterationHandler.java:40)
	at org.seasar.doma.internal.jdbc.command.AbstractSingleResultHandler.handle(AbstractSingleResultHandler.java:27)
	at org.seasar.doma.jdbc.command.SelectCommand.handleResultSet(SelectCommand.java:100)
	at org.seasar.doma.jdbc.command.SelectCommand.executeQuery(SelectCommand.java:92)
	at org.seasar.doma.jdbc.command.SelectCommand.execute(SelectCommand.java:52)
	at com.okr.shopping.infla.doma.dao.ProductDaoImpl.selectByIds(ProductDaoImpl.java:158)
	at com.okr.shopping.ShoppingApplicationKt.main(ShoppingApplication.kt:46)

Execution failed for task ':bootRun'.
> Process 'command '/Users/okurashoichi/.sdkman/candidates/java/17.0.10-amzn/bin/java'' finished with non-zero exit value 1

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/8.5/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
BUILD FAILED in 2s
10 actionable tasks: 7 executed, 3 up-to-date
```

### 複数レコードの検索
検索結果が複数件を期待する場合、戻り値をListにする。
```
複数のレコードを検索する場合は、メソッドの戻り値の型に java.util.List を指定します。List の要素は以下の型になります。
```
#### 実行結果が複数件の場合

DAO
```kotlin
@Dao
@ConfigAutowireable
interface ProductDao {
    @Select
    fun selectByIds(ids: List<String>): List<ProductEntity>
}
```

main関数
```kotlin
fun main(args: Array<String>) {
    val context: ApplicationContext = AnnotationConfigApplicationContext(
            ShoppingApplication::class.java)
    val productDao: ProductDao = context.getBean(ProductDao::class.java)

    val productList: List<ProductEntity> =
        productDao.selectByIds(listOf("p01", "p02", "p03"))
    productList.forEach { product ->
        System.out.println("productId:" + product.id + "\nname:" + product.name + "\nprice:" + product.price +"\nstock:" + product.stock)
    }
}
```

実行結果
```terminal
16:03:13:  'bootRun' を実行中...

> Task :checkKotlinGradlePluginConfigurationErrors
> Task :domaCopyResourcesKotlin UP-TO-DATE
> Task :domaCopyResourcesJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :kaptGenerateStubsKotlin
> Task :kaptKotlin UP-TO-DATE

> Task :compileKotlin
w: file:///Users/okurashoichi/Github/study/shopping/src/main/kotlin/com/okr/shopping/ShoppingApplication.kt:41:10 Parameter 'args' is never used

> Task :compileJava
> Task :classes
> Task :resolveMainClassName

> Task :bootRun
16:03:15.491 [main] INFO org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory -- Starting embedded database: url='jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
16:03:15.584 [main] WARN org.seasar.doma.boot.autoconfigure.DomaAutoConfiguration -- StandardDialect was selected because no explicit configuration and it is not possible to guess from 'spring.datasource.url property'
productId:p01
name:消しゴム
price:100
stock:10
productId:p02
name:ノート
price:200
stock:20
productId:p03
name:pname03
price:300
stock:30

Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to https://docs.gradle.org/8.5/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.

BUILD SUCCESSFUL in 2s
```

#### 検索結果が0件の場合
```
検索結果がない場合、空のリストが返されます。
```

main関数
```kotlin
fun main(args: Array<String>) {
    val context: ApplicationContext = AnnotationConfigApplicationContext(
        ShoppingApplication::class.java)
    val productDao: ProductDao = context.getBean(ProductDao::class.java)

    val productList: List<ProductEntity> =
        productDao.selectByIds(listOf("p011", "p012", "p013"))
    println("Listの中身の数:" + productList.size)
    productList.forEach { product ->
        println("productId:" + product.id + "\nname:" + product.name + "\nprice:" + product.price +"\nstock:" + product.stock)
    }
}
```

実行結果
```terminal
16:09:30:  'bootRun' を実行中...

> Task :checkKotlinGradlePluginConfigurationErrors
> Task :domaCopyResourcesKotlin UP-TO-DATE
> Task :domaCopyResourcesJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :kaptGenerateStubsKotlin
> Task :kaptKotlin UP-TO-DATE

> Task :compileKotlin
w: file:///Users/okurashoichi/Github/study/shopping/src/main/kotlin/com/okr/shopping/ShoppingApplication.kt:41:10 Parameter 'args' is never used

> Task :compileJava UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :resolveMainClassName

> Task :bootRun
16:09:31.640 [main] INFO org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory -- Starting embedded database: url='jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
16:09:31.733 [main] WARN org.seasar.doma.boot.autoconfigure.DomaAutoConfiguration -- StandardDialect was selected because no explicit configuration and it is not possible to guess from 'spring.datasource.url property'
Listの中身の数:0

Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to https://docs.gradle.org/8.5/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.

BUILD SUCCESSFUL in 1s
```