Skip to content

Commit b5ddd62

Browse files
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 <[email protected]>
1 parent be3a184 commit b5ddd62

File tree

14 files changed

+1563
-0
lines changed

14 files changed

+1563
-0
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@
215215
<module>single-table-inheritance</module>
216216
<module>dynamic-proxy</module>
217217
<module>gateway</module>
218+
<module>slob</module>
218219
</modules>
219220
<repositories>
220221
<repository>

slob/README.md

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
---
2+
title: Serialized LOB
3+
category: Structural
4+
language: en
5+
tag:
6+
- Data access
7+
---
8+
9+
## Intent
10+
11+
* Object models often contain complicated graphs of small objects. Much of the information in these
12+
structures isn’t in the objects but in the links between them.
13+
* Objects don’t have to be persisted as table rows related to each other.
14+
* Another form of persistence is serialization, where a whole graph of objects is written out as a
15+
single large object (LOB) in a table.
16+
17+
## Explanation
18+
19+
**In plain words**
20+
21+
> The Forest here represents the object graph.
22+
> A forest contains animals and plants. As is in real life the forest object contains plants and
23+
> animals where some animals eat other animals and some eat only plants.
24+
> * These relationships are maintained in the Forest Object.
25+
> * There are 2 types of Serializers available ClobSerializer and BlobSerializer.
26+
> * ClobSerializer uses character or textual based serialization, here the Object graph is converted
27+
> * to XML and then stored as text in DB.
28+
> * BlobSerializer uses binary data for serialization, here the Object graph is converted to Byte
29+
> * Array and then stored as a blob in DB.
30+
31+
**Programmatic Example**
32+
33+
* Here is the `Animal` class. It represents the Animal object in the Forest. It contains the name of
34+
the animals in the forest and details of what they eat from the forest plants/animals or both.
35+
36+
```java
37+
/**
38+
* Creates an object Forest which contains animals and plants as its constituents. Animals may eat
39+
* plants or other animals in the forest.
40+
*/
41+
@Data
42+
@NoArgsConstructor
43+
@AllArgsConstructor
44+
public class Forest implements Serializable {
45+
46+
private String name;
47+
private final Set<Animal> animals = new HashSet<>();
48+
private final Set<Plant> plants = new HashSet<>();
49+
50+
/**
51+
* Provides the representation of Forest in XML form.
52+
*
53+
* @return XML Element
54+
*/
55+
public Element toXmlElement() throws ParserConfigurationException {
56+
Document xmlDoc = getXmlDoc();
57+
58+
Element forestXml = xmlDoc.createElement("Forest");
59+
forestXml.setAttribute("name", name);
60+
61+
Element animalsXml = xmlDoc.createElement("Animals");
62+
for (Animal animal : animals) {
63+
Element animalXml = animal.toXmlElement(xmlDoc);
64+
animalsXml.appendChild(animalXml);
65+
}
66+
forestXml.appendChild(animalsXml);
67+
68+
Element plantsXml = xmlDoc.createElement("Plants");
69+
for (Plant plant : plants) {
70+
Element plantXml = plant.toXmlElement(xmlDoc);
71+
plantsXml.appendChild(plantXml);
72+
}
73+
forestXml.appendChild(plantsXml);
74+
return forestXml;
75+
}
76+
77+
/**
78+
* Returns XMLDoc to use for XML creation.
79+
*
80+
* @return XML DOC Object
81+
* @throws ParserConfigurationException {@inheritDoc}
82+
*/
83+
private Document getXmlDoc() throws ParserConfigurationException {
84+
return DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().newDocument();
85+
}
86+
87+
/**
88+
* Parses the Forest Object from the input XML Document.
89+
*
90+
* @param document the XML document from which the Forest is to be parsed
91+
*/
92+
public void createObjectFromXml(Document document) {
93+
name = document.getDocumentElement().getAttribute("name");
94+
NodeList nodeList = document.getElementsByTagName("*");
95+
iterateXmlForAnimalAndPlants(nodeList, animals, plants);
96+
}
97+
98+
@Override
99+
public String toString() {
100+
StringBuilder sb = new StringBuilder("\n");
101+
sb.append("Forest Name = ").append(name).append("\n");
102+
sb.append("Animals found in the ").append(name).append(" Forest: \n");
103+
for (Animal animal : animals) {
104+
sb.append("\n--------------------------\n");
105+
sb.append(animal.toString());
106+
sb.append("\n--------------------------\n");
107+
}
108+
sb.append("\n");
109+
sb.append("Plants in the ").append(name).append(" Forest: \n");
110+
for (Plant plant : plants) {
111+
sb.append("\n--------------------------\n");
112+
sb.append(plant.toString());
113+
sb.append("\n--------------------------\n");
114+
}
115+
return sb.toString();
116+
}
117+
}
118+
```
119+
120+
* Here is the `LobSerializer` abstract class. It provides the specification to serialize and
121+
deserialize the input object and persist and load that object into a DB.
122+
123+
```java
124+
/**
125+
* A LobSerializer can be used to create an instance of a serializer which can serialize and
126+
* deserialize an object and persist and load that object into a DB. from their Binary
127+
* Representation.
128+
*/
129+
public abstract class LobSerializer implements Serializable, Closeable {
130+
131+
private final transient DatabaseService databaseService;
132+
133+
/**
134+
* Constructor initializes {@link LobSerializer#databaseService}.
135+
*
136+
* @param dataTypeDb Input provides type of Data to be stored by the Data Base Service
137+
* @throws SQLException If any issue occurs during instantiation of DB Service or during startup.
138+
*/
139+
protected LobSerializer(String dataTypeDb) throws SQLException {
140+
databaseService = new DatabaseService(dataTypeDb);
141+
databaseService.startupService();
142+
}
143+
144+
/**
145+
* Provides the specification to Serialize the input object.
146+
*
147+
* @param toSerialize Input Object to serialize
148+
* @return Serialized Object
149+
* @throws ParserConfigurationException if any issue occurs during parsing of input object
150+
* @throws TransformerException if any issue occurs during Transformation
151+
* @throws IOException if any issues occur during reading object
152+
*/
153+
public abstract Object serialize(Forest toSerialize)
154+
throws ParserConfigurationException, TransformerException, IOException;
155+
156+
/**
157+
* Saves the object to DB with the provided ID.
158+
*
159+
* @param id key to be sent to DB service
160+
* @param name Object name to store in DB
161+
* @param object Object to store in DB
162+
* @return ID with which the object is stored in DB
163+
* @throws SQLException if any issue occurs while saving to DB
164+
*/
165+
public int persistToDb(int id, String name, Object object) throws SQLException {
166+
databaseService.insert(id, name, object);
167+
return id;
168+
}
169+
170+
/**
171+
* Loads the object from db using the ID and column name.
172+
*
173+
* @param id to query the DB
174+
* @param columnName column from which object is to be extracted
175+
* @return Object from DB
176+
* @throws SQLException if any issue occurs while loading from DB
177+
*/
178+
public Object loadFromDb(int id, String columnName) throws SQLException {
179+
return databaseService.select(id, columnName);
180+
}
181+
182+
/**
183+
* Provides the specification to Deserialize the input object.
184+
*
185+
* @param toDeserialize object to deserialize
186+
* @return Deserialized Object
187+
* @throws ParserConfigurationException If issue occurs during parsing of input object
188+
* @throws IOException if any issues occur during reading object
189+
* @throws SAXException if any issues occur during reading object for XML parsing
190+
*/
191+
public abstract Forest deSerialize(Object toDeserialize)
192+
throws ParserConfigurationException, IOException, SAXException, ClassNotFoundException;
193+
194+
@Override
195+
public void close() {
196+
try {
197+
databaseService.shutDownService();
198+
} catch (SQLException e) {
199+
throw new RuntimeException(e);
200+
}
201+
}
202+
}
203+
```
204+
205+
* Here is the `ClobSerializer` class. It extends the `LobSerializer` abstract class and provides the
206+
implementation to serialize and deserialize the input object and persist and load that object into
207+
a DB using ClobSerializer.
208+
* Objects are serialized using character or textual based serialization
209+
using XML to represent the object graph.
210+
211+
```java
212+
213+
/**
214+
* Creates a Serializer that uses Character based serialization and deserialization of objects graph
215+
* to and from XML Representation.
216+
*/
217+
public class ClobSerializer extends LobSerializer {
218+
219+
public static final String TYPE_OF_DATA_FOR_DB = "TEXT";
220+
221+
public ClobSerializer() {
222+
super(TYPE_OF_DATA_FOR_DB);
223+
}
224+
225+
/**
226+
* Converts the input node to its XML String Representation.
227+
*
228+
* @param node XML Node that is to be converted to string
229+
* @return String representation of XML parsed from the Node
230+
* @throws TransformerException If any issues occur in Transformation from Node to XML
231+
*/
232+
private static String elementToXmlString(Element node) throws TransformerException {
233+
StringWriter sw = new StringWriter();
234+
Transformer t = TransformerFactory.newDefaultInstance().newTransformer();
235+
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
236+
t.setOutputProperty(OutputKeys.INDENT, "yes");
237+
t.transform(new DOMSource(node), new StreamResult(sw));
238+
return sw.toString();
239+
}
240+
241+
/**
242+
* Serializes the input object graph to its XML Representation using DOM Elements.
243+
*
244+
* @param forest Object which is to be serialized
245+
* @return Serialized object
246+
* @throws ParserConfigurationException If any issues occur in parsing input object
247+
* @throws TransformerException If any issues occur in Transformation from Node to XML
248+
*/
249+
@Override
250+
public Object serialize(Forest forest) throws ParserConfigurationException, TransformerException {
251+
Element xmlElement = forest.toXmlElement();
252+
return elementToXmlString(xmlElement);
253+
}
254+
255+
/**
256+
* Deserializes the input XML string using DOM Parser and return its Object Graph Representation.
257+
*
258+
* @param toDeserialize Input Object to De-serialize
259+
* @return Deserialized Object
260+
* @throws ParserConfigurationException If any issues occur in parsing input object
261+
* @throws IOException if any issues occur during reading object
262+
* @throws SAXException If any issues occur in Transformation from Node to XML
263+
*/
264+
@Override
265+
public Forest deSerialize(Object toDeserialize)
266+
throws ParserConfigurationException, IOException, SAXException {
267+
DocumentBuilder documentBuilder = DocumentBuilderFactory.newDefaultInstance()
268+
.newDocumentBuilder();
269+
var stream = new ByteArrayInputStream(toDeserialize.toString().getBytes());
270+
Document parsed = documentBuilder.parse(stream);
271+
Forest forest = new Forest();
272+
forest.createObjectFromXml(parsed);
273+
return forest;
274+
}
275+
}
276+
```
277+
278+
* Here is the `SlobSerializer` class. It extends the `LobSerializer` abstract class and provides the
279+
implementation to serialize and deserialize the input object and persist and load that object into
280+
a DB using ClobSerializer.
281+
* Objects are serialized using binary data based serialization objects a persisted as a BINARY/BLOB
282+
in DB.
283+
284+
```java
285+
/**
286+
* Creates a Serializer that uses Binary serialization and deserialization of objects graph to and
287+
* from their Binary Representation.
288+
*/
289+
public class BlobSerializer extends LobSerializer {
290+
291+
public static final String TYPE_OF_DATA_FOR_DB = "BINARY";
292+
293+
public BlobSerializer() throws SQLException {
294+
super(TYPE_OF_DATA_FOR_DB);
295+
}
296+
297+
/**
298+
* Serializes the input object graph to its Binary Representation using Object Stream.
299+
*
300+
* @param toSerialize Object which is to be serialized
301+
* @return Serialized object
302+
* @throws IOException {@inheritDoc}
303+
*/
304+
@Override
305+
public Object serialize(Forest toSerialize) throws IOException {
306+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
307+
ObjectOutputStream oos = new ObjectOutputStream(baos);
308+
oos.writeObject(toSerialize);
309+
oos.close();
310+
return new ByteArrayInputStream(baos.toByteArray());
311+
}
312+
313+
/**
314+
* Deserializes the input Byte Array Stream using Object Stream and return its Object Graph
315+
* Representation.
316+
*
317+
* @param toDeserialize Input Object to De-serialize
318+
* @return Deserialized Object
319+
* @throws ClassNotFoundException {@inheritDoc}
320+
* @throws IOException {@inheritDoc}
321+
*/
322+
@Override
323+
public Forest deSerialize(Object toDeserialize) throws IOException, ClassNotFoundException {
324+
InputStream bis = (InputStream) toDeserialize;
325+
Forest forest;
326+
try (ObjectInput in = new ObjectInputStream(bis)) {
327+
forest = (Forest) in.readObject();
328+
}
329+
return forest;
330+
}
331+
}
332+
```
333+
334+
## Class diagram
335+
336+
![alt text](./etc/slob.urm.png "Serialized LOB")
337+
338+
## Applicability
339+
340+
* This pattern works best when you can chop out a piece of the object model and use it to represent
341+
the LOB.
342+
* Think of a LOB as a way to take a bunch of objects that aren’t likely to be queried from any SQL
343+
route outside the application.
344+
* This graph can then be hooked into the SQL schema. Serialized LOB works poorly when you have
345+
objects outside the LOB reference objects buried in it.
346+
* Serialized LOB isn’t considered as often as it might be. XML makes it much more attractive since
347+
it yields an easy-to-implement textual approach.
348+
* Its main disadvantage is that you can’t query the structure using SQL.
349+
* SQL extensions appear to get at XML data within a field, but that’s still not the same (or
350+
portable).
351+
352+
## Credits
353+
354+
* [Serialized LOB](https://martinfowler.com/eaaCatalog/serializedLOB.html) by Martin Fowler

slob/etc/slob.urm.png

39.1 KB
Loading

0 commit comments

Comments
 (0)