問題概要
* 以下の環境下で、CHAR(1)データ項目を文字(例えば 'f')で更新処理を行ったところ、
SQLException: Incorrect string value: 'xxx' for column 'yyy'が 発生した
環境
* OS : Windows7/10
* Java : Java1.8
* DB : MySQL
* dbutils-1.6
エラー内容
java.sql.SQLException: Incorrect string value: '\xAC\xED\x00\x05sr...' for column 'sex' at row 1 Query: INSERT INTO person (id, name, sex) VALUES (?, ?, ?) Parameters: [Z0000001, Tommy, f]
at org.apache.commons.dbutils.AbstractQueryRunner.rethrow(AbstractQueryRunner.java:392)
at org.apache.commons.dbutils.QueryRunner.update(QueryRunner.java:491)
at org.apache.commons.dbutils.QueryRunner.update(QueryRunner.java:404)
at com.sample.db.SampleDbUtils.execute(SampleDbUtils.java:28)
at com.sample.db.SampleDbUtils.main(SampleDbUtils.java:14)
サンプル
テーブル
CREATE TABLE `person` (
`id` CHAR(8) NOT NULL,
`name` VARCHAR(100) NULL DEFAULT NULL,
`sex` CHAR(1) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
NG例
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
public class SampleDbUtils {
public static void main(String[] args) {
SampleDbUtils sample = new SampleDbUtils();
sample.execute();
}
Connection connection = null;
QueryRunner queryRunner = new QueryRunner();
public void execute() {
try {
// DB接続
this.connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/sampledb", "root", "password");
// オートコミットを無効
this.connection.setAutoCommit(false);
// INSERTサンプル
this.queryRunner.update(this.connection, "INSERT INTO person (id, name, sex) VALUES (?, ?, ?)", "Z0000001",
"Tommy", 'f');
// commit
DbUtils.commitAndCloseQuietly(this.connection);
} catch (SQLException ex) {
DbUtils.rollbackAndCloseQuietly(this.connection);
ex.printStackTrace();
} finally {
DbUtils.closeQuietly(this.connection);
}
}
}
原因
* 文字 'f' を設定したこと
調査詳細
DbUtils内の[AbstractQueryRunner.java]のfillStatement()より
~~~
public void fillStatement(PreparedStatement stmt, Object... params)
// 略
if (params[i] != null) {
stmt.setObject(i + 1, params[i]);
} else {
~~~
params[i]が文字(例 'f')の場合、うまくいかない
試しに、DbUtilsを使わずに、以下のようにPreparedStatement を使っても同じエラーが発生する
~~~
PreparedStatement preparedStatement =
connection.prepareStatement("INSERT INTO person(id, name, sex) VALUES(?, ?, ?)");) {
preparedStatement.setString(1, "Z0000001");
preparedStatement.setString(2, "Tom");
preparedStatement.setObject(3, 'f');
result = preparedStatement.executeUpdate();
~~~
解決策
* 文字 CHAR(1)の項目であっても、文字 'f' ではなく、文字列 "f" として設定する
~~~
this.queryRunner.update(this.connection, "INSERT INTO person (id, name, sex) VALUES (?, ?, ?)",
"Z0000001", "Tommy", "f");
preparedStatement.setString(3, "f");
~~~