source

ORA-12704: null NVARCHAR의 다중 행 INSERT를 수행할 때 문자 집합이 일치하지 않습니다.

factcode 2023. 7. 23. 14:45
반응형

ORA-12704: null NVARCHAR의 다중 행 INSERT를 수행할 때 문자 집합이 일치하지 않습니다.

중 다음 해 보십시오.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모든 것이 정상적으로 작동하고 정확히 두 줄이 삽입된 것을 관찰합니다.하지만, 만약 내가 섞는다면,NULL non-message 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)

문제를 재현하는 코드는 다음과 같습니다.

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;

근데 원래 코드가 뭐가 문제야?

DB로 전송되는 실제 쿼리를 가로챌 수 있다면 다음과 유사해 보입니다.

INSERT
    INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)
    SELECT 0, 'abc' FROM DUAL
    UNION ALL
    SELECT 1, CAST(NULL AS NVARCHAR2(100)) FROM DUAL;
-- ORA-12704: character set mismatch

-- or
INSERT
INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)
SELECT 0, N'abc' FROM DUAL
UNION ALL
SELECT 1, CAST(NULL AS VARCHAR2(100)) FROM DUAL;
-- ORA-12704: character set mismatch

DB Fidle 데모


Oracle에서 다음 작업을 수행할 경우:

SELECT N'abc' FROM dual
UNION ALL
SELECT 'abc' FROM dual

오류가 발생합니다.

ORA-12704: 문자 집합 불일치

시작:

구성 요소 쿼리가 문자 데이터를 선택하는 경우 반환 값의 데이터 유형은 다음과 같이 결정됩니다.

  • 두 쿼리 모두 길이가 동일한 데이터 유형 CHAR 값을 선택하면 반환되는 값의 데이터 유형 CHAR 값이 해당 길이의 데이터 유형 CHAR가 됩니다.쿼리에서 다른 길이의 CHAR 값을 선택하면 반환되는 값은 VARCHAR2이고, CHAR 값의 길이는 더 큽니다.

  • 쿼리 중 하나 또는 둘 다 데이터 유형 VARCHAR2의 값을 선택하면 반환되는 값은 데이터 유형 VARCHAR2를 가집니다.

이제 작업 방식으로 돌아갑니다.

동일한 데이터 유형(명시 변환)

INSERT
    INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)
    SELECT ?, TO_NCHAR(?) FROM DUAL
    UNION ALL
    SELECT ?, TO_NCHAR(?) FROM DUAL;

개의 " 두개독 "립적의"INSERTs:

INSERT ALL
    INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)
    VALUES (?, ?)
    INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)
    VALUES (?, ?)
    SELECT * FROM DUAL;

"NVARCHAR 값이 NULL이거나 NULL이 아닌 경우 모든 것이 정상적으로 실행되고 정확히 2개의 행이 삽입된 것으로 확인됩니다." - 동일한 데이터 유형이므로 정상적으로 작동합니다.

INSERT
    INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)
    SELECT ?, ? FROM DUAL
    UNION ALL
    SELECT ?, ? FROM DUAL;

경우NULL그리고.NOT NULL값이 오류를 생성합니다.매핑이 유효하지 않음을 분명히 나타냅니다.나는 그것이 다음과 관련이 있다고 생각합니다.

유효한 SQL-JDBC 데이터 유형 매핑:

┌────────────────────────┬──────────────────────────────────────────┐
│ These SQL data types:  │ Can be materialized as these Java types: │
├────────────────────────┼──────────────────────────────────────────┤
│ NVARCHAR2              │ no (see Note)                            │
└────────────────────────┴──────────────────────────────────────────┘

참고: NCARNVARCHAR2 유형은 간접적으로 지원됩니다.해당 java.sql이 없습니다.유형을 입력합니다. 그러나 프로그램에서 NCAR(form Of Use)을 호출하는 경우 이러한 유형에 액세스할 수 있습니다.

그리고 NCAR, NVARCHAR2, NCLOB기본값JDK 1.5의 NChar 속성:

기본적으로 oracle.jdbc입니다.Oracle Prepared Statement 인터페이스는 모든 열의 데이터 유형을 데이터베이스 문자 집합에 인코딩된 것과 동일한 방식으로 처리합니다.그러나 Oracle Database 10g 이후로 oracle.jdbc.default 값을 설정하면NChar 시스템 속성이 true이면 JDBC는 모든 문자 열을 국가 언어로 처리합니다.

기본값Nchar는 거짓입니다.기본값인 경우NChar가 false이면 setFormOfUse(, OraclePreparedStatement)를 호출해야 합니다.특히 국가별 문자가 필요한 열에 대한 FORM_NCAR) 메서드입니다.

따라서 다음과 같이 보일 수 있습니다.

pstmt.setInt(1, 0);
pstmt.setFormOfUse(2, OraclePreparedStatement.FORM_NCHAR);
pstmt.setNString(2, "NVARCHAR");
pstmt.setInt(3, 1);
pstmt.setFormOfUse(4, OraclePreparedStatement.FORM_NCHAR);
pstmt.setNull(4, Types.NVARCHAR);

한 가지 더 생각해 보세요.Oracle은 빈 문자열을 다음과 동일하게 처리합니다.NULL그래서 아래 코드도 잘 작동할 것입니다.

pstmt.setInt(1, 0);
pstmt.setNString(2, "NVARCHAR");
pstmt.setInt(3, 1);
pstmt.setNString(4, "");

대신 다음 sql을 사용해 볼 수 있습니다.

SELECT ?, cast(? as nvarchar2(32)) FROM DUAL
UNION ALL
SELECT ?, cast(? as nvarchar2(32)) FROM DUAL;

기본적으로 null이 varchar2 유형이고 SQL의 모든 부분을 결합하는 데 유형 불일치가 있기 때문에 당신의 오류가 있다고 생각합니다.Btw 삽입 부품 없이 이 SQL을 실행할 수 있는지 확인하고 오류가 계속 발생하는지 확인합니다.

저는 당신에게 세 번의 수표를 추천합니다.

먼저 이 부분을 변경합니다.

pstmt.setInt(1, 0);
pstmt.setNString(2, "NVARCHAR");
pstmt.setInt(3, 1);
pstmt.setNull(4, Types.NVARCHAR);

대상:

pstmt.setInt(1, 0);
pstmt.setString(2, "NVARCHAR");
pstmt.setInt(3, 1);
pstmt.setString(4, null);

(당신의 문제가 아니라고 생각합니다.일부 데이터베이스 문자 집합 문제를 해결할 수 있기 때문에 권장 사항일 뿐입니다.)

번째로 연결 풀 문자 집합을 확인합니다. "UTF-8"을 설정하는 것을 선호합니다. 이 spring.datasource.connectionProperties=useUnicode=true; characterEncoding=utf-8;

또는 응용프로그램 서버에 설정하거나 코드에서 처리할 수 있습니다.

셋째, plsql developer 또는 ...와 같은 sql 도구로 삽입문을 확인하고 이 문을 직접 테스트해야 합니다.

INSERT INTO CHARACTER_SET_MISMATCH_TEST (ID, VALUE)
SELECT 1, 'test' FROM DUAL
UNION ALL
SELECT 2, null FROM DUAL;

아니면 심지어 이것도:

SELECT 1 aa, 'test' bb FROM DUAL
UNION ALL
SELECT 2 aa, null bb FROM DUAL;

만약 당신이 또 오류를 발견한다면요.데이터베이스 문자 집합이며 코드와 관련이 없기 때문입니다.

이것이 도움이 되길 바랍니다.

언급URL : https://stackoverflow.com/questions/45219916/ora-12704-character-set-mismatch-when-performing-multi-row-insert-of-nullable-n

반응형