Skip to content

Commit d798a53

Browse files
committed
docs: serialized entity
1 parent ddf20b8 commit d798a53

File tree

5 files changed

+146
-160
lines changed

5 files changed

+146
-160
lines changed

serialized-entity/README.md

Lines changed: 138 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,43 @@
11
---
2-
title: Serialized Entity Pattern
3-
categories: Architectural
2+
title: Serialized Entity
3+
categories: Data access
44
language: en
55
tag:
6-
- Data access
6+
- Data access
7+
- Data transfer
8+
- Persistence
79
---
810

11+
## Also known as
12+
13+
* Object Serialization
914

1015
## Intent
1116

12-
To easily persist Java objects to the database.
17+
Enable easy conversion of Java objects to and from a serialized format, allowing them to be easily stored and transferred.
1318

1419
## Explanation
15-
Java serialization allow us to convert the object to a set of bytes. We can store these bytes into database as
16-
BLOB(binary long objects) and read them at any time and reconstruct them into Java objects.
1720

21+
Real-world example
22+
23+
> An example of the Serialized Entity design pattern in the real world can be found in the process of saving and loading game state in video games. When a player decides to save their progress, the current state of the game, including the player's position, inventory, and achievements, is serialized into a file format such as JSON or binary. This file can then be stored on the player's device or on a cloud server. When the player wants to resume their game, the serialized data is deserialized back into the game's objects, allowing the player to continue from where they left off. This mechanism ensures that complex game states can be easily saved and restored, providing a seamless gaming experience.
24+
25+
In plain words
26+
27+
> The Serialized Entity design pattern enables the conversion of Java objects to and from a serialized format for easy storage and transfer.
28+
29+
Wikipedia says
30+
31+
> In computing, serialization is the process of translating a data structure or object state into a format that can be stored (e.g. files in secondary storage devices, data buffers in primary storage devices) or transmitted (e.g. data streams over computer networks) and reconstructed later (possibly in a different computer environment). When the resulting series of bits is reread according to the serialization format, it can be used to create a semantically identical clone of the original object. For many complex objects, such as those that make extensive use of references, this process is not straightforward. Serialization of objects does not include any of their associated methods with which they were previously linked.
1832
1933
**Programmatic Example**
2034

21-
Walking through our customers example, here's the basic `Customer` entity.
35+
The Serialized Entity design pattern is a way to easily persist Java objects to the database. It uses the Serializable interface and the DAO (Data Access Object) pattern. The pattern first uses Serializable to convert a Java object into a set of bytes, then it uses the DAO pattern to store this set of bytes as a BLOB (Binary Large OBject) in the database.
36+
37+
First, we have the `Country` class, which is a simple POJO (Plain Old Java Object) that represents the data that will be serialized and stored in the database. It implements the `Serializable` interface, which means it can be converted to a byte stream and restored from it.
2238

2339
```java
40+
2441
@Getter
2542
@Setter
2643
@EqualsAndHashCode
@@ -32,190 +49,155 @@ public class Country implements Serializable {
3249
private String name;
3350
private String continents;
3451
private String language;
35-
public static final long serialVersionUID = 7149851;
36-
// Constructor ->
37-
// getters and setters ->
38-
}
52+
@Serial
53+
private static final long serialVersionUID = 7149851;
3954

55+
}
4056
```
41-
Here is `CountrySchemaSql`, this class have method allow us to serialize `Country` object and insert it into the
42-
database, also have a method that read serialized data from the database and deserialize it to `Country` object.
57+
58+
Next, we have the `CountryDao` interface, which defines the methods for persisting and retrieving `Country` objects from the database.
4359

4460
```java
45-
@Slf4j
46-
public class CountrySchemaSql {
47-
public static final String CREATE_SCHEMA_SQL = "CREATE TABLE IF NOT EXISTS WORLD (ID INT PRIMARY KEY, COUNTRY BLOB)";
48-
49-
public static final String DELETE_SCHEMA_SQL = "DROP TABLE WORLD IF EXISTS";
50-
51-
private Country country;
52-
private DataSource dataSource;
53-
54-
/**
55-
* Public constructor.
56-
*
57-
* @param dataSource datasource
58-
* @param country country
59-
*/
60-
public CountrySchemaSql(Country country, DataSource dataSource) {
61-
this.country = new Country(
62-
country.getCode(),
63-
country.getName(),
64-
country.getContinents(),
65-
country.getLanguage()
66-
);
67-
this.dataSource = dataSource;
68-
}
69-
70-
/**
71-
* This method will serialize a Country object and store it to database.
72-
* @return int type, if successfully insert a serialized object to database then return country code, else return -1.
73-
* @throws IOException if any.
74-
*/
75-
public int insertCountry() throws IOException {
76-
var sql = "INSERT INTO WORLD (ID, COUNTRY) VALUES (?, ?)";
77-
try (var connection = dataSource.getConnection();
78-
var preparedStatement = connection.prepareStatement(sql);
79-
ByteArrayOutputStream baos = new ByteArrayOutputStream();
80-
ObjectOutputStream oss = new ObjectOutputStream(baos)) {
81-
82-
oss.writeObject(country);
83-
oss.flush();
84-
85-
preparedStatement.setInt(1, country.getCode());
86-
preparedStatement.setBlob(2, new ByteArrayInputStream(baos.toByteArray()));
87-
preparedStatement.execute();
88-
return country.getCode();
89-
} catch (SQLException e) {
90-
LOGGER.info("Exception thrown " + e.getMessage());
91-
}
92-
return -1;
93-
}
94-
95-
/**
96-
* This method will select a data item from database and deserialize it.
97-
* @return int type, if successfully select and deserialized object from database then return country code,
98-
* else return -1.
99-
* @throws IOException if any.
100-
* @throws ClassNotFoundException if any.
101-
*/
102-
public int selectCountry() throws IOException, ClassNotFoundException {
103-
var sql = "SELECT ID, COUNTRY FROM WORLD WHERE ID = ?";
104-
try (var connection = dataSource.getConnection();
105-
var preparedStatement = connection.prepareStatement(sql)) {
106-
107-
preparedStatement.setInt(1, country.getCode());
108-
109-
try (ResultSet rs = preparedStatement.executeQuery()) {
110-
if (rs.next()) {
111-
Blob countryBlob = rs.getBlob("country");
112-
ByteArrayInputStream baos = new ByteArrayInputStream(countryBlob.getBytes(1, (int) countryBlob.length()));
113-
ObjectInputStream ois = new ObjectInputStream(baos);
114-
country = (Country) ois.readObject();
115-
LOGGER.info("Country: " + country);
116-
}
117-
return rs.getInt("id");
118-
}
119-
} catch (SQLException e) {
120-
LOGGER.info("Exception thrown " + e.getMessage());
121-
}
122-
return -1;
123-
}
61+
public interface CountryDao {
62+
int insertCountry() throws IOException;
12463

64+
int selectCountry() throws IOException, ClassNotFoundException;
12565
}
12666
```
12767

128-
This `App.java` will first delete a World table from the h2 database(if there is one) and create a new table called
129-
`WORLD` to ensure we have a table we want.
130-
It will then create two `Country` objects called `China` and `UnitedArabEmirates`, then create two `CountrySchemaSql`
131-
objects and use objects `China` and `UnitedArabEmirates` as arguments.
132-
Finally, call `SerializedChina.insertCountry()` and `serializedUnitedArabEmirates.insertCountry()` to serialize and
133-
store them to database, and call `SerializedChina.selectCountry()` and `serializedUnitedArabEmirates.selectCountry()`
134-
methods to read and deserialize data items to `Country` objects.
68+
The `CountrySchemaSql` class implements the `CountryDao` interface. It uses a `DataSource` to connect to the database and a `Country` object that it will serialize and store in the database.
13569

13670
```java
137-
@Slf4j
138-
public class App {
139-
private static final String DB_URL = "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1";
140-
141-
private App() {
14271

143-
}
144-
145-
/**
146-
* Program entry point.
147-
* @param args command line args.
148-
* @throws IOException if any
149-
* @throws ClassNotFoundException if any
150-
*/
151-
public static void main(String[] args) throws IOException, ClassNotFoundException {
152-
final var dataSource = createDataSource();
153-
154-
deleteSchema(dataSource);
155-
createSchema(dataSource);
156-
157-
final var China = new Country(
158-
86,
159-
"China",
160-
"Asia",
161-
"Chinese"
162-
);
163-
164-
final var UnitedArabEmirates = new Country(
165-
971,
166-
"United Arab Emirates",
167-
"Asia",
168-
"Arabic"
72+
@Slf4j
73+
public class CountrySchemaSql implements CountryDao {
74+
public static final String CREATE_SCHEMA_SQL = "CREATE TABLE IF NOT EXISTS WORLD (ID INT PRIMARY KEY, COUNTRY BLOB)";
75+
public static final String DELETE_SCHEMA_SQL = "DROP TABLE WORLD IF EXISTS";
76+
77+
private Country country;
78+
private DataSource dataSource;
79+
80+
public CountrySchemaSql(Country country, DataSource dataSource) {
81+
this.country = new Country(
82+
country.getCode(),
83+
country.getName(),
84+
country.getContinents(),
85+
country.getLanguage()
16986
);
170-
171-
final var serializedChina = new CountrySchemaSql(China, dataSource);
172-
final var serializedUnitedArabEmirates = new CountrySchemaSql(UnitedArabEmirates, dataSource);
173-
serializedChina.insertCountry();
174-
serializedUnitedArabEmirates.insertCountry();
175-
176-
serializedChina.selectCountry();
177-
serializedUnitedArabEmirates.selectCountry();
87+
this.dataSource = dataSource;
17888
}
17989

180-
private static void deleteSchema(DataSource dataSource) {
90+
@Override
91+
public int insertCountry() throws IOException {
92+
var sql = "INSERT INTO WORLD (ID, COUNTRY) VALUES (?, ?)";
18193
try (var connection = dataSource.getConnection();
182-
var statement = connection.createStatement()) {
183-
statement.execute(CountrySchemaSql.DELETE_SCHEMA_SQL);
94+
var preparedStatement = connection.prepareStatement(sql);
95+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
96+
ObjectOutputStream oss = new ObjectOutputStream(baos)) {
97+
98+
oss.writeObject(country);
99+
oss.flush();
100+
101+
preparedStatement.setInt(1, country.getCode());
102+
preparedStatement.setBlob(2, new ByteArrayInputStream(baos.toByteArray()));
103+
preparedStatement.execute();
104+
return country.getCode();
184105
} catch (SQLException e) {
185106
LOGGER.info("Exception thrown " + e.getMessage());
186107
}
108+
return -1;
187109
}
188110

189-
private static void createSchema(DataSource dataSource) {
111+
@Override
112+
public int selectCountry() throws IOException, ClassNotFoundException {
113+
var sql = "SELECT ID, COUNTRY FROM WORLD WHERE ID = ?";
190114
try (var connection = dataSource.getConnection();
191-
var statement = connection.createStatement()) {
192-
statement.execute(CountrySchemaSql.CREATE_SCHEMA_SQL);
115+
var preparedStatement = connection.prepareStatement(sql)) {
116+
117+
preparedStatement.setInt(1, country.getCode());
118+
119+
try (ResultSet rs = preparedStatement.executeQuery()) {
120+
if (rs.next()) {
121+
Blob countryBlob = rs.getBlob("country");
122+
ByteArrayInputStream baos = new ByteArrayInputStream(countryBlob.getBytes(1, (int) countryBlob.length()));
123+
ObjectInputStream ois = new ObjectInputStream(baos);
124+
country = (Country) ois.readObject();
125+
LOGGER.info("Country: " + country);
126+
}
127+
return rs.getInt("id");
128+
}
193129
} catch (SQLException e) {
194130
LOGGER.info("Exception thrown " + e.getMessage());
195131
}
132+
return -1;
196133
}
134+
}
135+
```
197136

198-
private static DataSource createDataSource() {
199-
var dataSource = new JdbcDataSource();
200-
dataSource.setURL(DB_URL);
201-
return dataSource;
202-
}
137+
Finally, in the `App` class, we create `Country` objects and `CountrySchemaSql` objects. We then call the `insertCountry` method to serialize the `Country` objects and store them in the database. We also call the `selectCountry` method to retrieve the serialized `Country` objects from the database and deserialize them back into `Country` objects.
138+
139+
```java
140+
public static void main(String[] args) throws IOException, ClassNotFoundException {
141+
final var dataSource = createDataSource();
142+
143+
deleteSchema(dataSource);
144+
createSchema(dataSource);
145+
146+
final var China = new Country(86, "China", "Asia", "Chinese");
147+
final var UnitedArabEmirates = new Country(971, "United Arab Emirates", "Asia", "Arabic");
148+
149+
final var serializedChina = new CountrySchemaSql(China, dataSource);
150+
final var serializedUnitedArabEmirates = new CountrySchemaSql(UnitedArabEmirates, dataSource);
151+
152+
serializedChina.insertCountry();
153+
serializedUnitedArabEmirates.insertCountry();
154+
155+
serializedChina.selectCountry();
156+
serializedUnitedArabEmirates.selectCountry();
203157
}
204158
```
205159

160+
This is a basic example of the Serialized Entity design pattern. It shows how to serialize Java objects, store them in a database, and then retrieve and deserialize them.
161+
206162
## Class diagram
207-
![alt_text](./etc/class_diagram.urm.png "Serialized Entity")
163+
164+
![Serialized Entity](./etc/class_diagram.urm.png "Serialized Entity")
208165

209166
## Applicability
210167

211-
Use the Serialized Entity pattern when
168+
* Use when you need to persist the state of an object or transfer objects between different tiers of an application.
169+
* Useful in scenarios where objects need to be shared over a network or saved to a file.
170+
171+
## Known Uses
172+
173+
* Java's built-in serialization mechanism using `Serializable` interface.
174+
* Storing session data in web applications.
175+
* Caching objects to improve performance.
176+
* Transferring objects in distributed systems using RMI or other RPC mechanisms.
177+
178+
## Consequences
179+
180+
Benefits:
181+
182+
* Simplifies the process of saving and restoring object state.
183+
* Facilitates object transfer across different parts of a system.
184+
* Reduces boilerplate code for manual serialization and deserialization.
185+
186+
Trade-offs:
187+
188+
* Can introduce security risks if not handled properly (e.g., deserialization attacks).
189+
* Serialized formats may not be easily readable or editable by humans.
190+
* Changes to the class structure may break compatibility with previously serialized data.
212191

213-
* You want to easily persist Java objects to the database.
192+
## Related Patterns
214193

215-
## Related patterns
216-
[Data Access Object](https://github.com/iluwatar/java-design-patterns/tree/master/dao)
194+
* [Data Transfer Object (DTO)](https://java-design-patterns.com/patterns/data-transfer-object/): Used to encapsulate data and send it over the network. Often serialized for transmission.
195+
* [Proxy](https://java-design-patterns.com/patterns/proxy/): Proxies can serialize requests to interact with remote objects.
196+
* [Memento](https://java-design-patterns.com/patterns/memento/): Provides a way to capture and restore an object's state, often using serialization.
217197

218198
## Credits
219199

220-
* [J2EE Design Patterns by William Crawford, Jonathan Kaplan](https://www.oreilly.com/library/view/j2ee-design-patterns/0596004273/re21.html)
221-
* [komminen](https://github.com/komminen/java-design-patterns) (His attempts of serialized-entity inspired me and learned a lot from his code)
200+
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
201+
* [Effective Java](https://amzn.to/4cGk2Jz)
202+
* [J2EE Design Patterns](https://amzn.to/4dpzgmx)
203+
* [Java Concurrency in Practice](https://amzn.to/4aRMruW)

serialized-entity/src/main/java/com/iluwatar/serializedentity/Country.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
* THE SOFTWARE.
2424
*/
2525
package com.iluwatar.serializedentity;
26+
import java.io.Serial;
2627
import java.io.Serializable;
2728
import lombok.AllArgsConstructor;
2829
import lombok.EqualsAndHashCode;
@@ -44,6 +45,7 @@ public class Country implements Serializable {
4445
private String name;
4546
private String continents;
4647
private String language;
47-
public static final long serialVersionUID = 7149851;
48+
@Serial
49+
private static final long serialVersionUID = 7149851;
4850

4951
}

serialized-entity/src/main/java/com/iluwatar/serializedentity/CountryDao.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2323
* THE SOFTWARE.
2424
*/
25+
2526
/**
2627
* In an application the Data Access Object (DAO) is a part of Data access layer. It is an object
2728
* that provides an interface to some type of persistence mechanism. By mapping application calls to

serialized-entity/src/test/java/com/iluwatar/serializedentity/AppTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ class AppTest {
3535

3636
/**
3737
* Issue: Add at least one assertion to this test case.
38-
*
3938
* Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
4039
* throws an exception.
4140
*/

0 commit comments

Comments
 (0)