Сериализация самодельных и Java

У меня есть определенный POJO, который необходимо сохранить в базе данных, текущий дизайн определяет его поле в виде одного строкового столбца, и добавление дополнительных полей в таблицу не вариант.

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

Итак, я попробовал базовую сериализацию Java, но из базового теста, который я провел, это почему-то становится очень дорогостоящей операцией (создание ByteArrayOutputStream, ObjectOutputStream и т. Д., То же самое для десериализации).

Так какие у меня варианты? Каков предпочтительный способ сериализации объектов в базе данных?

Изменить: это будет очень распространенная операция в моем проекте, поэтому накладные расходы должны быть сведены к минимуму, а производительность имеет решающее значение. Кроме того, сторонние решения хороши, но не имеют значения (и обычно приводят к накладным расходам, которых я стараюсь избегать)

14 ответов

Решение

Эллиот Расти Гарольд написал хороший аргумент против использования сериализации Java Object для объектов в своей библиотеке XOM. Те же принципы применяются к вам. Встроенная Java-сериализация является специфичной для Java, хрупкой и медленной, поэтому ее лучше избегать.

Вы имеете примерно правильную идею в использовании формата на основе строк. Проблема, как вы заявляете, заключается в том, что вы сталкиваетесь с проблемами форматирования / синтаксиса с разделителями. Решение состоит в том, чтобы использовать формат, который уже создан, чтобы справиться с этим. Если это стандартизированный формат, то вы также можете использовать другие библиотеки / языки для управления им. Кроме того, строковый формат означает, что у вас есть надежда понять это, просто взглянув на данные; двоичные форматы удаляют эту опцию.

XML и JSON - два отличных варианта; они стандартизированы, основаны на тексте, гибки, удобочитаемы и имеют много библиотечной поддержки. Они также будут работать на удивление хорошо (иногда даже быстрее, чем сериализация Java).

Вы можете попробовать Protocol Buffers, это проект с открытым исходным кодом от Google, он называется быстрым (генерирует более короткую сериализованную форму, чем XML, и работает быстрее). Он также аккуратно обрабатывает добавление нового поля (вставляет значения по умолчанию).

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

Итак, решения, приведенные выше, которые включают сериализацию в пары имя / значение, - это подход, который вы, вероятно, захотите использовать.

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

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

XStream или YAML или OGNL приходят на ум как простые методы сериализации. XML был наиболее распространенным, но OGNL обеспечивает наибольшую гибкость при минимальном количестве метаданных.

Рассмотрите возможность помещения данных в Properties возражать и использовать его load()/store() сериализации. Это текстовая техника, поэтому она по-прежнему читается в базе данных:

public String getFieldsAsString() {
  Properties data = new Properties();
  data.setProperty( "foo", this.getFoo() );
  data.setProperty( "bar", this.getBar() );
  ...
  ByteArrayOutputStream out = new ByteArrayOutputStream();
  data.store( out, "" );
  return new String( out.toByteArray(), "8859-1" );   //store() always uses this encoding
}

Чтобы загрузить из строки, сделайте подобное, используя новый Properties объект и load() данные.

Это лучше, чем Java-сериализация, потому что она очень удобочитаема и компактна.

Если вам нужна поддержка различных типов данных (т.е. не только String), используйте BeanUtils для преобразования каждого поля в строковое представление и из него.

Я бы сказал, что ваш первоначальный подход не так уж и плох, если ваш POJO состоит из строк и примитивных типов. Вы можете принудительно экранировать разделитель, чтобы предотвратить искажения. Также, если вы используете Hibernate, вы инкапсулируете сериализацию в пользовательский тип.

Если вы не возражаете против другой зависимости, Гессиан, предположительно, является более эффективным способом сериализации объектов Java.

Как насчет стандартного механизма персистентности JavaBeans:

java.beans.XMLEncoder
java.beans.XMLDecoder

Они способны создавать Java POJO из XML (которые были сохранены в XML). По памяти это выглядит (что-то) как...

<object class="java.util.HashMap">
    <void method="put">
        <string>Hello</string>
        <float>1</float>
    </void>
</object>

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

Если вы используете разделитель, вы можете использовать символ, который, как вы знаете, никогда не встречается в вашем тексте, такой как \0, или специальные символы http://unicode.org/charts/symbols.html

Однако время, потраченное на отправку данных в базу данных и ее сохранение, вероятно, будет намного больше, чем стоимость сериализации. Поэтому я бы предложил начать с чего-то простого и удобного для чтения (например, XStream), посмотреть, где ваше приложение тратит большую часть своего времени, и оптимизировать его.

Вы можете оптимизировать сериализацию, выполняя внешний вид вашего объекта. Это даст вам полный контроль над сериализацией и улучшит производительность процесса. Это просто сделать, если ваш POJO прост (т.е. не имеет ссылок на другие объекты), иначе вы можете легко прервать сериализацию.

учебник здесь

РЕДАКТИРОВАТЬ: не подразумевает, что это предпочтительный подход, но вы очень ограничены в своих вариантах, если это критично для производительности, и вы можете использовать только строковый столбец в таблице.

Рассмотрите возможность изменения вашей схемы. Даже если вы найдете быстрый способ сериализации POJO в строку, как вы обрабатываете разные версии? Как вы переносите базу данных из X->Y? Или хуже от A->D? У меня возникают проблемы, когда мы сохраняем сериализованный объект в поле BLOB и вынуждены переносить клиента по нескольким версиям.

Вы смотрели в JAXB? Это механизм, с помощью которого вы можете определить набор java-объектов, которые создаются из XML-схемы. Это позволяет вам выполнить маршализацию из иерархии объектов в XML или демонтировать XML обратно в иерархию объектов.

У меня есть определенный POJO, который необходимо сохранить в базе данных, текущий дизайн определяет его поле в виде одного строкового столбца, и добавление дополнительных полей в таблицу не вариант.

Не могли бы вы создать новую таблицу и вставить внешний ключ в этот столбец!?!?:) Я подозреваю, что нет, но давайте рассмотрим все основы!

Сериализация: у нас недавно было это обсуждение, чтобы в случае сбоя нашего приложения мы могли воскресить его в том же состоянии, что и ранее. По сути, мы отправляем событие персистентности в очередь, а затем это захватывает объект, блокирует его и затем сериализует его. Это кажется довольно быстрым. Сколько данных вы сериализуете? Можете ли вы сделать какие-либо переменные (например, кэшированные переменные)? Можете ли вы рассмотреть возможность разделения вашей сериализации? Остерегайтесь: что произойдет, если ваши объекты изменятся (блокировка) или классы изменятся (другой идентификатор сериализации)? Вам нужно обновить все, что сериализовано, до последних классов. Возможно, вам нужно хранить это только на ночь, чтобы это не имело значения?

XML: Вы можете использовать что-то вроде xstream для достижения этой цели. Создание чего-то нестандартного выполнимо (хороший вопрос для интервью!), Но я бы, наверное, не сделал это сам. Зачем беспокоиться? Помните, если у вас есть циклические ссылки или если у вас есть ссылки на объекты более одного раза. Восстановление объектов не так тривиально.

Хранилище базы данных: если вы используете Oracle 10g для хранения больших двоичных объектов, обновитесь до последней версии, поскольку производительность c/blob значительно возросла. Если мы говорим о больших объемах данных, то возможно сжатый поток данных?

Это приложение в реальном времени, или будут две или две паузы, где вы можете безопасно сохранить реальный объект? Если у вас есть время, вы можете клонировать его, а затем сохранить клон в другом потоке. Для чего настойчивость? Это важно, что это сделано внутри транзакции?

Вы можете попробовать Preon. Preon стремится быть двоично-закодированными данными, как Hibernate для реляционных баз данных, а JAXB - XML.

Я предложу использовать JAXB или XStream (первое быстрее, второе больше фокусируется на части сериализации объектов). Кроме того, я дополнительно предложу достойную альтернативу на основе JSON, Джексон ( http://jackson.codehaus.org/Tutorial), которая может полностью сериализовать / десериализовать компоненты в текст JSON для хранения в столбце.

Да, и я абсолютно согласен с тем, что не используйте двоичную сериализацию Java ни при каких обстоятельствах для долговременного хранения данных. То же самое касается протокольных буферов; оба являются слишком хрупкими для этой цели (они лучше для передачи данных между системами с жесткой связью).

Другие вопросы по тегам