자바데몬에서 한글을 입력시에 MySQL 에 Insert 하는 과정에서 아래와 같은 Exception 이 종종 발생 했는데.. 며칠 삽질한 끝에 원인을 찾았습니다.
Incorrect string value: '\xF0\x9F\x98\x8D\xF0\x9F...' for column 'NICKNAME' at row 1java.sql.SQLException: Incorrect string value: '\xF0\x9F\x98\x8D\xF0\x9F...' for column 'NICKNAME' at row 1
com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1056)
com.mysql.jdbc.SQLError.createSQLException(SQLError.java:957)
com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3376)
com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3308)
com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1837)
com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1961)
com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2543)
com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1737)
com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2022)
com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1940)
com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1925)
nateon.qsync.table.SyncStatementInfo.sync(SyncStatementInfo.java:100)
nateon.qsync.table.TableDistribute.syncRecord(TableDistribute.java:118)
nateon.qsync.QSyncEntry.run(QSyncManager.java:124)
문제는 Surrogate Area 라고 불리는 유니코드의 영억 때문인데요.
D800-DBFF, DC00-DFFF 이 영억에는 실제로 매핑된 캐릭터가 오는게 아니고, UTF-16 확장을 위해서 미리 잡아놓은 영역입니다.
JAVA String, Oracle NVARCHAR 는 UTF-16 입니다. (UCS-2 아님)
즉 UCS-4, UCS-8 도 수용이 가능하다는 얘기이지요.
그런데, MySQL의 유니코드 캐릭터셋은 아마도 UTF-16 이 아닌 UCS-2 를 그대로 사용하는 듯 합니다.
그래서 이 예비 영역의 글자가 들어오면 인식하지 못하고 에러를 뱉어 냅니다.
해결책은 아래 샘플 코드 처럼 스트링에서 Surrogate Area 에 있는 캐릭터를 제거하는 겁니다.
public static String removeSurrogateArea(String val)
{
if ( val == null ) return null;
StringBuffer buf = new StringBuffer();
int len = val.length();
for ( int i = 0 ; i < len ; i++ )
{
char c = val.charAt(i);
if ( 0xD800 <= c && c <= 0xDBFF ||
0xDC00 <= c && c <= 0xDFFF )
{
}
else
{
buf.append(c);
}
}
return buf.toString();
}
아래는 wiki 에서 찾은 Surrogates 의 원문입니다.
http://en.wikipedia.org/wiki/Mapping_of_Unicode_characters#Surrogates
SurrogatesThe 2,048 surrogates are not characters, but are reserved for use in UTF-16 to specify code points outside the Basic Multilingual Plane. They are divided into leading or "high surrogates" (D800-DBFF) and trailing or "low surrogates" (DC00-DFFF). In UTF-16, they must always appear in pairs, as a high surrogate followed by a low surrogate, thus using 32 bits to denote one code point.
10000 + (H - D800) × 400 + (L - DC00)
where H and L are the numeric values of the high and low surrogates respectively.
Since high surrogate values in the range DB80-DBFF always produce values in the Private Use planes, the high surrogate range can be further divided into (normal) high surrogates (D800-DB7F) and "high private use surrogates" (DB80-DBFF).
최근 덧글