Введение
Поскольку прошло немало времени и библиотека изменилась, то решил продолжить описание и осветить некоторые моменты. Огромное спасибо пользователям за конструктивную критику, которая надеюсь помогла повысить usability.
Первое, что хотелось бы отметить, так это то, что из основного фасадного класса XmlDataStore была убрана группа методов для работы с root объектами. Теперь разделение объектов на корневые и остальные отсутствует.
Интерфейс IXmlDataStoreIdentifiable
Поскольку, при обсуждении первой версии было правильно подмечено, что методы getId() и setId() могут быть использованы разработчиками модели данных в каких-то своих целях и необязательно они будут работать с типом java.lang.String, то название методов изменилось на getDataStoreId() и setDataStoreId(). И интерфейс соответственно выглядит следующим образом:
public interface IXmlDataStoreIdentifiable {
String getDataStoreId();
void setDataStoreId(String dataStoreId);
}
Также был добавлен абстрактный класс, реализующий этот интерфейс:
public abstract class AbstractXmlDataStoreIdentifiable
implements IXmlDataStoreIdentifiable {
private String dataStoreId;
@Override
public String getDataStoreId() {
return dataStoreId;
}
@Override
public void setDataStoreId(final String dataStoreId) {
this.dataStoreId = dataStoreId;
}
}
Наследоваться можно как от интерфейса, так и от абстрактного класса.
Формат файлов
В библиотеку введена возможность сменить формат файлов объектов на любой другой. Это делается с помощью фабрики, которая должна предоставлять writer и reader.
public interface IXmlDataStoreIOFactory {
IXmlDataStoreObjectsReader newInstanceReader(
Map<Class<? extends IXmlDataStoreIdentifiable>, XmlDataStorePolicy> policies);
IXmlDataStoreObjectsWriter newInstanceWriter(
Map<Class<? extends IXmlDataStoreIdentifiable>, XmlDataStorePolicy> policies);
}
Хорошо видно, что параметром каждого из методов является мапа политик хранения, чтобы реализации writer-а и reader-а могли определять тип хранения объекта и сохранять отдельные из объектов как простые ссылки. Интерфейсы выглядят следующим образом:
public interface IXmlDataStoreObjectsWriter {
void writeReferences(Writer writer, Collection<IXmlDataStoreIdentifiable> references)
throws XmlDataStoreIOException;
void writeObjects(Writer writer, Collection<IXmlDataStoreIdentifiable> objects)
throws XmlDataStoreIOException;
}
Класс writer должен реализовать два метода:
1) метод для сериализации ссылок writeReferences();
2) метод для сериализации объектов writeObjects().
Причем в случае возникновения каких-либо ошибок в ходе выполнения этих методов они должны выбрасывать исключение типа XmlDataStoreIOException.
public interface IXmlDataStoreObjectsReader {
Collection<IXmlDataStoreIdentifiable> readReferences(Reader reader)
throws XmlDataStoreIOException;
Collection<IXmlDataStoreIdentifiable> readObjects(Reader reader)
throws XmlDataStoreIOException;
}
Интерфейс reader-а симметричен и определяет методы для десериализации ссылок и объектов.
Выборка объектов по разным полям
Поскольку в первой версии не предоставлялась возможность выборки объектов по различным полям, то эта возможность была реализована в более поздних версиях посредством интерфейса IXmlDataStorePredicate.
public interface IXmlDataStorePredicate<T extends IXmlDataStoreIdentifiable> {
boolean passed(final T object);
}
Метод passed() должен возвращать true если объект должен быть включен в результирующую выборку. А в классе XmlDataStore реализован следующий метод:
public <T extends IXmlDataStoreIdentifiable> Map<String, T> loadObjects(
final Class<T> cl, final IXmlDataStorePredicate<T> predicate) throws XmlDataStoreReadException
Фрагментация
Как уже говорилось в предыдущей статье: «фрагментация подразумевает под собой разбиение одного файла (политика ClassObjectsFile) на фрагменты, содержащие ограниченное количество объектов, а индекс в данном случае будет содержать ссылки с указанием файла фрагмента, в котором сохранен объект». Использовать или не использовать фрагментацию настраивается при инициализации хранилища, простым указанием в конструкторе размера фрагмента в количестве объектов.
Заранее хотел бы заметить, что при фрагментации нежелательно задавать большое количество объектов, поскольку изменения в пределах одного фрагмента может осуществить лишь одна транзакция, остальные будут получать отказ до тех пор, пока транзакция начавшая изменение не будет принята или откачена.
Этот момент мне очень не нравится, поэтому в следующей реализации, скорее всего он будет переработан.
Триггеры
В последнюю версию библиотеки добавлены триггеры, которые выполняются в рамках транзакции, в которой выполнялось изменение, добавление или удаление объекта. Триггер вызывается после того, как приняты данные транзакции по модифицированному объекту, но до того момента, когда данные будут записаны на диск. Поэтому внутри триггера допускается дополнительная обработка полученного объекта и крайне нежелательна модификация объектов других классов (т.е. объектов хранящихся в другом ресурсном объекте), поскольку они могут быть уже сброшены на диск. Интерфейс триггера выглядит следующим образом:
public interface IXmlDataStoreTrigger<T extends IXmlDataStoreIdentifiable> {
XmlDataStoreTriggerType getType();
Class<T> getClazz();
void perform(T object);
}
Метод getType() должен возвращать тип операции после которой должен вызваться триггер (добавление, изменение или удаление объекта). Метод getClazz() определяет класс объектов, на которые настроен триггер. А метод perform(T object) производить какие-то операции над полученным объектом. Модификация полученного объекта допускается.
Послесловие
Последний релиз xdstore-1.5 является стабильной и отлаженной версией хранилища, которую уже можно использовать. Интерфейс меняться уже не будет. Следующие изменения будут касаться лишь внутренней скрытой от пользователя оптимизации.
Поэтому если у вас возникла необходимость и/или желание использовать, то милости прошу: xdstore-1.5.
Автор: Бесчастный Евгений
Автор: EwgenyB