Doma 2でTRUNCATEしたい

# What's？

Doma 2で`TRUNCATE`を実行する方法が、ドキュメントに書いていなかったので。

https://doma.readthedocs.io/en/2.47.0/search/?q=truncate

# TRUNCATEを使う

`@Script`アノテーションを使うのが良さそうです。

https://doma.readthedocs.io/en/2.47.0/query/script/

以下のように`@Script`アノテーションを付与して、`@Sql`アノテーションで`TRUNCATE`文を書くか

```java
    @Sql("truncate table sample")
    @Script
    void truncate();
```

`@Script`アノテーションだけ付与して

```java
    @Script
    void truncate();
```

拡張子が`.script`なSQLファイルを用意するとよいでしょう。

```sql:src/main/resources/META-INF/[DaoインターフェースのFQDN]/truncate.script
truncate table sample
```

また、`@Delete`アノテーションを使った場合でも`@Sql`アノテーションで`TRUNCATE`文を指定するか

```java
    @Sql("truncate table sample")
    @Delete
    int truncateAsDelete();
```

`@Delete`アノテーションに`sqlFile = true`として

```java
    @Delete(sqlFile = true)
    int truncateAsDelete();
```

`TRUNCATE`文を書いたSQLファイルを用意しても実行はできますが

```sql:src/main/resources/META-INF/[DaoインターフェースのFQDN]/truncateAsDelete.sql
truncate table sample
```

メソッドの戻り値が`int`であることを求められるものの、`TRUNCATE`文の実行結果としては`0`件となるため、`@Script`アノテーションを使用する方が良さそうな気がします。

少し確認してみましょう。

# 環境

確認環境。

```shell
$ java --version
openjdk 11.0.11 2021-04-20
OpenJDK Runtime Environment (build 11.0.11+9-Ubuntu-0ubuntu2.20.04)
OpenJDK 64-Bit Server VM (build 11.0.11+9-Ubuntu-0ubuntu2.20.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 11.0.11, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-74-generic", arch: "amd64", family: "unix"
```

データベースは、MySQL 8.0.25を使います。IPアドレスは、`172.17.0.2`とします。

# Maven依存関係

プロジェクトは、Mavenで作成。`pom.xml`の依存関係は、このようにしました。

```xml
    <dependencies>
        <dependency>
            <groupId>org.seasar.doma</groupId>
            <artifactId>doma-core</artifactId>
            <version>2.47.0</version>
        </dependency>

        <dependency>
            <groupId>org.seasar.doma</groupId>
            <artifactId>doma-processor</artifactId>
            <version>2.47.0</version>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
```

# テーブルとEntity

テーブル定義は、このようにしました。

```sql
create table sample(
  id int,
  word varchar(20),
  primary key(id)
);
```

対応する`Entity`クラス。

```java:src/main/java/com/example/doma/Sample.java
package com.example.doma;

import org.seasar.doma.Entity;
import org.seasar.doma.Id;

@Entity
public class Sample {
    @Id
    private Integer id;
    private String word;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getWord() {
        return word;
    }

    public void setWord(String word) {
        this.word = word;
    }
}
```

# Daoの定義

`@Dao`の定義は、このようにしました。

```java:src/main/java/com/example/doma/SampleDao.java
package com.example.doma;

import java.util.List;

import org.seasar.doma.Dao;
import org.seasar.doma.Delete;
import org.seasar.doma.Insert;
import org.seasar.doma.Script;
import org.seasar.doma.Select;
import org.seasar.doma.Sql;

@Dao
public interface SampleDao {
    @Insert
    int insert(Sample sample);

    @Sql("select /*%expand*/* from sample order by id asc")
    @Select
    List<Sample> selectAll();

    @Sql("truncate table sample")  // SQLファイルを使う場合は、こちらをコメントアウト
    @Script
    void truncate();

    @Sql("truncate table sample")  // SQLファイルを使う場合は、こちらをコメントアウト
    @Delete
    // @Delete(sqlFile = true) // SQLファイルを使う場合は、こちらを指定
    int truncateAsDelete();
}
```

`TRUNCATE`文は、`@Script`アノテーションおよび`@Delete`アノテーションでの定義の両方を用意しました。

SQLファイルを使う場合は、`@Sql`アノテーションをコメントアウトします。`@Delete`アノテーションの方は、`sqlFile = true`が必要になりますが。

SQLファイルを用意する場合は、こちら。

`@Script`アノテーションで`TRUNCATE`文を実現する場合。

```sql:src/main/resources/META-INF/com/example/doma/SampleDao/truncate.script
truncate table sample
```

`@Delete`アノテーションで`TRUNCATE`文を実現する場合。

```sql:src/main/resources/META-INF/com/example/doma/SampleDao/truncateAsDelete.sql
truncate table sample
```

# Configクラスと起動クラス

最後に、`Config`クラスと起動クラスを作成。

`Config`クラス。

```java:src/main/java/com/example/doma/DomaConfig.java
package com.example.doma;

import javax.sql.DataSource;

import org.seasar.doma.jdbc.Config;
import org.seasar.doma.jdbc.Naming;
import org.seasar.doma.jdbc.dialect.Dialect;
import org.seasar.doma.jdbc.dialect.MysqlDialect;
import org.seasar.doma.jdbc.tx.LocalTransactionDataSource;
import org.seasar.doma.jdbc.tx.LocalTransactionManager;
import org.seasar.doma.jdbc.tx.TransactionManager;

public class DomaConfig implements Config {
    public static DomaConfig INSTANCE = new DomaConfig();

    LocalTransactionDataSource dataSource =
            new LocalTransactionDataSource(
                    "jdbc:mysql://172.17.0.2:3306/example?characterEncoding=utf-8&characterSetResults=utf-8",
                    "charon",
                    "Passw@rd"
            );
    LocalTransactionManager transactionManager =
            new LocalTransactionManager(dataSource.getLocalTransaction(getJdbcLogger()));

    Dialect dialect = new MysqlDialect();

    @Override
    public DataSource getDataSource() {
        return dataSource;
    }

    @Override
    public Dialect getDialect() {
        return dialect;
    }

    @Override
    public TransactionManager getTransactionManager() {
        return transactionManager;
    }

    @Override
    public Naming getNaming() {
        return Naming.SNAKE_LOWER_CASE;
    }
}
```

起動クラス。

```java:src/main/java/com/example/doma/App.java
package com.example.doma;

import java.util.List;

import org.seasar.doma.jdbc.tx.TransactionManager;

public class App {
    public static void main(String... args) {
        TransactionManager tx = DomaConfig.INSTANCE.getTransactionManager();

        SampleDao dao = new SampleDaoImpl(DomaConfig.INSTANCE);

        // insert
        tx.required(() -> {
            Sample s1 = new Sample();
            s1.setId(1);
            s1.setWord("Hello");
            dao.insert(s1);

            Sample s2 = new Sample();
            s2.setId(2);
            s2.setWord("World");
            dao.insert(s2);
        });

        // select
        tx.required(() -> {
            List<Sample> list = dao.selectAll();

            System.out.printf("selected = %d%n", list.size());
            list.forEach(sample -> System.out.printf("id = %d%n", sample.getId()));
        });

        // truncate
        tx.required(() -> {
            dao.truncate();
            System.out.println("truncated.");
        });

        // select
        tx.required(() -> {
            List<Sample> list = dao.selectAll();

            System.out.printf("selected = %d%n", list.size());
            list.forEach(sample -> System.out.printf("id = %d%n", sample.getId()));
        });

        // insert
        tx.required(() -> {
            Sample s1 = new Sample();
            s1.setId(1);
            s1.setWord("Hello");
            dao.insert(s1);

            Sample s2 = new Sample();
            s2.setId(2);
            s2.setWord("World");
            dao.insert(s2);
        });

        // select
        tx.required(() -> {
            List<Sample> list = dao.selectAll();

            System.out.printf("selected = %d%n", list.size());
            list.forEach(sample -> System.out.printf("id = %d%n", sample.getId()));
        });

        // truncate
        tx.required(() -> {
            System.out.printf("truncated = %d%n", dao.truncateAsDelete());
        });

        // select
        tx.required(() -> {
            List<Sample> list = dao.selectAll();

            System.out.printf("selected = %d%n", list.size());
            list.forEach(sample -> System.out.printf("id = %d%n", sample.getId()));
        });
    }
}
```

データ登録 → データ取得＆確認 → `TRUNCATE` → データ取得＆確認を、`@Script`アノテーション、`@Delete`アノテーションをそれぞれ使った`TRUNCATE`の実現方法で確認しています。

実行結果（の抜粋）。

```shell
selected = 2
id = 1
id = 2
truncated.
selected = 0
selected = 2
id = 1
id = 2
truncated = 0
selected = 0
```

いずれの場合でも、`TRUNCATE`は成功しているようです。

ですが、`@Delete`アノテーションで`TRUNCATE`を使っても結果が`0`件になるので、`@Script`アノテーションで実現する方が素直なんでしょうね。