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

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

## 主要なクラス
![名称未設定ファイル-ページ2.drawio (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289963/e2514c3c-1b43-1e79-2b46-8ca024ac6103.png)

### [基本クラス](https://doma.readthedocs.io/ja/2.58.0/basic/)
```
Doma では、データベースのカラム型にマッピング可能なJava型を 基本クラス と呼びます。
```
エンティティクラスやドメインクラス、DAOインターフェースで使用する

### [ドメインクラス](https://doma.readthedocs.io/ja/2.58.0/domain/)
```
ドメインクラスは、カラムにマップできるユーザー定義のクラスです。ドメインクラスの使用はオプションです。
```
エンティティクラスやDAOインターフェースで使用する
#### 内部ドメインクラス
```
クラスに @Domain アノテーションを付けられる場合のみ、内部ドメインクラスとして扱われます。
```

#### 外部ドメインクラス
```
クラスに @Domain アノテーションを付けられない場合、外部ドメインクラスとして扱われます。
```

### [エンティティクラス](https://doma.readthedocs.io/ja/2.58.0/entity/)
```
エンティティクラスはデータベースのテーブルまたはクエリの結果セットに対応します。
```

### [DAOインターフェース](https://doma.readthedocs.io/ja/2.58.0/dao/)
```
DAOインターフェースは、データベースにアクセスするためのメソッドを宣言します。
```

## クエリ
### 検索
```
SELECTステートメントを使用して検索を実行するには、DAOメソッドに @Select アノテーションを付けます。
```
#### 検索条件
```
検索条件にはメソッドのパラメータを使用します。使用可能なパラメータの型は次のとおりです。
```
* 基本クラス
* ドメインクラス
* 任意の型
* 基本クラス や ドメインクラス や任意の型を要素とするjava.util.Optional
* 基本クラス や ドメインクラス や任意の型を要素とするjava.util.Iterable
* java.util.OptionalInt
* java.util.OptionalLong
* java.util.OptionalDouble

##### 基本クラスで検索してみる

テーブルの作成
```sql schema.sql
create table t_product (
    id varchar(20) primary key,
    name varchar(100),
    price integer,
    stock integer
);
```
テストデータの投入
```sql data.sql
insert into t_product (id, name, price, stock) values ('p01', '消しゴム', 100, 10);
```

内部ドメインクラス
```kotlin
// 内部ドメインクラス
@Domain(valueType = String::class)
data class ProductId(val value: String)
```
DAOインターフェース
```kotlin
@Dao
@ConfigAutowireable
interface ProductDao {
    @Select
    fun selectById(id: String): ProductEntity // 検索条件に基本クラスを使用
}
```

SQLファイル
```sql selectById.sql
select * from t_product where id = /* id */'1'
```

mainメソッド
```kotlin
@ComponentScan(basePackages = ["com.okr.shopping", "org.seasar.doma"])
@Configuration
class ShoppingApplication {

    @Bean
    fun dataSource(): DataSource {
        val dataSource = EmbeddedDatabaseBuilder()
                .addScripts("schema.sql", "data.sql")
                .setType(EmbeddedDatabaseType.H2).build()
        return dataSource
    }
}

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("[外部ドメインクラスでの検索結果]\nproductId:" + product.id + "\nname:" + product.name + "\nprice:" + product.price +"\nstock:" + product.stock)
}
```
実行結果

```
> Task :bootRun
17:50:32.080 [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'
17:50:32.173 [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
```

##### 内部ドメインクラスで検索してみる

内部ドメインクラス  
Kotlinでドメインクラスを作る場合は
```kotlin
// 内部ドメインクラス
@Domain(valueType = String::class)
data class ProductId(val value: String)
```
DAOインターフェース
```kotlin
@Dao
@ConfigAutowireable
interface ProductDao {
    @Select
    fun selectByProductId(productId: ProductId): ProductEntity // 検索条件にドメインクラスを使用
}
```

SQLファイル
```sql selectByProductId.sql
select * from t_product where id = /* productId */'1'
```

mainメソッド
```kotlin
@ComponentScan(basePackages = ["com.okr.shopping", "org.seasar.doma"])
@Configuration
class ShoppingApplication {

    @Bean
    fun dataSource(): DataSource {
        val dataSource = EmbeddedDatabaseBuilder()
            .addScripts("schema.sql", "data.sql")
            .setType(EmbeddedDatabaseType.H2).build()
        return dataSource
    }
}

fun main(args: Array<String>) {
    val context: ApplicationContext = AnnotationConfigApplicationContext(
        ShoppingApplication::class.java)
    val productDao: ProductDao = context.getBean(ProductDao::class.java)
    
    val domainProductId = ProductId("p01")
    val product2: ProductEntity = productDao.selectByProductId(domainProductId)

    System.out.println("[ドメインクラスでの検索結果]\nproductId:" + product2.id + "\nname:" + product2.name + "\nprice:" + product2.price +"\nstock:" + product2.stock)
}
```
実行結果

```
> Task :bootRun
17:50:32.080 [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'
17:50:32.173 [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
```

##### 外部ドメインクラスで検索してみる

外部ドメインクラス
```kotlin
// 外部ドメインクラス
data class ProductId(val value: String)
```

DomainConvertersクラス
```kotlin
@DomainConverters(ProductIdConverter::class)
class DomainConvertersProvider
```

ExternalDomainクラス
```kotlin
@ExternalDomain
class ProductIdConverter: DomainConverter<ProductId, String> {
    override fun fromDomainToValue(productId: ProductId): String {
        return productId.value
    }

    override fun fromValueToDomain(value: String): ProductId {
        return ProductId(value)
    }
}
```

build.gradle.kts
```kotlin
// 以下を追加
kapt {
    arguments {
        arg("doma.domain.converters", "com.okr.shopping.infla.doma.domain.DomainConvertersProvider")
    }
}
```


DAOインターフェース
```kotlin
@Dao
@ConfigAutowireable
interface ProductDao {
    @Select
    fun selectByProductId(productId: ProductId): ProductEntity // 検索条件に外部ドメインクラスを使用
}
```

SQLファイル
```sql selectByProductId.sql
select * from t_product where id = /* productId */'1'
```

mainメソッド
```kotlin
@ComponentScan(basePackages = ["com.okr.shopping", "org.seasar.doma"])
@Configuration
class ShoppingApplication {

    @Bean
    fun dataSource(): DataSource {
        val dataSource = EmbeddedDatabaseBuilder()
            .addScripts("schema.sql", "data.sql")
            .setType(EmbeddedDatabaseType.H2).build()
        return dataSource
    }
}

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("[任意の型での検索結果]\nproductId:" + product.id + "\nname:" + product.name + "\nprice:" + product.price +"\nstock:" + product.stock)
}
```

実行結果
```
> Task :bootRun
18:35:27.951 [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'
18:35:28.038 [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
```


##### 任意の型で検索してみる

任意の型
```kotlin
class Product : Serializable {
    var id: String? = null
    var name: String? = null
    var price: Int? = null
    var stock: Int? = null
}
```
DAOインターフェース
```kotlin
@Dao
@ConfigAutowireable
interface ProductDao {
    @Select
    fun selectByProduct(product: Product): ProductEntity// 検索条件に任意のクラスを使用
}
```

SQLファイル
```sql selectByProductId.sql
select * from t_product where id = /* product.id */'1'
```

mainメソッド
```kotlin
@ComponentScan(basePackages = ["com.okr.shopping", "org.seasar.doma"])
@Configuration
class ShoppingApplication {

    @Bean
    fun dataSource(): DataSource {
        val dataSource = EmbeddedDatabaseBuilder()
            .addScripts("schema.sql", "data.sql")
            .setType(EmbeddedDatabaseType.H2).build()
        return dataSource
    }
}

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

    val product: ProductEntity = productDao.selectByProduct(Product().apply {
        id = "p01"
    })

    System.out.println("[任意の型での検索結果]\nproductId:" + product.id + "\nname:" + product.name + "\nprice:" + product.price +"\nstock:" + product.stock)
}
```
実行結果

```
> Task :bootRun
16:32:50.173 [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:32:50.260 [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
```

##### IN句で検索してみる

DAOインターフェース
```kotlin
@Dao
@ConfigAutowireable
interface ProductDao {

    @Select
    fun selectByIds(ids: List<String>): List<ProductEntity> // 検索条件にListを使用
}
```

SQLファイル
```sql selectByIds.sql
select * from t_product where id in /* ids */('1', '2')
```

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

    val products: List<ProductEntity> = productDao.selectByIds(listOf("p01", "p02", "p03"))
    products.forEach { product ->
        System.out.println("[任意の型での検索結果]\nproductId:" + product.id + "\nname:" + product.name + "\nprice:" + product.price +"\nstock:" + product.stock)
    }
}
```

実行結果

```
> Task :bootRun
16:41:11.977 [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:41:12.069 [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
```