feat: Implemenation of the Serialized Lob Pattern (#2795)

* #1596:Adding module for SLOB Pattern

* #1596:Adding Interface for Serializers

* #1596:Adding Objects

* #1596:Adding BLOB Based Serializer

* #1596:Implemented Clob Based Serializer

* #1596:Implemented Service for DB operations

* #1596:Implemented basic flow using Clob Serializer

* #1596:Added H2 DB Dependency

* #1596:Added Java Doc Stubs

* Updating Objects

* #1596:Completed Clob Serializer along with Tests

* #1596:Completed Slob Serializer along with Tests

* #1596:Completed Blob Serializer along with Tests

* #1596:Updated DatabaseService to handle Blob and Clob Data

* #1596:Added UML and updated README.md

* #1596:Updated data types for DB

* #1596:Code Style Formatting Cnames

* Adding Java Docs

* #1596:Reformatted as per Code Style

* #1596:Updating Java Doc

* #1596:Updating Main function to handle both Serializers

* #1596:Updated Java Docs

* #1596:Updated Java Docs

* #1596:Updated Java Docs and formatting

* #1596:Updated Java Docs and formatting

* #1596:Adding Java Docs

* #1596:Reformatted as per Code Style

* #1596:Updating Java Doc

* #1596:Updating Main function to handle both Serializers

* #1596:Updated Java Docs

* #1596:Updated Java Docs

* #1596:Updated Java Docs and formatting

* #1596:Updated Java Docs and formatting

* #1596:Updated Java Docs and formatting

* #1596:Updated Java Docs and formatting

* #1596:Updated README.md

* #1596:Updated Java Docs and README.md

* #1596:Updated Extension

* #1596:Updated README.md

* #1596:Updated Documentation as per review comments

* #1596:Updated Documentation as per review comments

* #1596: Updated README.md

* #1596:Updated Documentation as per review comments

* #1596: Added SLOB Module

---------

Co-authored-by: SHRADDHAP18 <87650482+SHRADDHAP18@users.noreply.github.com>
This commit is contained in:
Kishalay PandeyandSHRADDHAP18 authored and GitHub committed 2024-03-26 20:27:17 +02:00
1 parent be3a184445
commit b5ddd62145
14 files changed
+1563

No files matched your search

+354
View File
@@ -0,0 +1,354 @@
---
title: Serialized LOB
category: Structural
language: en
tag:
- Data access
---
## Intent
* Object models often contain complicated graphs of small objects. Much of the information in these
structures isn’t in the objects but in the links between them.
* Objects don’t have to be persisted as table rows related to each other.
* Another form of persistence is serialization, where a whole graph of objects is written out as a
single large object (LOB) in a table.
## Explanation
**In plain words**
> The Forest here represents the object graph.
> A forest contains animals and plants. As is in real life the forest object contains plants and
> animals where some animals eat other animals and some eat only plants.
> * These relationships are maintained in the Forest Object.
> * There are 2 types of Serializers available ClobSerializer and BlobSerializer.
> * ClobSerializer uses character or textual based serialization, here the Object graph is converted
> * to XML and then stored as text in DB.
> * BlobSerializer uses binary data for serialization, here the Object graph is converted to Byte
> * Array and then stored as a blob in DB.
**Programmatic Example**
* Here is the `Animal` class. It represents the Animal object in the Forest. It contains the name of
the animals in the forest and details of what they eat from the forest plants/animals or both.
```java
/**
* Creates an object Forest which contains animals and plants as its constituents. Animals may eat
* plants or other animals in the forest.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Forest implements Serializable {
private String name;
private final Set<Animal> animals = new HashSet<>();
private final Set<Plant> plants = new HashSet<>();
/**
* Provides the representation of Forest in XML form.
*
* @return XML Element
*/
public Element toXmlElement() throws ParserConfigurationException {
Document xmlDoc = getXmlDoc();
Element forestXml = xmlDoc.createElement("Forest");
forestXml.setAttribute("name", name);
Element animalsXml = xmlDoc.createElement("Animals");
for (Animal animal : animals) {
Element animalXml = animal.toXmlElement(xmlDoc);
animalsXml.appendChild(animalXml);
}
forestXml.appendChild(animalsXml);
Element plantsXml = xmlDoc.createElement("Plants");
for (Plant plant : plants) {
Element plantXml = plant.toXmlElement(xmlDoc);
plantsXml.appendChild(plantXml);
}
forestXml.appendChild(plantsXml);
return forestXml;
}
/**
* Returns XMLDoc to use for XML creation.
*
* @return XML DOC Object
* @throws ParserConfigurationException {@inheritDoc}
*/
private Document getXmlDoc() throws ParserConfigurationException {
return DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().newDocument();
}
/**
* Parses the Forest Object from the input XML Document.
*
* @param document the XML document from which the Forest is to be parsed
*/
public void createObjectFromXml(Document document) {
name = document.getDocumentElement().getAttribute("name");
NodeList nodeList = document.getElementsByTagName("*");
iterateXmlForAnimalAndPlants(nodeList, animals, plants);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("\n");
sb.append("Forest Name = ").append(name).append("\n");
sb.append("Animals found in the ").append(name).append(" Forest: \n");
for (Animal animal : animals) {
sb.append("\n--------------------------\n");
sb.append(animal.toString());
sb.append("\n--------------------------\n");
}
sb.append("\n");
sb.append("Plants in the ").append(name).append(" Forest: \n");
for (Plant plant : plants) {
sb.append("\n--------------------------\n");
sb.append(plant.toString());
sb.append("\n--------------------------\n");
}
return sb.toString();
}
}
```
* Here is the `LobSerializer` abstract class. It provides the specification to serialize and
deserialize the input object and persist and load that object into a DB.
```java
/**
* A LobSerializer can be used to create an instance of a serializer which can serialize and
* deserialize an object and persist and load that object into a DB. from their Binary
* Representation.
*/
public abstract class LobSerializer implements Serializable, Closeable {
private final transient DatabaseService databaseService;
/**
* Constructor initializes {@link LobSerializer#databaseService}.
*
* @param dataTypeDb Input provides type of Data to be stored by the Data Base Service
* @throws SQLException If any issue occurs during instantiation of DB Service or during startup.
*/
protected LobSerializer(String dataTypeDb) throws SQLException {
databaseService = new DatabaseService(dataTypeDb);
databaseService.startupService();
}
/**
* Provides the specification to Serialize the input object.
*
* @param toSerialize Input Object to serialize
* @return Serialized Object
* @throws ParserConfigurationException if any issue occurs during parsing of input object
* @throws TransformerException if any issue occurs during Transformation
* @throws IOException if any issues occur during reading object
*/
public abstract Object serialize(Forest toSerialize)
throws ParserConfigurationException, TransformerException, IOException;
/**
* Saves the object to DB with the provided ID.
*
* @param id key to be sent to DB service
* @param name Object name to store in DB
* @param object Object to store in DB
* @return ID with which the object is stored in DB
* @throws SQLException if any issue occurs while saving to DB
*/
public int persistToDb(int id, String name, Object object) throws SQLException {
databaseService.insert(id, name, object);
return id;
}
/**
* Loads the object from db using the ID and column name.
*
* @param id to query the DB
* @param columnName column from which object is to be extracted
* @return Object from DB
* @throws SQLException if any issue occurs while loading from DB
*/
public Object loadFromDb(int id, String columnName) throws SQLException {
return databaseService.select(id, columnName);
}
/**
* Provides the specification to Deserialize the input object.
*
* @param toDeserialize object to deserialize
* @return Deserialized Object
* @throws ParserConfigurationException If issue occurs during parsing of input object
* @throws IOException if any issues occur during reading object
* @throws SAXException if any issues occur during reading object for XML parsing
*/
public abstract Forest deSerialize(Object toDeserialize)
throws ParserConfigurationException, IOException, SAXException, ClassNotFoundException;
@Override
public void close() {
try {
databaseService.shutDownService();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
```
* Here is the `ClobSerializer` class. It extends the `LobSerializer` abstract class and provides the
implementation to serialize and deserialize the input object and persist and load that object into
a DB using ClobSerializer.
* Objects are serialized using character or textual based serialization
using XML to represent the object graph.
```java
/**
* Creates a Serializer that uses Character based serialization and deserialization of objects graph
* to and from XML Representation.
*/
public class ClobSerializer extends LobSerializer {
public static final String TYPE_OF_DATA_FOR_DB = "TEXT";
public ClobSerializer() {
super(TYPE_OF_DATA_FOR_DB);
}
/**
* Converts the input node to its XML String Representation.
*
* @param node XML Node that is to be converted to string
* @return String representation of XML parsed from the Node
* @throws TransformerException If any issues occur in Transformation from Node to XML
*/
private static String elementToXmlString(Element node) throws TransformerException {
StringWriter sw = new StringWriter();
Transformer t = TransformerFactory.newDefaultInstance().newTransformer();
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.transform(new DOMSource(node), new StreamResult(sw));
return sw.toString();
}
/**
* Serializes the input object graph to its XML Representation using DOM Elements.
*
* @param forest Object which is to be serialized
* @return Serialized object
* @throws ParserConfigurationException If any issues occur in parsing input object
* @throws TransformerException If any issues occur in Transformation from Node to XML
*/
@Override
public Object serialize(Forest forest) throws ParserConfigurationException, TransformerException {
Element xmlElement = forest.toXmlElement();
return elementToXmlString(xmlElement);
}
/**
* Deserializes the input XML string using DOM Parser and return its Object Graph Representation.
*
* @param toDeserialize Input Object to De-serialize
* @return Deserialized Object
* @throws ParserConfigurationException If any issues occur in parsing input object
* @throws IOException if any issues occur during reading object
* @throws SAXException If any issues occur in Transformation from Node to XML
*/
@Override
public Forest deSerialize(Object toDeserialize)
throws ParserConfigurationException, IOException, SAXException {
DocumentBuilder documentBuilder = DocumentBuilderFactory.newDefaultInstance()
.newDocumentBuilder();
var stream = new ByteArrayInputStream(toDeserialize.toString().getBytes());
Document parsed = documentBuilder.parse(stream);
Forest forest = new Forest();
forest.createObjectFromXml(parsed);
return forest;
}
}
```
* Here is the `SlobSerializer` class. It extends the `LobSerializer` abstract class and provides the
implementation to serialize and deserialize the input object and persist and load that object into
a DB using ClobSerializer.
* Objects are serialized using binary data based serialization objects a persisted as a BINARY/BLOB
in DB.
```java
/**
* Creates a Serializer that uses Binary serialization and deserialization of objects graph to and
* from their Binary Representation.
*/
public class BlobSerializer extends LobSerializer {
public static final String TYPE_OF_DATA_FOR_DB = "BINARY";
public BlobSerializer() throws SQLException {
super(TYPE_OF_DATA_FOR_DB);
}
/**
* Serializes the input object graph to its Binary Representation using Object Stream.
*
* @param toSerialize Object which is to be serialized
* @return Serialized object
* @throws IOException {@inheritDoc}
*/
@Override
public Object serialize(Forest toSerialize) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(toSerialize);
oos.close();
return new ByteArrayInputStream(baos.toByteArray());
}
/**
* Deserializes the input Byte Array Stream using Object Stream and return its Object Graph
* Representation.
*
* @param toDeserialize Input Object to De-serialize
* @return Deserialized Object
* @throws ClassNotFoundException {@inheritDoc}
* @throws IOException {@inheritDoc}
*/
@Override
public Forest deSerialize(Object toDeserialize) throws IOException, ClassNotFoundException {
InputStream bis = (InputStream) toDeserialize;
Forest forest;
try (ObjectInput in = new ObjectInputStream(bis)) {
forest = (Forest) in.readObject();
}
return forest;
}
}
```
## Class diagram
![alt text](./etc/slob.urm.png "Serialized LOB")
## Applicability
* This pattern works best when you can chop out a piece of the object model and use it to represent
the LOB.
* Think of a LOB as a way to take a bunch of objects that aren’t likely to be queried from any SQL
route outside the application.
* This graph can then be hooked into the SQL schema. Serialized LOB works poorly when you have
objects outside the LOB reference objects buried in it.
* Serialized LOB isn’t considered as often as it might be. XML makes it much more attractive since
it yields an easy-to-implement textual approach.
* Its main disadvantage is that you can’t query the structure using SQL.
* SQL extensions appear to get at XML data within a field, but that’s still not the same (or
portable).
## Credits
* [Serialized LOB](https://martinfowler.com/eaaCatalog/serializedLOB.html) by Martin Fowler