новая строка (байт []) дает разные результаты, когда я вставляю ее и вытаскиваю из базы данных

Прежде чем я сохраню массив байтов в базу данных, если я распечатаю вывод new String(data), он вернет удобочитаемую строку, такую ​​​​как foobar, но после того, как я вытащу ее из базы данных, new String(data) будет читаться как куча тарабарщины, например 9238929384739427349327.... Их так много части здесь я просто попытаюсь перечислить их все. Я использую eclipselink, и мой столбец данных определен:

@Lob
@Column(name = "data")
private byte[] data;

Если я запускаю этот код:

public static void main(String[] args) {
    System.out.println(Charset.defaultCharset());
}

Выводит windows-1250.

Моя база данных определяется как:

CREATE DATABASE project_trunk
  WITH OWNER = project
       ENCODING = 'UTF8'
       TABLESPACE = pg_default
       LC_COLLATE = 'English_United States.1252'
       LC_CTYPE = 'English_United States.1252'
       CONNECTION LIMIT = -1;

Я также пробовал это на БД, определенной следующим образом:

CREATE DATABASE project_trunk
  WITH OWNER = project
       ENCODING = 'UTF8'
       TABLESPACE = pg_default
       LC_COLLATE = 'en_US.UTF-8'
       LC_CTYPE = 'en_US.UTF-8'
       CONNECTION LIMIT = -1;

И проблема все еще возникает.

Я думаю, что моя база данных имеет кодировку, отличную от моего сервера приложений. Когда я помещаю что-то в базу данных и снова извлекаю ее, она декодирует ее неправильно, поэтому это выглядит как тарабарщина. Я на что-то там?

Теперь, когда дело доходит до решения этой проблемы, я немного смущен. Я думаю, что мне следует изменить кодировку файла моего сервера приложений, чтобы она совпадала с кодировкой базы данных. Я использую Glassfish 2.1.1. Когда я перехожу к application server -> advanced -> domain attributes и устанавливаю Locale на UTF8 или UTF-8, он говорит мне, что требуется перезагрузка. После перезапуска Glassfish это поле по-прежнему остается пустым, и я все еще получаю сообщение об ошибке. Я думаю, может быть, это не спасает собственность. Я бы вручную поместил его в файл конфигурации, но я не знаю, куда и что поставить.

В качестве альтернативы я попытался создать свою базу данных с ENCODING = 'WIN1250', но когда я это делаю, он говорит, что мой LC_CTYPE должен быть WIN1252. Когда я устанавливаю LC_CTYPE в WIN1252, он говорит, что кодировка не существует.


Я трачу много времени на это, я хотел бы знать, если я на что-то здесь. Правильно ли звучит моя теория рассинхронизированных кодировок между appserver и db, или я гоняюсь за отвлекающим маневром? Если бы кто-нибудь мог помочь мне понять, как изменить этот параметр для Glassfish 2.1.1, это также было бы очень полезно. Спасибо

РЕДАКТИРОВАТЬ: Люди спрашивают, почему я храню строки в виде необработанных байтов. Это не совсем то, что я делаю, иногда необработанные байты представляют собой изображение, PDF или двоичный файл, иногда это текст. Мой тест вставляет простую текстовую строку и извлекает ее обратно, чтобы убедиться, что она правильно сохранена. Этот тест проходит на нашем CI-сервере, работающем под Linux.

EDIT2: меня попросили показать необработанный двоичный ввод и необработанный двоичный вывод.

Ожидается: [116, 104, 105, 115, 32, 105, 115, 32, 109, 121, 32, 97, 116, 116, 97, 99, 104, 109, 101, 110, 116, 32, 97, 115 , 32, 97, 32, 83, 116, 114, 105, 110, 103]

Фактический: [60, 54, 56, 54, 57, 55, 51, 50, 48, 54, 57, 55, 51, 50, 48, 54, 100, 55, 57, 50, 48, 54, 49, 55 , 52, 55, 52, 54, 49, 54, 51, 54, 56, 54, 100, 54, 53, 54, 101, 55, 52, 50, 48, 54, 49, 55, 51, 50, 48 , 54, 49, 50, 48, 53, 51, 55, 52, 55, 50, 54, 57, 54, 101, 54, 55]

Я дал этот же тест, который проверяет байты, моему коллеге, который работает на Mac, и он проходит для него.


person Daniel Kaplan    schedule 12.02.2013    source источник
comment
Используйте [String(byte[] bytes, Charset charset)](docs.oracle.com/javase/7/docs/api/java/lang/, java.nio.charset.Charset)) конструктор.   -  person jlordo    schedule 12.02.2013
comment
Действительно ли ваш массив байтов представляет текст? Если да, то почему он у вас в byte[]? Если нет, вам не следует использовать String...   -  person Jon Skeet    schedule 12.02.2013
comment
Кроме того... почему вы храните Strings в виде необработанных байтов?   -  person Brian Roach    schedule 12.02.2013
comment
@jlordo Я сделал это в своем коде new String(attachment.getData(), Charset.lookup("UTF8")), и он все равно распечатал 87474703a2f2f6269742e6c792f617948363977.   -  person Daniel Kaplan    schedule 12.02.2013
comment
Я отредактировал нижнюю часть, чтобы объяснить, почему я сохраняю строку как массив байтов.   -  person Daniel Kaplan    schedule 12.02.2013
comment
Предполагая, что тарабарщина, которую вы получаете, является фактическим выводом, который вы видите, мне кажется странным, что это будут все шестнадцатеричные цифры, а не полный двоичный мусор. Если это правда, я не уверен, что это ошибка кодировки.   -  person millimoose    schedule 12.02.2013
comment
Я не знаю, поможет ли это, но входная строка http://bit.ly/ayH69w   -  person Daniel Kaplan    schedule 12.02.2013
comment
@tieTYT: HTML или изображения?   -  person Jon Skeet    schedule 12.02.2013
comment
@tieTYT: я не могу найти Charset.lookup(String) в JavaDoc... откуда взялся этот метод?   -  person jlordo    schedule 12.02.2013
comment
@JonSkeet Это настоящая строка, которую я использую в качестве ввода.   -  person Daniel Kaplan    schedule 12.02.2013
comment
Вы имеете в виду URL? Было бы полезно взять это в кавычки. Во всяком случае, я ответил на вопрос сейчас.   -  person Jon Skeet    schedule 12.02.2013
comment
@jlordo Моя ошибка. Отладчик дал мне этот вариант в качестве метода для использования. Я изменил его на forName и получил ту же тарабарщину.   -  person Daniel Kaplan    schedule 12.02.2013


Ответы (2)


иногда необработанные байты представляют собой изображение, pdf или двоичный файл, иногда это текст

Хорошо, тогда вам не следует хранить их в виде текста.

Независимо от того, что сейчас происходит не так, даже если вы можете заставить это работать с данными, которые фактически являются текстом, у вас возникнут проблемы позже.

Если вы должны хранить произвольные двоичные данные в виде текста, вам следует использовать base64 для их кодирования, чтобы вы могли без проблем вернуться к исходному двоичному файлу. (Вам нужно только иметь возможность передавать строки ASCII, и это обычно достаточно просто.) Существует множество сторонних библиотек для Base64; Мне нравится это автономное общественное достояние.

В качестве альтернативы можно хранить данные как двоичные данные в базе данных, например. используя поле типа данных bytea. Таким образом, вам не нужно выполнять какую-либо работу по преобразованию: вы должны просто поместить его в базу данных в виде массива байтов и получить его в виде массива байтов.

РЕДАКТИРОВАТЬ: Хорошо, похоже, вы возвращаете hex представление двоичных данных, но в ASCII. Это явно странно.

person Jon Skeet    schedule 12.02.2013
comment
Извините, я не очень хорошо общаюсь. Я не храню двоичный файл как текст, я сохраняю двоичный файл как двоичный файл. Но чтобы проверить, что двоичный файл, который я вставляю, совпадает с двоичным файлом, который я извлекаю, я использую new String(...) для двоичных данных. Когда эта строка оказывается текстом, очевидно, что она вышла по-другому. В БД это bytea, а в JPA это @Lob - person Daniel Kaplan; 12.02.2013
comment
@tieTYT: вообще не преобразовывайте его в строку. Это просто затуманивает проблему (очень значительно). Почему бы вам не опубликовать точные байты до и после? Arrays.toString(byte[]) должно быть нормально для получения диагностических данных. Когда вы только имеете дело с двоичными данными, все, что вы написали о локалях и наборах символов, должно быть совершенно неуместным. - person Jon Skeet; 12.02.2013
comment
Хорошо, я отредактировал свой вопрос с этой информацией. Они выглядят как-то далеко. - person Daniel Kaplan; 13.02.2013
comment
В дополнение к этому, я только что дал этот же тест своему коллеге, который использует Mac, и тест прошел. - person Daniel Kaplan; 13.02.2013
comment
@tieTYT: похоже, вы возвращаете шестнадцатеричный код (каждый байт ввода возвращается как два байта вывода, которые декодируются как ASCII и представляют собой шестнадцатеричное представление). Можете ли вы воспроизвести это с помощью короткой, но полной программы? Ваш коллега с Mac использует тот же код и те же версии всех библиотек? - person Jon Skeet; 13.02.2013
comment
Re: мой коллега, я думаю, можно с уверенностью сказать «да». Мы можем несколько дней не синхронизироваться с исходным кодом, но у меня была эта проблема в течение нескольких месяцев, и мы все используем maven, поэтому мы должны получать одинаковые библиотеки. Моя короткая, но полная программа должна была бы получить доступ к БД для работы, потому что именно так я это воспроизвожу. Это было бы нормально? - person Daniel Kaplan; 13.02.2013
comment
@tieTYT: Да, все в порядке. Было бы неплохо попытаться воспроизвести его, в принципе. Вы выяснили, в чем проблема: в том, как пишется ваш код, или в том, как он читается? Что произойдет, если ваш коллега прочитает записанные вами данные? - person Jon Skeet; 13.02.2013
comment
Я сделал тестовый пример, который проходит для меня. pastebin.com/Ar6MfgUb Это подключение к той же базе данных с тестовой таблицей. Я думаю, это говорит о том, что в середине нашего приложения есть какой-то код, который изменяет ввод. - person Daniel Kaplan; 13.02.2013
comment
@tieTYT: Верно. Меня бы это совсем не удивило. К сожалению, это означает, что нам будет трудно помочь вам в дальнейшем :( - person Jon Skeet; 13.02.2013
comment
Джон: Все в порядке, написание этого автономного тестового примера было полезным упражнением, которое я должен был сделать раньше. Я нашел другого коллегу, тоже на Mac, у которого этот тест не проходит. - person Daniel Kaplan; 13.02.2013
comment
Проверьте мой ответ, это настоящая причина проблемы. - person Daniel Kaplan; 13.02.2013

Оказывается, это вызвано тем, что PostgreSQL работает в версии 9 по сравнению с версией 8. Большинство моих коллег использовали версию 8, но недавно у меня появился новый компьютер, поэтому я использовал последнюю версию PostgreSQL.

Вам нужно установить для output_bytea значение «escape».

Получение почти двойной длины при чтении байта [] из postgres с jpa

Однако я не нашел там достаточного ответа, но я нашел это в списке рассылки, и это решило мою проблему: http://www.postgresql.org/message-id/[email protected]

Дорогой Лист,

недавнее изменение формата bytea_output с escape на шестнадцатеричный в 9.0, по-видимому, ломает популярные Perl-модули постоянной обработки сеансов, такие как Apache::Session::Postgres, которые хранят обработанные структуры данных в столбцах bytea таблицы db. Трудно угадать основную причину по исключению, выданному указанным модулем. Проблема решается добавлением bytea_output='escape' в postgresql.conf и выполнением перезагрузки pg_ctl.

Например, в приложении RT ошибка: error: RT не удалось сохранить ваш сеанс. Это может означать, что каталог /blah/blah/foo/bar недоступен для записи или таблица базы данных отсутствует или повреждена.

С уважением, Раджеш Кумар Маллах.

person Daniel Kaplan    schedule 13.02.2013