Невозможно вставить в базу данных, используя org. postgresql .copy.CopyManager, если данные содержат японские символы - PullRequest
0 голосов
/ 20 апреля 2020

Последние несколько часов я ломал голову, пытаясь понять, что не так с моим кодом. Этот фрагмент кода работал нормально, пока я не получил файл с японскими символами. Notepad ++ и даже некоторые онлайн-утилиты говорят, что кодировка файла UTF-8 . Блокнот говорит, что UTF-8-BOM . Я прочитал мои данные из файла, обработал их и, наконец, хочу записать их в базу данных.

Я получаю сообщение об ошибке. 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);
    }
  }
}

1 Ответ

0 голосов
/ 21 апреля 2020

Проблема в том, что DataOutputStream#writeBytes("INR,字仮名交じり文,3255104BTK1") не выполняет то, что вы ожидаете.

  1. Вы должны воздерживаться от использования BaseConnection, поскольку это внутренний класс. Код приложения должен использовать PGConnection

  2. Вот как вы получите CopyManager:

    Connection con = ...;
    PGConnection pgcon = con.unwrap(org.postgresql.PGConnection.class);
    CopyManager mgr = pgcon.getCopyAPI();
    
  3. Источник ваших данных может отличаться так что есть несколько способов выполнить 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 (см. Его Javado c).

  4. Если вам нужно передать файл CSV в базу данных, вы можете использовать FileInputStream:

    try (InputStream fis = new FileInputStream("file.csv")) {
      mgr.copyIn(sql, fis);
    }
    
  5. Если вы хотите построить содержимое постепенно, то вы можете использовать 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()));
    
  6. Альтернативный вариант - использовать Reader

    Примечание: кодировка не указана, и она использует кодировка подключения по умолчанию (в 99,42% случаев это utf-8, поскольку драйвер по умолчанию использует кодировку подключения utf-8).

    String sql = "COPY testtable  FROM stdin delimiter ','  NULL AS 'null'";
    mgr.copyIn(sql, new StringReader("INR,字仮名交じり文,3255104BTK1"));
    
  7. Пока что другая альтернатива должен использовать copyIn(String sql, ByteStreamWriter from) API, который может быть более эффективным для определенных случаев использования (например, все данные находятся в памяти, и вы знаете количество байтов, которые вы собираетесь записать)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...