しおしお

IntelliJ IDEAのことなんかを書いてます

Vue.jsでC3.jsを使ってドーナツチャートを表示してみる

Vue.jsなアプリケーションでドーナツチャートを表示する必要があったので、C3.jsを試してみたよ。

ドーナツチャートを表示するコンポーネントの作成

C3.jsを使ってドーナツチャートを表示するためのコンポーネントを作ります。 とりあえず、ファイル名をDonutChart.vue として作ってみます。

ドーナツチャートは、C3のExampleを参考にして作ります。

<template>
  <div id="chart"></div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import c3 from 'c3'

@Component
export default class DonutChart extends Vue {
  @Prop()
  data!: {key: string, count: number}[]

  mounted() {
    c3.generate({
      bindto: '#chart',
      data: {
        columns: this.data.map(value => [value.key, value.count]),
        type: 'donut'

      },
    })
  }
}
</script>

<style scoped>
  @import '~c3/c3.min.css';
</style>

DonutChart.vueを使う親コンポーネント

data1からdata4までの4つのデータを渡してチャートを表示するようにしています。

<template>
  <div id="app">
    <donut-chart :data="data"/>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import DonutChart from './components/DonutChart.vue'

@Component({
  components: {
    DonutChart,
  },
})
export default class App extends Vue {
  data = [
    {key: 'data1', count: 500},
    {key: 'data2', count: 250},
    {key: 'data3', count: 150},
    {key: 'data4', count: 100},
  ]
}
</script>

表示結果

いい感じに、ドーナツチャートが表示されました。

f:id:sioiri:20191224131518p:plain

Spring BootでScalikeJDBCを使ってみた

Spring BootでScalikeJDBCを使ってデータベースにアクセスしてみたお話です。

build.gradle

データベース関連のライブラリとScalikeJDBCを足してあげます。

  implementation 'org.springframework.boot:spring-boot-starter'
  implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
  implementation 'org.scala-lang:scala-library:2.12.10'
  implementation 'org.scalikejdbc:scalikejdbc_2.12:3.4.0'
  implementation 'mysql:mysql-connector-java:8.0.18'

データベースの接続設定

Spring Bootのコネクションプールを使うので、application.propertiesに接続設定を行います

spring.datasource.url=jdbc:mysql://localhost:3306/siosio
spring.datasource.username=siosio
spring.datasource.password=siosio
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

ScalikeJDBCの設定クラス

コネクションプール(HikariCPのDataSource)をScalikeJDBCのConnectionPoolに登録してあげます。 これは、一度だけやればいいのでConfigurationクラスのPostConstructで行っています。

@Configuration
class ScalikeJdbcConfig(private val dataSource: DataSource) {
  
  @PostConstruct
  def initializeConnectionPool(): Unit = {
    ConnectionPool.singleton(new DataSourceConnectionPool(dataSource))
  }
}

データベースアクセスの実装

CommandLineRunnerでデータベースに1レコード追加するような処理にしています。 Springのアプリケーションの場合、トランザクション制御は@Transactionalで行いますが、ScalikeJDBC側の機能を使ってトランザクション制御を行うので@Transactionalはつけていません。

@SpringBootApplication
class Application {}

object Application {

  def main(args: Array[String]): Unit = {
    SpringApplication.run(classOf[Application], args: _*)
  }
}

@Component
class Cli(private val dbTest: DbTest) extends CommandLineRunner {
  override def run(args: String*): Unit = {
    dbTest.register("test")
  }
}

@Component
class DbTest {
  
  def register(name: String): Unit = {
    DB localTx { implicit session =>
      sql"insert into account(name) values ($name)".update().apply()
    }
  }
}

実行結果

登録したデータが入ってますね!

mysql> select * from account;
+----+------+
| id | name |
+----+------+
|  1 | test |
+----+------+
1 row in set (0.00 sec)

Spring MVCのHandlerInterceptorでPathVariableの値を参照する

HandlerInterceptorでPathVariableの値を参照する方法のメモ

HandlerInterceptorの実装例

PathVariableの値はrequest attributeに入っているので、そこから取り出せばよい。 名前は、HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTEを指定する。

class SampleInterceptor : HandlerInterceptor {

    override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
        val pathVariables = request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE) as Map<String, String>
        println("pathVariables = ${pathVariables}")

        return true
    }
}

実行結果

こんな感じのハンドラメソッドを作って動作を確認してみる。

@GetMapping("/{id}")
fun get(@PathVariable id: String): String = "id: ${id}"

この場合、100がPathVariableとなる感じ。

GET http://localhost:8080/api/100

標準出力にPathVariableの値が出ているのでちゃんと参照出来ている。

pathVariables = {id=100}

WebStormでVue.jsなアプリケーションをデバッグする

npm run devしたVue.jsなアプリケーションをIntelliJ IDEA(WebStorm)でデバッグする的な話を社内のIntelliJ IDEA勉強会でしたら盛り上がったのでブログにも手順を残しておく。*1

アプリケーションを起動する

npm run devで起動してあげましょう。 こんな感じのメッセージが表示されたら起動成功ですね。

 I  Your application is running here: http://localhost:8080

下のリンク先にあるように、WebStorm 2020.2 Betaから、コンソールに出力されたリンクから簡単にデバッグができるようになりましたね。 リンクを Cmd/Ctrl+Shift + クリックだけなのでだいぶ簡単になりましたね。

blog.jetbrains.com

IntelliJ(WebStorm)にデバッグ用の実行構成を追加する(以降の手順は2020.1以前のバージョンの場合)

  1. 実行構成のJavaScript Debugに構成を追加する
  2. 構成のURLには、デバッグ対象のアプリケーションのURLを設定する。(npm run devで表示されたURL) f:id:sioiri:20190907234350p:plain

デバッグをしてみる

前の手順で登録した構成を実行します。注意しないといけないのは、ここでDebugを選択すること。(JavaScript Debugな構成でも明示的にDebug実行してあげないとデバッグできないので…)

f:id:sioiri:20190907235001p:plain

あとは好きなところにブレークポイントおいてデバッグするだけですね。こんな感じに使い慣れた環境でデバッグ出来るようになります。 f:id:sioiri:20190907234853p:plain

おわり。

*1:多分Vue.jsだけじゃなってReactでも出来るはずです

古のspring-data-jpaでNativeQueryとPageableを使ってハマった件

Spring Boot 1.5系のバッチ的な処理に急ぎ手を入れる必要があって、RepositoryにNativeQueryでページングするようなメソッド追加したら謎のエラーでめっちゃハマったお話です。*1

Spring Bootバージョン

1.5系の古いやつですね。過去の遺産じゃないかぎり使うことはないですね…

  id 'org.springframework.boot' version '1.5.22.RELEASE'

Repositoryの実装

Repositoryでは、NativeQueryを使ってページングさせるようなメソッドを定義しています。

@Query(
        nativeQuery = true,
        value = "select * from users ",
        countQuery = "select count(*) from users"
)
Page<UsersEntity> find(Pageable pageable);

動作検証用のコード

動作検証用にこんな感じのテストコードを書いています。

@Test
public void test() throws Exception {
    final Page<UsersEntity> result = sut.find(new PageRequest(0, 10));
    System.out.println("result = " + result);
}

実行結果

実行すると、InvalidJpaQueryMethodExceptionが発生してしまいます。

Caused by: org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodException: Cannot use native queries with dynamic sorting and/or pagination in method public abstract org.springframework.data.domain.Page siosio.datajpaexample.repository.UsersRepository.find(org.springframework.data.domain.Pageable)
    at org.springframework.data.jpa.repository.query.NativeJpaQuery.<init>(NativeJpaQuery.java:58)

例外が発生している、NativeJpaQueryの実装(↓)を見てみるとPageableパラメータを持つ場合には#pageableという文字列がSQL内にないとダメなようです。

public NativeJpaQuery(JpaQueryMethod method, EntityManager em, String queryString,
        EvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) {

    super(method, em, queryString, evaluationContextProvider, parser);

    Parameters<?, ?> parameters = method.getParameters();
    boolean hasPagingOrSortingParameter = parameters.hasPageableParameter() || parameters.hasSortParameter();
    boolean containsPageableOrSortInQueryExpression = queryString.contains("#pageable")
            || queryString.contains("#sort");

    if (hasPagingOrSortingParameter && !containsPageableOrSortInQueryExpression) {
        throw new InvalidJpaQueryMethodException(
                "Cannot use native queries with dynamic sorting and/or pagination in method " + method);
    }
}

SQL#pageableを追加して再実行してみよう

Repositoryの実装を修正して、SQL#pageableを追加してみます。

@Query(
        nativeQuery = true,
        value = "select * from users #pageable",
        countQuery = "select count(*) from users"
)
Page<UsersEntity> find(Pageable pageable);

対応後でも残念ながら別のエラーで落ちてしまいましたね…SQLを実行するところまではいっているようなのでどんなSQLを実行しようとしているのか見てみましょう。

could not execute query; nested exception is org.hibernate.exception.GenericJDBCException: could not execute query
org.springframework.orm.jpa.JpaSystemException: could not execute query; nested exception is org.hibernate.exception.GenericJDBCException: could not execute query
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:333)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)

まさかのSQL内の#pageableが残ったままになってますね(´・ω・`) f:id:sioiri:20190817064721p:plain

エラーの回避方法

Spring Data JPA @Query | Baeldung4.3. Spring Data JPA Versions Prior to 2.0.4に回避方法が書いてありますね。
どうやら、Spring Data JPA 2.0.4より前の場合には、バグ?的なものがあるようです。

リンク先の回避方法を真似て、#pageableSQLコメントとして書いてあげます。大事なのは、#pageableの前後に改行を入れることですね。 改行忘れると、SQLの最後にくっつくlimitまでコメントになってしまいます。

@Query(
        nativeQuery = true,
        value = "select * from users \n -- #pageable \n",
        countQuery = "select count(*) from users"
)
Page<UsersEntity> find(Pageable pageable);

これで正常に実行できるようになりました。

result = Page 2 of 1 containing UNKNOWN instances

Spring Data JPAを2.0.4以降にしてみると

最初に#pageableの実装がないよと例外投げてたNativeJpaQueryから#pageableに関する実装が消えていますね。

public NativeJpaQuery(JpaQueryMethod method, EntityManager em, String queryString,
        EvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) {

    super(method, em, queryString, evaluationContextProvider, parser);

    Parameters<?, ?> parameters = method.getParameters();

    if (parameters.hasSortParameter() && !queryString.contains("#sort")) {
        throw new InvalidJpaQueryMethodException(
                "Cannot use native queries with dynamic sorting in method " + method);
    }
    this.resultType = getTypeToQueryFor();
}

ということで、Repositoryの実装はこんな感じで#pageableなしで書けるようになります。

@Query(
        nativeQuery = true,
        value = "select * from users",
        countQuery = "select count(*) from users"
)
Page<UsersEntity> find(Pageable pageable);

#pageableは消えたけど、#sortに関する実装がまだ残っているのでどうなるのか見てみます。 Repositoryの実装をソートのみに変えてみます。SQLには、#sortを含めてあげます。

@Query(
        nativeQuery = true,
        value = "select * from users #sort"
)
List<UsersEntity> find(Sort sort);

エラーにならず実行できました。#sortに関する処理は正しく動くようです。

result = [siosio.datajpaexample.entity.UsersEntity@6dff619a, siosio.datajpaexample.entity.UsersEntity@3f73d455, siosio.datajpaexample.entity.UsersEntity@24df2d20]

まとめ

バージョンアップして幸せになりたい。

おわり。

*1:色々と制約があってこうするしかなかった…

法人番号のチェックデジットを算出してみた

法人番号のチェックデジットの求め方は、法人番号を確認するページにあるチェックデジットの計算リンクから確認できます。

チェックデジットを求めるコード

川口市の法人番号を使ってチェックデジットを求めるコードを書いてみました。 頭ひと桁がチェックデジットなので結果として2が求められればOKですね。

fun main() {
    val corporateNumber = "2000020112038"
    data class Temp(val odd: Int, val even: Int) {
        fun addOdd(value: Int) = this.copy(odd = odd + value)
        fun addEven(value: Int) = this.copy(even = even + value)
    }

    val checkDigit = corporateNumber
            .substring(1)
            .reversed()
            .foldIndexed(Temp(0, 0)) { index, acc, c ->
                when ((index + 1) % 2) {
                    0 -> acc.addOdd(c.toInt())
                    else -> acc.addEven(c.toInt())
                }
            }
            .let { (it.odd * 2) + it.even }
            .let { 9 - (it % 9) }
    println("checkDigit = ${checkDigit}")
}

実行結果

チェックデジットが正しく求められたっぽいことがわかります!
f:id:sioiri:20190719233927p:plain

Spring Batchのchunkステップを並列で実行してみた

Spring Batchのchunkステップの各処理を並列実行してみました。

並列実行するためのジョブ定義

Readerで1から100までの連番を生成して、Writerでtestテーブルにbatch insertするだけのシンプルなchunkステップを持つジョブを使って試します。 並列実行されていることを確認するためにWriterでitemsをログに出力しています。

@Configuration
class BatchConfiguration(
        private val jobBuilderFactory: JobBuilderFactory,
        private val stepBuilderFactory: StepBuilderFactory,
        private val jdbcTemplate: JdbcTemplate
) {
    
    private val logger = LoggerFactory.getLogger(BatchConfiguration::class.java)
    
    @Bean
    fun job(): Job {
        return jobBuilderFactory.get("job")
                .incrementer(RunIdIncrementer())
                .start(step())
                .build()
    }

    @Bean
    fun step(): Step {
        val input = (1..100).iterator()
        return stepBuilderFactory.get("step")
                .chunk<Int, Int>(10)
                .reader(fun(): Int? {
                    return if (input.hasNext()) {
                        input.nextInt()
                    } else {
                        null
                    }
                })
                .writer {
                    logger.info("write size: ${it.size}, items: ${it}")
                    jdbcTemplate.batchUpdate("insert into test (id) values (?)", object : BatchPreparedStatementSetter {
                        override fun setValues(ps: PreparedStatement, i: Int) {
                            ps.setInt(1, it[i])
                        }

                        override fun getBatchSize(): Int {
                            return it.size
                        }
                    })
                }
                .build()
    }
}

並列実行の構成をせずにJOBを実行した結果

並列実行の構成を行わなかった場合、Writerの処理がmainスレッドで行われていることがわかります。

2019-07-14 07:11:45.608  INFO 22744 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step]
2019-07-14 07:11:45.623  INFO 22744 --- [           main] s.springbatchsample.BatchConfiguration   : write size: 10, items: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2019-07-14 07:11:45.633  INFO 22744 --- [           main] s.springbatchsample.BatchConfiguration   : write size: 10, items: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
2019-07-14 07:11:45.642  INFO 22744 --- [           main] s.springbatchsample.BatchConfiguration   : write size: 10, items: [21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
2019-07-14 07:11:45.648  INFO 22744 --- [           main] s.springbatchsample.BatchConfiguration   : write size: 10, items: [31, 32, 33, 34, 35, 36, 37, 38, 39, 40]
2019-07-14 07:11:45.653  INFO 22744 --- [           main] s.springbatchsample.BatchConfiguration   : write size: 10, items: [41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
2019-07-14 07:11:45.659  INFO 22744 --- [           main] s.springbatchsample.BatchConfiguration   : write size: 10, items: [51, 52, 53, 54, 55, 56, 57, 58, 59, 60]
2019-07-14 07:11:45.666  INFO 22744 --- [           main] s.springbatchsample.BatchConfiguration   : write size: 10, items: [61, 62, 63, 64, 65, 66, 67, 68, 69, 70]
2019-07-14 07:11:45.672  INFO 22744 --- [           main] s.springbatchsample.BatchConfiguration   : write size: 10, items: [71, 72, 73, 74, 75, 76, 77, 78, 79, 80]
2019-07-14 07:11:45.679  INFO 22744 --- [           main] s.springbatchsample.BatchConfiguration   : write size: 10, items: [81, 82, 83, 84, 85, 86, 87, 88, 89, 90]
2019-07-14 07:11:45.685  INFO 22744 --- [           main] s.springbatchsample.BatchConfiguration   : write size: 10, items: [91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
2019-07-14 07:11:45.711  INFO 22744 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=job]] completed with the following parameters: [{run.id=59}] and the following status: [COMPLETED]

並列実行するための構成を追加

並列実行するためのTaskExecutorが必要となるためインジェクションします。 TaskExecutorは、org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfigurationが生成してくれるので、特にBeanを生成しなくても利用できます。

@Configuration
class BatchConfiguration(
        private val jobBuilderFactory: JobBuilderFactory,
        private val stepBuilderFactory: StepBuilderFactory,
        private val jdbcTemplate: JdbcTemplate,
        private val taskExecutor: TaskExecutor
) {

インジェクションしたTaskExecutorをステップ構築時に設定(taskExecutorメソッドに)します。 また、並列に実行されては困る部分は、適宜同期処理を入れる必要が有ります。今回のステップではReaderの処理が同時に実行されると同じ番号を返す必要があるため@Synchronizedを追加しています。

    @Bean
    fun step(): Step {
        val input = (1..100).iterator()
        return stepBuilderFactory.get("step")
                .chunk<Int, Int>(10)
                .reader(@Synchronized fun(): Int? {
                  // 省略
                })
                .writer {
                  // 省略
                }
                .taskExecutor(taskExecutor)
                .build()
    }

実行結果

Writerで出力しているログ内容から各タスクが並列実行(ログのスレッド名からスレッド数は8)されていることがわかります。
※デフォルトのスレッド数が8となっているので、この結果となります。

2019-07-14 07:40:51.342  INFO 24520 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step]
2019-07-14 07:40:51.403  INFO 24520 --- [         task-2] s.springbatchsample.BatchConfiguration   : write size: 10, items: [1, 7, 11, 14, 17, 20, 24, 28, 32, 36]
2019-07-14 07:40:51.404  INFO 24520 --- [         task-3] s.springbatchsample.BatchConfiguration   : write size: 10, items: [3, 5, 21, 25, 29, 33, 37, 38, 39, 40]
2019-07-14 07:40:51.405  INFO 24520 --- [         task-1] s.springbatchsample.BatchConfiguration   : write size: 10, items: [2, 8, 10, 13, 16, 18, 22, 26, 31, 35]
2019-07-14 07:40:51.406  INFO 24520 --- [         task-4] s.springbatchsample.BatchConfiguration   : write size: 10, items: [4, 6, 9, 12, 15, 19, 23, 27, 30, 34]
2019-07-14 07:40:51.436  INFO 24520 --- [         task-5] s.springbatchsample.BatchConfiguration   : write size: 10, items: [41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
2019-07-14 07:40:51.445  INFO 24520 --- [         task-6] s.springbatchsample.BatchConfiguration   : write size: 10, items: [51, 52, 53, 54, 55, 56, 57, 58, 59, 60]
2019-07-14 07:40:51.452  INFO 24520 --- [         task-7] s.springbatchsample.BatchConfiguration   : write size: 10, items: [61, 62, 63, 64, 65, 66, 67, 68, 69, 70]
2019-07-14 07:40:51.460  INFO 24520 --- [         task-8] s.springbatchsample.BatchConfiguration   : write size: 10, items: [71, 72, 73, 74, 75, 76, 77, 78, 79, 80]
2019-07-14 07:40:51.467  INFO 24520 --- [         task-2] s.springbatchsample.BatchConfiguration   : write size: 10, items: [81, 82, 83, 84, 85, 86, 87, 88, 89, 90]
2019-07-14 07:40:51.473  INFO 24520 --- [         task-4] s.springbatchsample.BatchConfiguration   : write size: 10, items: [91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
2019-07-14 07:40:51.618  INFO 24520 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=job]] completed with the following parameters: [{run.id=63}] and the following status: [COMPLETED]

スレッド数を変更してみる

スレッド数は、application.propertiesで設定できます。 例えば、スレッド数を4としたい場合には、下のように設定します。

spring.task.execution.pool.core-size=4

実行結果

スレッド名が、task-1からtask-4までとなっていてスレッド数が4に制限されたことがわかります。

2019-07-14 07:48:49.039  INFO 24812 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step]
2019-07-14 07:48:49.062  INFO 24812 --- [         task-1] s.springbatchsample.BatchConfiguration   : write size: 10, items: [3, 8, 11, 15, 19, 23, 27, 31, 35, 39]
2019-07-14 07:48:49.063  INFO 24812 --- [         task-2] s.springbatchsample.BatchConfiguration   : write size: 10, items: [1, 6, 10, 13, 17, 21, 25, 29, 34, 37]
2019-07-14 07:48:49.063  INFO 24812 --- [         task-4] s.springbatchsample.BatchConfiguration   : write size: 10, items: [2, 5, 9, 14, 18, 22, 26, 30, 33, 38]
2019-07-14 07:48:49.063  INFO 24812 --- [         task-3] s.springbatchsample.BatchConfiguration   : write size: 10, items: [4, 7, 12, 16, 20, 24, 28, 32, 36, 40]
2019-07-14 07:48:49.076  INFO 24812 --- [         task-4] s.springbatchsample.BatchConfiguration   : write size: 10, items: [41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
2019-07-14 07:48:49.083  INFO 24812 --- [         task-3] s.springbatchsample.BatchConfiguration   : write size: 10, items: [51, 52, 53, 54, 55, 56, 57, 58, 59, 60]
2019-07-14 07:48:49.090  INFO 24812 --- [         task-2] s.springbatchsample.BatchConfiguration   : write size: 10, items: [61, 62, 63, 64, 65, 66, 67, 68, 69, 70]
2019-07-14 07:48:49.096  INFO 24812 --- [         task-1] s.springbatchsample.BatchConfiguration   : write size: 10, items: [71, 72, 73, 74, 75, 76, 77, 78, 79, 80]
2019-07-14 07:48:49.101  INFO 24812 --- [         task-4] s.springbatchsample.BatchConfiguration   : write size: 10, items: [81, 82, 83, 84, 85, 86, 87, 88, 89, 90]
2019-07-14 07:48:49.107  INFO 24812 --- [         task-3] s.springbatchsample.BatchConfiguration   : write size: 10, items: [91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
2019-07-14 07:48:49.178  INFO 24812 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=job]] completed with the following parameters: [{run.id=64}] and the following status: [COMPLETED]

ステップで使用できるスレッド数を制限する

ステップ構築時に、throttleLimitを設定することで制限することができます。 デフォルトでは、スレッド数が8でthrottleLimitが4なので、スッテップでは同時に4スレッドのみが使われます。 多分ですが、Flowを使用して同時に複数のステップを実行するような構成とした場合に使うのかなと思います。

今回は、throttleLimitを2にしてWriterにスリープ処理を入れて制限がかかっていることを確認しやすくしています。

    @Bean
    fun step(): Step {
        val input = (1..100).iterator()
        return stepBuilderFactory.get("step")
                .chunk<Int, Int>(10)
                .reader(@Synchronized fun(): Int? {
                  // 省略
                })
                .writer {
                    logger.info("write size: ${it.size}, items: ${it}")
                    TimeUnit.SECONDS.sleep(5)
                    jdbcTemplate.batchUpdate("insert into test (id) values (?)", object : BatchPreparedStatementSetter {
                        override fun setValues(ps: PreparedStatement, i: Int) {
                            ps.setInt(1, it[i])
                        }

                        override fun getBatchSize(): Int {
                            return it.size
                        }
                    })
                }
                .taskExecutor(taskExecutor)
                .throttleLimit(2)
                .build()
    }

実行結果

ログから、2スレッド処理する毎に5秒間が開くのでthrottleLimitでスレッド数が制限されていることがわかります。

2019-07-14 08:07:10.815  INFO 26547 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step]
2019-07-14 08:07:10.834  INFO 26547 --- [         task-1] s.springbatchsample.BatchConfiguration   : write size: 10, items: [1, 4, 6, 7, 9, 11, 13, 15, 17, 19]
2019-07-14 08:07:10.834  INFO 26547 --- [         task-2] s.springbatchsample.BatchConfiguration   : write size: 10, items: [2, 3, 5, 8, 10, 12, 14, 16, 18, 20]
2019-07-14 08:07:15.889  INFO 26547 --- [         task-3] s.springbatchsample.BatchConfiguration   : write size: 10, items: [21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
2019-07-14 08:07:15.898  INFO 26547 --- [         task-4] s.springbatchsample.BatchConfiguration   : write size: 10, items: [31, 32, 33, 34, 35, 36, 37, 38, 39, 40]
2019-07-14 08:07:20.932  INFO 26547 --- [         task-5] s.springbatchsample.BatchConfiguration   : write size: 10, items: [41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
2019-07-14 08:07:20.947  INFO 26547 --- [         task-6] s.springbatchsample.BatchConfiguration   : write size: 10, items: [51, 52, 53, 54, 55, 56, 57, 58, 59, 60]
2019-07-14 08:07:25.974  INFO 26547 --- [         task-7] s.springbatchsample.BatchConfiguration   : write size: 10, items: [61, 62, 63, 64, 65, 66, 67, 68, 69, 70]
2019-07-14 08:07:25.990  INFO 26547 --- [         task-8] s.springbatchsample.BatchConfiguration   : write size: 10, items: [71, 72, 73, 74, 75, 76, 77, 78, 79, 80]
2019-07-14 08:07:31.018  INFO 26547 --- [         task-2] s.springbatchsample.BatchConfiguration   : write size: 10, items: [81, 82, 83, 84, 85, 86, 87, 88, 89, 90]
2019-07-14 08:07:31.037  INFO 26547 --- [         task-1] s.springbatchsample.BatchConfiguration   : write size: 10, items: [91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
2019-07-14 08:07:36.095  INFO 26547 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=job]] completed with the following parameters: [{run.id=71}] and the following status: [COMPLETED]