The first post on the topic:
Multi-file storage of Java objects in xml format (part 1)Introduction
Since a lot of time has passed and the library has changed, I decided to continue the description and highlight some points. Many thanks to users for constructive criticism, which I hope helped to improve usability.
The first thing I would like to note is that the group of methods for working with root objects has been removed from the main XmlDataStore front class. Now the division of objects into root and others is absent.
IXmlDataStoreIdentifiable interface
Since, when discussing the first version, it was correctly noticed that the getId () and setId () methods can be used by data model developers for some of their own purposes and they will not necessarily work with the java.lang.String type, the method name has changed to getDataStoreId () and setDataStoreId (). And the interface accordingly looks like this:
public interface IXmlDataStoreIdentifiable { String getDataStoreId(); void setDataStoreId(String dataStoreId); }
An abstract class has also been added that implements this interface:
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; } }
It is possible to inherit from both the interface and the abstract class.
')
File format
The library introduced the ability to change the file format of objects to any other. This is done using a factory that should provide the writer and reader.
public interface IXmlDataStoreIOFactory { IXmlDataStoreObjectsReader newInstanceReader( Map<Class<? extends IXmlDataStoreIdentifiable>, XmlDataStorePolicy> policies); IXmlDataStoreObjectsWriter newInstanceWriter( Map<Class<? extends IXmlDataStoreIdentifiable>, XmlDataStorePolicy> policies); }
It is clearly seen that the parameter of each of the methods is the storage policies map, so that the implementers of the writer and reader can determine the type of object storage and save individual objects as simple links. Interfaces are as follows:
public interface IXmlDataStoreObjectsWriter { void writeReferences(Writer writer, Collection<IXmlDataStoreIdentifiable> references) throws XmlDataStoreIOException; void writeObjects(Writer writer, Collection<IXmlDataStoreIdentifiable> objects) throws XmlDataStoreIOException; }
The class writer must implement two methods:
1) a method to serialize writeReferences ();
2) a method for serializing writeObjects () objects.
Moreover, in the event of any errors in the execution of these methods, they should throw an exception of type XmlDataStoreIOException.
public interface IXmlDataStoreObjectsReader { Collection<IXmlDataStoreIdentifiable> readReferences(Reader reader) throws XmlDataStoreIOException; Collection<IXmlDataStoreIdentifiable> readObjects(Reader reader) throws XmlDataStoreIOException; }
The reader interface is symmetrical and defines methods for deserializing links and objects.
Sampling objects by different fields
Since the first version did not provide the ability to select objects from different fields, this feature was implemented in later versions via the IXmlDataStorePredicate interface.
public interface IXmlDataStorePredicate<T extends IXmlDataStoreIdentifiable> { boolean passed(final T object); }
The passed () method must return true if the object is to be included in the result set. And in the XmlDataStore class the following method is implemented:
public <T extends IXmlDataStoreIdentifiable> Map<String, T> loadObjects( final Class<T> cl, final IXmlDataStorePredicate<T> predicate) throws XmlDataStoreReadException
Fragmentation
As mentioned in the previous article: “Fragmentation means breaking up a single file (the ClassObjectsFile policy) into fragments containing a limited number of objects, and the index in this case will contain links indicating the fragment file in which the object is stored.” To use or not to use fragmentation is configured during storage initialization, by simply specifying the fragment size in the number of objects in the constructor.
I would like to note in advance that during fragmentation it is undesirable to set a large number of objects, since only one transaction can make changes within one fragment, the rest will be refused until the transaction that started the change is accepted or rolled back.
I really do not like this moment, so in the next implementation, it will most likely be reworked.
Triggers
Triggers are added to the latest version of the library; they are executed as part of the transaction in which the object was modified, added or deleted. The trigger is called after the transaction data on the modified object has been received, but until the moment when the data will be written to the disk. Therefore, inside the trigger, additional processing of the obtained object is allowed and it is highly undesirable to modify objects of other classes (that is, objects stored in another resource object), since they may already be flushed to disk. The trigger interface is as follows:
public interface IXmlDataStoreTrigger<T extends IXmlDataStoreIdentifiable> { XmlDataStoreTriggerType getType(); Class<T> getClazz(); void perform(T object); }
The getType () method should return the type of operation after which the trigger should be called (add, change, or delete an object). The getClazz () method determines the class of objects to which the trigger is configured. And the perform (T object) method to perform some operations on the received object. Modification of the received object is allowed.
Afterword
The latest release of xdstore-1.5 is a stable and streamlined version of the repository, which can already be used. The interface will not change anymore. The following changes will concern only the internal optimization hidden from the user.
Therefore, if you have a need and / or desire to use, then you are
welcome :
xdstore-1.5 .
Author: Beschastny Evgeny