Spring Batchをいじっている最中に遭遇した問題です。
環境
Spring Boot 1.3.2.RELEASE
spring-boot-starter-batch
SQL Server 2012
エラー
発生のタイミングは、Job実行中に例外が発生してJobが中断されるときでした。
org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [UPDATE BATCH_STEP_EXECUTION set START_TIME = ?, END_TIME = ?, STATUS = ?, COMMIT_COUNT = ?, READ_COUNT = ?, FILTER_COUNT = ?, WRITE_COUNT = ?, EXIT_CODE = ?, EXIT_MESSAGE = ?, VERSION = ?, READ_SKIP_COUNT = ?, PROCESS_SKIP_COUNT = ?, WRITE_SKIP_COUNT = ?, ROLLBACK_COUNT = ?, LAST_UPDATED = ? where STEP_EXECUTION_ID = ? and VERSION = ?]; 文字列データまたはバイナリ データが切り捨てられます。; nested exception is com.microsoft.sqlserver.jdbc.SQLServerException: 文字列データまたはバイナリ データが切り捨てられます。
at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:102) ~[spring-jdbc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73) ~[spring-jdbc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
...
原因はBATCH_STEP_EXECUTION. EXIT_MESSAGEにカラムサイズを超える値をセットしようとしたことでした。
EXIT_MESSAGEのカラム定義は**varchar(2500)**です。
さて、SQL Server 2012の場合、文字列型のサイズの数え方は下記の通りとなります。
型 | 数え方 |
---|---|
charおよびvarchar | バイト数 |
ncharおよびnvarchar | 文字数 |
JobRepositoryへのINSERTおよびUPDATEでは、カラム定義を考慮してバッチ処理のなかで2500文字以上の文字は切り捨てられます。
上記のUPDATE文では、EXIT_MESSAGEにスタックトレースを入れようとしていたのですが、スタックトレースに日本語等のマルチバイト文字が含まれていました。
そうなると挿入文字数は2500文字以内に収まっているのに、バイト数に換算すると2500バイトを超えてしまうというわけでした。
回避方法
カラムの型をnvarcharに変更する
JobRepositoryのテーブルが生成されるたびに変えなくてはいけないので面倒ですが。。。
更新するのは
- BATCH_JOB_EXECUTION.EXIT_MESSAGE
- BATCH_STEP_EXECUTION.EXIT_MESSAGE
の2つです。
文字数の上限を2500から減らす
こっちはコードで回避できます。
@Configuration
@EnableBatchProcessing
public class BatchConfiguration {
@Bean
public JobRepository jobRepository(DataSource dataSource, PlatformTransactionManager manager) throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setMaxVarCharLength(2000);
factory.setDataSource(dataSource);
factory.setTransactionManager(manager);
return factory.getObject();
}
...
(後略)
...
}
JobRepositoryFactoryBean#setMaxVarCharLength を使って、文字数の上限を2500から減らせば、バイト数でカウントしても収まるという計算です。
ですが、マルチバイト文字が多いとやっぱり駄目です。。。
カラムのサイズを増やす
文字数の上限を変えるのと同じ発想です。varchar(2500)からvarchar(3000)くらいにすれば収まる気がします。
余談
ISSUEがあったのですが、「上手いやり方がないから手動でなんとかしてくれ」(超訳)とのことです。
参考
https://sites.google.com/site/soracane/home/springnitsuite/spring-batch/7-spring-batchno-tips#TOC-JobRepository-
https://jira.spring.io/browse/BATCH-1296