【发布时间】:2017-07-20 16:03:59
【问题描述】:
考虑下表,其中一列的类型为可为空的NVARCHAR:
CREATE TABLE CHARACTER_SET_MISMATCH_TEST (
ID NUMBER(10) NOT NULL,
VALUE NVARCHAR2(32)
);
现在,我想使用多行INSERT(带有子查询)语法将多个数据元组插入到该表中:
INSERT
INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)
SELECT ?, ? FROM DUAL
UNION ALL
SELECT ?, ? FROM DUAL;
如果NVARCHAR 的值要么都是NULL 要么都不是NULL,那么一切运行正常,我观察到插入了 2 行。但是,如果我在一个 PreparedStatement 中混合了 NULL 和非 NULL 值,我会立即收到 ORA-12704: character set mismatch 错误:
java.sql.SQLException: ORA-12704: character set mismatch
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:452)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:400)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:884)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:471)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:199)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:535)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:238)
at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:1385)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1709)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:4364)
at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:4531)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeUpdate(OraclePreparedStatementWrapper.java:5575)
这是重现问题的代码:
package com.example;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import javax.sql.DataSource;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import oracle.jdbc.pool.OracleConnectionPoolDataSource;
import oracle.jdbc.pool.OracleDataSource;
public final class Ora12704Test {
@NonNull
private static final String SQL = "INSERT INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE) SELECT ?, ? FROM DUAL UNION ALL SELECT ?, ? FROM DUAL";
@Nullable
private static DataSource dataSource;
@Nullable
private Connection conn;
@BeforeClass
public static void setUpOnce() throws SQLException {
dataSource = new OracleConnectionPoolDataSource();
((OracleDataSource) dataSource).setURL("jdbc:oracle:thin:@:1521:XE");
}
@BeforeMethod
public void setUp() throws SQLException {
this.conn = dataSource.getConnection("SANDBOX", "SANDBOX");
}
@AfterMethod
public void tearDown() throws SQLException {
if (this.conn != null) {
this.conn.close();
}
this.conn = null;
}
@Test
public void testNullableNvarchar()
throws SQLException {
try (final PreparedStatement pstmt = this.conn.prepareStatement(SQL)) {
pstmt.setInt(1, 0);
pstmt.setNString(2, "NVARCHAR");
pstmt.setInt(3, 1);
pstmt.setNull(4, Types.NVARCHAR);
final int rowCount = pstmt.executeUpdate();
assertThat(rowCount, is(2));
}
}
}
奇怪的是,如果我将参数显式转换为NCHAR,上面的单元测试就可以通过了:
INSERT
INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)
SELECT ?, TO_NCHAR(?) FROM DUAL
UNION ALL
SELECT ?, TO_NCHAR(?) FROM DUAL;
或切换到INSERT ALL 语法:
INSERT ALL
INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)
VALUES (?, ?)
INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)
VALUES (?, ?)
SELECT * FROM DUAL;
但是原始代码有什么问题?
【问题讨论】:
标签: java sql oracle jdbc nvarchar