Bas*_*ass 12 java sql oracle nvarchar jdbc
请考虑下表,其中一列的类型为nullable NVARCHAR:
CREATE TABLE CHARACTER_SET_MISMATCH_TEST (
ID NUMBER(10) NOT NULL,
VALUE NVARCHAR2(32)
);
Run Code Online (Sandbox Code Playgroud)
现在,我想使用多行INSERT(带子查询)语法将多个数据元组插入到此表中:
INSERT
INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)
SELECT ?, ? FROM DUAL
UNION ALL
SELECT ?, ? FROM DUAL;
Run Code Online (Sandbox Code Playgroud)
如果NVARCHAR值是两者NULL都是非NULL,或者两者都是非的,那么一切都运行正常,我确实插入了2行.但是,如果我在一个单独的混合NULL和非NULL值PreparedStatement,我立即收到一个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)
Run Code Online (Sandbox Code Playgroud)
这是重现问题的代码:
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));
}
}
}
Run Code Online (Sandbox Code Playgroud)
奇怪的是,如果我明确地将我的参数转换为NCHAR:
INSERT
INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)
SELECT ?, TO_NCHAR(?) FROM DUAL
UNION ALL
SELECT ?, TO_NCHAR(?) FROM DUAL;
Run Code Online (Sandbox Code Playgroud)
或切换到INSERT ALL语法:
INSERT ALL
INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)
VALUES (?, ?)
INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)
VALUES (?, ?)
SELECT * FROM DUAL;
Run Code Online (Sandbox Code Playgroud)
但原始代码有什么问题?
如果您可以拦截发送到数据库的实际查询,我想它看起来类似于:
\n\nINSERT\n INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)\n SELECT 0, \'abc\' FROM DUAL\n UNION ALL\n SELECT 1, CAST(NULL AS NVARCHAR2(100)) FROM DUAL;\n-- ORA-12704: character set mismatch\n\n-- or\nINSERT\nINTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)\nSELECT 0, N\'abc\' FROM DUAL\nUNION ALL\nSELECT 1, CAST(NULL AS VARCHAR2(100)) FROM DUAL;\n-- ORA-12704: character set mismatch\nRun Code Online (Sandbox Code Playgroud)\n\n\n\n在 Oracle 中,如果您这样做:
\n\nSELECT N\'abc\' FROM dual\nUNION ALL\nSELECT \'abc\' FROM dual\nRun Code Online (Sandbox Code Playgroud)\n\n你会得到错误:
\n\n\n\n\n\n\nORA-12704: 字符集不匹配
\n
\n\n\n如果组件查询选择字符数据,则返回值的数据类型确定如下:
\n\n\n
\n- \n
如果两个查询都选择长度相等的 CHAR 数据类型的值,则返回的值具有该长度的 CHAR 数据类型。如果查询选择不同长度的 CHAR 值,则返回值为 VARCHAR2,其长度为较大的 CHAR 值。
- \n
如果其中一个或两个查询选择数据类型 VARCHAR2 的值,则返回的值的数据类型为 VARCHAR2。
那么回到你的工作方法:
\n\n1)相同的数据类型(显式转换)
\n\nINSERT\n INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)\n SELECT ?, TO_NCHAR(?) FROM DUAL\n UNION ALL\n SELECT ?, TO_NCHAR(?) FROM DUAL;\nRun Code Online (Sandbox Code Playgroud)\n\n2)两个“独立” INSERTs:
INSERT ALL\n INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)\n VALUES (?, ?)\n INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)\n VALUES (?, ?)\n SELECT * FROM DUAL;\nRun Code Online (Sandbox Code Playgroud)\n\n3)“如果 NVARCHAR 值都是 NULL 或都是非 NULL,则一切运行正常,并且我观察到插入了 2 行” - 相同的数据类型,因此工作正常
\n\nINSERT\n INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)\n SELECT ?, ? FROM DUAL\n UNION ALL\n SELECT ?, ? FROM DUAL;\nRun Code Online (Sandbox Code Playgroud)\n\nNULL最后,有值的情况NOT NULL会产生错误。它清楚地表明映射无效。我相信这与:
\n\n\nRun Code Online (Sandbox Code Playgroud)\n\n\xe2\x94\x8c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x90\n\xe2\x94\x82 These SQL data types: \xe2\x94\x82 Can be materialized as these Java types: \xe2\x94\x82\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4\n\xe2\x94\x82 NVARCHAR2 \xe2\x94\x82 no (see Note) \xe2\x94\x82\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x98\n注意:\n 间接支持 NCHAR 和 NVARCHAR2 类型。没有相应的 java.sql.Types 类型,但如果您的应用程序调用formOfUse(NCHAR),则可以访问这些类型。
\n
以及JDK 1.5 中的 NCHAR、NVARCHAR2、NCLOB 和 defaultNChar 属性:
\n\n\n\n\n默认情况下,oracle.jdbc.OraclePreparedStatement 接口以与数据库字符集中编码相同的方式处理所有列的数据类型。但是,从 Oracle Database 10g 开始,如果将 oracle.jdbc.defaultNChar 系统属性的值设置为 true,则 JDBC 会将所有字符列视为国家语言。
\n\ndefaultNChar 的默认值为 false。如果 defaultNChar 的值为 false,则必须为那些特别需要本地语言字符的列调用 setFormOfUse(, OraclePreparedStatement.FORM_NCHAR) 方法。
\n
所以你的可能看起来像:
\n\npstmt.setInt(1, 0);\npstmt.setFormOfUse(2, OraclePreparedStatement.FORM_NCHAR);\npstmt.setNString(2, "NVARCHAR");\npstmt.setInt(3, 1);\npstmt.setFormOfUse(4, OraclePreparedStatement.FORM_NCHAR);\npstmt.setNull(4, Types.NVARCHAR);\nRun Code Online (Sandbox Code Playgroud)\n\n另一种想法:Oracle 将空字符串视为与NULL下面的代码相同,因此也应该可以正常工作:
pstmt.setInt(1, 0);\npstmt.setNString(2, "NVARCHAR");\npstmt.setInt(3, 1);\npstmt.setNString(4, "");\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
855 次 |
| 最近记录: |