当数据包含日文字符时无法使用 org.postgresql.copy.CopyManager 插入数据库
Unable to insert into database using org.postgresql.copy.CopyManager when the data has japanese characters
在过去的几个小时里,我一直在努力弄清楚我的代码有什么问题。这段代码一切正常,直到我收到一个包含日文字符的文件。 Notepad++甚至一些在线实用工具都说文件的编码是UTF-8。记事本显示其 UTF-8-BOM。
我已经从文件中读取了我的数据并对其进行了处理,最后想将其写出到数据库中。
我收到错误 org.postgresql.util.PSQLException:错误:编码字节序列无效 "UTF8":0xee
我的数据库编码只有UTF8..
package citynet.dta.pump;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import org.postgresql.copy.CopyManager;
import org.postgresql.core.BaseConnection;
import citynet.common.ServerException;
public class TestEncoding {
public static void main(String[] args) {
byte[] bytes = null;
try {
//use the below sql to create table 'testtable'
// create table testtable (text1 character varying, text2 character varying,text3 character varying)
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
DataOutputStream out = new DataOutputStream(baos);
out.writeBytes("INR,字仮名交じり文,3255104BTK1");
bytes = baos.toByteArray();
}
Class.forName("org.postgresql.Driver");
Connection c = DriverManager.getConnection("jdbc:postgresql://server:5432/dbname", "username", "password");
if (bytes != null) {
try (ByteArrayInputStream input = new ByteArrayInputStream(bytes)) {
String sql = "COPY testtable FROM stdin delimiter ',' NULL AS 'null' ENCODING 'UTF8' ";
BaseConnection pgcon = (BaseConnection) c;
CopyManager mgr = new CopyManager(pgcon);
try {
mgr.copyIn(sql, input);
} catch (SQLException ex) {
throw new ServerException("Error while copying data in Postgres DB:" + ex);
}
}
}
} catch (Exception e) {
System.out.println("Error:" + e);
}
}
}
问题是 DataOutputStream#writeBytes("INR,字仮名交じり文,3255104BTK1")
没有按照您的预期进行。
您应该避免使用 BaseConnection
,因为它是一个内部 class。应用程序代码应使用 PGConnection
这是你如何获得 CopyManager
:
Connection con = ...;
PGConnection pgcon = con.unwrap(org.postgresql.PGConnection.class);
CopyManager mgr = pgcon.getCopyAPI();
您的数据来源可能不同,因此有多种执行方式copyAPI
。
如果您想通过自己的代码将 String
转换为 UTF-8
字节,那么您需要 getBytes
.
String sql = "COPY testtable FROM stdin delimiter ',' NULL AS 'null' ENCODING 'UTF8' ";
byte[] bytes = "INR,字仮名交じり文,3255104BTK1".getBytes(StandardCharsets.UTF_8);
mgr.copyIn(sql, new ByteArrayInputStream(bytes));
注意:不需要 close
ByteArrayInputStream
(参见它的 Javadoc)。
如果您需要将 CSV 文件流式传输到数据库,您可以使用 FileInputStream
:
try (InputStream fis = new FileInputStream("file.csv")) {
mgr.copyIn(sql, fis);
}
如果您想逐步构建内容,则可以使用 ByteArrayOutputStream + OutputStreamWriter
注意:所有行都需要适合内存,否则你会得到 OutOfMemoryError
.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (OutputStreamWriter wr = new OutputStreamWriter(baos, StandardCharsets.UTF_8)) {
// Write 10 rows
for (int i = 0; i < 10; i++) {
wr.write("INR,字仮名交じり文,3255104BTK1\n");
}
}
String sql = "COPY testtable FROM stdin delimiter ',' NULL AS 'null' ENCODING 'UTF8'";
mgr.copyIn(sql, new ByteArrayInputStream(baos.toByteArray()));
另一种选择是使用 Reader
注意:未指定编码,它使用 connection-default 编码(在 99.42% 的情况下为 utf-8,因为驱动程序默认为 utf-8连接编码)。
String sql = "COPY testtable FROM stdin delimiter ',' NULL AS 'null'";
mgr.copyIn(sql, new StringReader("INR,字仮名交じり文,3255104BTK1"));
另一种选择是使用 copyIn(String sql, ByteStreamWriter from)
API 这对于某些用例可能更有效(例如,所有数据都在内存中,并且您知道您要写入的字节数)
在过去的几个小时里,我一直在努力弄清楚我的代码有什么问题。这段代码一切正常,直到我收到一个包含日文字符的文件。 Notepad++甚至一些在线实用工具都说文件的编码是UTF-8。记事本显示其 UTF-8-BOM。 我已经从文件中读取了我的数据并对其进行了处理,最后想将其写出到数据库中。
我收到错误 org.postgresql.util.PSQLException:错误:编码字节序列无效 "UTF8":0xee 我的数据库编码只有UTF8..
package citynet.dta.pump;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import org.postgresql.copy.CopyManager;
import org.postgresql.core.BaseConnection;
import citynet.common.ServerException;
public class TestEncoding {
public static void main(String[] args) {
byte[] bytes = null;
try {
//use the below sql to create table 'testtable'
// create table testtable (text1 character varying, text2 character varying,text3 character varying)
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
DataOutputStream out = new DataOutputStream(baos);
out.writeBytes("INR,字仮名交じり文,3255104BTK1");
bytes = baos.toByteArray();
}
Class.forName("org.postgresql.Driver");
Connection c = DriverManager.getConnection("jdbc:postgresql://server:5432/dbname", "username", "password");
if (bytes != null) {
try (ByteArrayInputStream input = new ByteArrayInputStream(bytes)) {
String sql = "COPY testtable FROM stdin delimiter ',' NULL AS 'null' ENCODING 'UTF8' ";
BaseConnection pgcon = (BaseConnection) c;
CopyManager mgr = new CopyManager(pgcon);
try {
mgr.copyIn(sql, input);
} catch (SQLException ex) {
throw new ServerException("Error while copying data in Postgres DB:" + ex);
}
}
}
} catch (Exception e) {
System.out.println("Error:" + e);
}
}
}
问题是 DataOutputStream#writeBytes("INR,字仮名交じり文,3255104BTK1")
没有按照您的预期进行。
您应该避免使用
BaseConnection
,因为它是一个内部 class。应用程序代码应使用PGConnection
这是你如何获得
CopyManager
:Connection con = ...; PGConnection pgcon = con.unwrap(org.postgresql.PGConnection.class); CopyManager mgr = pgcon.getCopyAPI();
您的数据来源可能不同,因此有多种执行方式
copyAPI
。如果您想通过自己的代码将
String
转换为UTF-8
字节,那么您需要getBytes
.String sql = "COPY testtable FROM stdin delimiter ',' NULL AS 'null' ENCODING 'UTF8' "; byte[] bytes = "INR,字仮名交じり文,3255104BTK1".getBytes(StandardCharsets.UTF_8); mgr.copyIn(sql, new ByteArrayInputStream(bytes));
注意:不需要
close
ByteArrayInputStream
(参见它的 Javadoc)。如果您需要将 CSV 文件流式传输到数据库,您可以使用
FileInputStream
:try (InputStream fis = new FileInputStream("file.csv")) { mgr.copyIn(sql, fis); }
如果您想逐步构建内容,则可以使用
ByteArrayOutputStream + OutputStreamWriter
注意:所有行都需要适合内存,否则你会得到
OutOfMemoryError
.ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (OutputStreamWriter wr = new OutputStreamWriter(baos, StandardCharsets.UTF_8)) { // Write 10 rows for (int i = 0; i < 10; i++) { wr.write("INR,字仮名交じり文,3255104BTK1\n"); } } String sql = "COPY testtable FROM stdin delimiter ',' NULL AS 'null' ENCODING 'UTF8'"; mgr.copyIn(sql, new ByteArrayInputStream(baos.toByteArray()));
另一种选择是使用
Reader
注意:未指定编码,它使用 connection-default 编码(在 99.42% 的情况下为 utf-8,因为驱动程序默认为 utf-8连接编码)。
String sql = "COPY testtable FROM stdin delimiter ',' NULL AS 'null'"; mgr.copyIn(sql, new StringReader("INR,字仮名交じり文,3255104BTK1"));
另一种选择是使用
copyIn(String sql, ByteStreamWriter from)
API 这对于某些用例可能更有效(例如,所有数据都在内存中,并且您知道您要写入的字节数)