Repository: iluwatar/java-design-patterns
Issue: Serialized Entity pattern
Labels: epic: pattern
type: feature

https://www.oreilly.com/library/view/j2ee-design-patterns/0596004273/re21.html

Issue comments:
Hi iluwatar, I'd like to volunteer to take a crack at this and try to fix in November/December if you're okay with that.
I'm happy to hear you're interested @komminen. You're welcome to take a shot!
Can I take this?
Ok, you're assigned @JurenXu 
> Ok, you're assigned @JurenXu

Thank you! I will start working on this

> Ok, you're assigned @JurenXu

Hi, Iluwatar.
I got a question, do I have to use the h2 database, or can I use MySQL instead?
@JurenXu I would use h2 by default. If you want, you can add a configuration option to use MySQL. This is because people want to run the example quickly and easily and probably most people don't want to setup the MySQL server for this.

Pull request review comments:
Please check the updated YAML frontmatter requirements in https://github.com/iluwatar/java-design-patterns/wiki/01.-How-to-contribute
Describe the pattern and explain how this example implements it
We need comments explaining what is going on
Use logger
Use logger
> Use logger

I have resolved all changes you required! Could you check it again?

Patch that resolved the issue:
diff --git a/pom.xml b/pom.xml
index 7ed97bcae754..c0ead428cba0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -215,6 +215,7 @@
     <module>composite-view</module>
     <module>metadata-mapping</module>
     <module>service-to-worker</module>
+    <module>serialized-entity</module>
     <module>identity-map</module>
   </modules>
   <repositories>
diff --git a/serialized-entity/README.md b/serialized-entity/README.md
new file mode 100644
index 000000000000..a0a3853f9c63
--- /dev/null
+++ b/serialized-entity/README.md
@@ -0,0 +1,221 @@
+---
+title: Serialized Entity Pattern
+categories: Architectural
+language: en
+tags:
+ - Data access
+---
+
+
+## Intent
+
+To easily persist Java objects to the database.
+
+## Explanation
+Java serialization allow us to convert the object to a set of bytes. We can store these bytes into database as 
+BLOB(binary long objects) and read them at any time and reconstruct them into Java objects.
+
+
+**Programmatic Example**
+
+Walking through our customers example, here's the basic `Customer` entity.
+
+```java
+@Getter
+@Setter
+@EqualsAndHashCode
+@ToString
+@AllArgsConstructor
+public class Country implements Serializable {
+
+    private int code;
+    private String name;
+    private String continents;
+    private String language;
+    public static final long serialVersionUID = 7149851;
+    // Constructor ->
+    // getters and setters -> 
+}
+
+```
+Here is `CountrySchemaSql`, this class have method allow us to serialize `Country` object and insert it into the 
+database, also have a method that read serialized data from the database and deserialize it to `Country` object. 
+
+```java
+@Slf4j
+public class CountrySchemaSql {
+  public static final String CREATE_SCHEMA_SQL = "CREATE TABLE IF NOT EXISTS WORLD (ID INT PRIMARY KEY, COUNTRY BLOB)";
+
+  public static final String DELETE_SCHEMA_SQL = "DROP TABLE WORLD IF EXISTS";
+
+  private Country country;
+  private DataSource dataSource;
+
+  /**
+   * Public constructor.
+   *
+   * @param dataSource datasource
+   * @param country country
+   */
+  public CountrySchemaSql(Country country, DataSource dataSource) {
+    this.country = new Country(
+            country.getCode(),
+            country.getName(),
+            country.getContinents(),
+            country.getLanguage()
+    );
+    this.dataSource = dataSource;
+  }
+
+  /**
+   * This method will serialize a Country object and store it to database.
+   * @return int type, if successfully insert a serialized object to database then return country code, else return -1.
+   * @throws IOException if any.
+   */
+  public int insertCountry() throws IOException {
+    var sql = "INSERT INTO WORLD (ID, COUNTRY) VALUES (?, ?)";
+    try (var connection = dataSource.getConnection();
+         var preparedStatement = connection.prepareStatement(sql);
+         ByteArrayOutputStream baos = new ByteArrayOutputStream();
+         ObjectOutputStream oss = new ObjectOutputStream(baos)) {
+
+      oss.writeObject(country);
+      oss.flush();
+
+      preparedStatement.setInt(1, country.getCode());
+      preparedStatement.setBlob(2, new ByteArrayInputStream(baos.toByteArray()));
+      preparedStatement.execute();
+      return country.getCode();
+    } catch (SQLException e) {
+      LOGGER.info("Exception thrown " + e.getMessage());
+    }
+    return -1;
+  }
+
+  /**
+   * This method will select a data item from database and deserialize it.
+   * @return int type, if successfully select and deserialized object from database then return country code,
+   *     else return -1.
+   * @throws IOException if any.
+   * @throws ClassNotFoundException if any.
+   */
+  public int selectCountry() throws IOException, ClassNotFoundException {
+    var sql = "SELECT ID, COUNTRY FROM WORLD WHERE ID = ?";
+    try (var connection = dataSource.getConnection();
+         var preparedStatement = connection.prepareStatement(sql)) {
+
+      preparedStatement.setInt(1, country.getCode());
+
+      try (ResultSet rs = preparedStatement.executeQuery()) {
+        if (rs.next()) {
+          Blob countryBlob = rs.getBlob("country");
+          ByteArrayInputStream baos = new ByteArrayInputStream(countryBlob.getBytes(1, (int) countryBlob.length()));
+          ObjectInputStream ois = new ObjectInputStream(baos);
+          country = (Country) ois.readObject();
+          LOGGER.info("Country: " + country);
+        }
+        return rs.getInt("id");
+      }
+    } catch (SQLException e) {
+      LOGGER.info("Exception thrown " + e.getMessage());
+    }
+    return -1;
+  }
+
+}
+```
+
+This `App.java` will first delete a World table from the h2 database(if there is one) and create a new table called 
+`WORLD` to ensure we have a table we want. 
+It will then create two `Country` objects called `China` and `UnitedArabEmirates`, then create two `CountrySchemaSql` 
+objects and use objects `China` and  `UnitedArabEmirates` as arguments. 
+Finally, call `SerializedChina.insertCountry()` and `serializedUnitedArabEmirates.insertCountry()` to serialize and 
+store them to database, and call `SerializedChina.selectCountry()` and `serializedUnitedArabEmirates.selectCountry()`
+methods to read and deserialize data items to `Country` objects.
+
+```java
+@Slf4j
+public class App {
+    private static final String DB_URL = "jdbc:h2:~/test";
+
+    private App() {
+
+    }
+
+    /**
+     * Program entry point.
+     * @param args command line args.
+     * @throws IOException if any
+     * @throws ClassNotFoundException if any
+     */
+    public static void main(String[] args) throws IOException, ClassNotFoundException {
+        final var dataSource = createDataSource();
+
+        deleteSchema(dataSource);
+        createSchema(dataSource);
+
+        final var China = new Country(
+                86,
+                "China",
+                "Asia",
+                "Chinese"
+        );
+
+        final var UnitedArabEmirates = new Country(
+                971,
+                "United Arab Emirates",
+                "Asia",
+                "Arabic"
+        );
+
+        final var serializedChina = new CountrySchemaSql(China, dataSource);
+        final var serializedUnitedArabEmirates = new CountrySchemaSql(UnitedArabEmirates, dataSource);
+        serializedChina.insertCountry();
+        serializedUnitedArabEmirates.insertCountry();
+
+        serializedChina.selectCountry();
+        serializedUnitedArabEmirates.selectCountry();
+    }
+
+    private static void deleteSchema(DataSource dataSource) {
+        try (var connection = dataSource.getConnection();
+             var statement = connection.createStatement()) {
+            statement.execute(CountrySchemaSql.DELETE_SCHEMA_SQL);
+        } catch (SQLException e) {
+            LOGGER.info("Exception thrown " + e.getMessage());
+        }
+    }
+
+    private static void createSchema(DataSource dataSource) {
+        try (var connection = dataSource.getConnection();
+             var statement = connection.createStatement()) {
+            statement.execute(CountrySchemaSql.CREATE_SCHEMA_SQL);
+        } catch (SQLException e) {
+            LOGGER.info("Exception thrown " + e.getMessage());
+        }
+    }
+
+    private static DataSource createDataSource() {
+        var dataSource = new JdbcDataSource();
+        dataSource.setURL(DB_URL);
+        return dataSource;
+    }
+}
+```
+
+## Class diagram
+![alt_text](./etc/class_diagram.urm.png "Serialized Entity")
+
+## Applicability
+
+Use the Serialized Entity pattern when
+
+* You want to easily persist Java objects to the database.
+
+## Related patterns
+[Data Access Object](https://github.com/iluwatar/java-design-patterns/tree/master/dao)
+
+## Credits
+
+* [J2EE Design Patterns by William Crawford, Jonathan Kaplan](https://www.oreilly.com/library/view/j2ee-design-patterns/0596004273/re21.html)
+* [komminen](https://github.com/komminen/java-design-patterns) (His attempts of serialized-entity inspired me and learned a lot from his code)
diff --git a/serialized-entity/etc/serialize-entity.urm.puml b/serialized-entity/etc/serialize-entity.urm.puml
new file mode 100644
index 000000000000..7bf89dbb98af
--- /dev/null
+++ b/serialized-entity/etc/serialize-entity.urm.puml
@@ -0,0 +1,56 @@
+@startuml
+package com.iluwatar.serializedentity {
+  class App {
+      - DB_URL : String {static}
+      - LOGGER : Logger {static}
+      + App()
+      - createDataSource() : DataSource {static}
+      - createSchema(dataSource : DataSource) {static}
+      - deleteSchema(dataSource : DataSource) {static}
+      + main(args : String[]) {static}
+  }
+
+  class Country {
+      - code : int
+      - name : String
+      - continents : String
+      - language : String
+      + serialVersionUID : final Long {static}
+      ~Country(int, String, String, String)
+      + getCode() : int
+      + getName() : String
+      + getContinents() : String
+      + getLanguage() : String
+      + setCode(code : int)
+      + setName(name : String)
+      + setContinents(continents : String)
+      + setLanguage(language : String)
+      + equals(that: Object) : boolean
+      + hashCode() : int
+      + toString() : String
+  }
+
+  interface CountryDao {
+    + insertCountry : int {abstract}
+    + selectCountry : int {abstract}
+  }
+
+  class CountrySchemaSql {
+    - CREATE_SCHEMA_SQL : String {static}
+    - DELETE_SCHEMA_SQL : String {static}
+    - LOGGER : Logger {static}
+    - dataSource : DataSource
+    - country : Country
+    ~ CountrySchemaSql(country : Country, dataSource : DataSource)
+    + insertCountry()
+    + selectCountry()
+  }
+  diamond h2db
+}
+
+App --> "Initialize Country objects" Country
+App --> "Initialize CountrySchemaSql" CountrySchemaSql
+CountrySchemaSql --> "implements" CountryDao
+Country --> "Stored in database" h2db
+CountrySchemaSql --> "Perform INSERT and SELECT operation after serialize and deserialize Country object" h2db
+@enduml
\ No newline at end of file
diff --git a/serialized-entity/pom.xml b/serialized-entity/pom.xml
new file mode 100644
index 000000000000..aea122f5a0ac
--- /dev/null
+++ b/serialized-entity/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+
+    The MIT License
+    Copyright © 2014-2022 Ilkka Seppälä
+
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to deal
+    in the Software without restriction, including without limitation the rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+    THE SOFTWARE.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.iluwatar</groupId>
+        <artifactId>java-design-patterns</artifactId>
+        <version>1.26.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>serialized-entity</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <configuration>
+                            <archive>
+                                <manifest>
+                                    <mainClass>com.iluwatar.serializedentity.App</mainClass>
+                                </manifest>
+                            </archive>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/serialized-entity/src/main/java/com/iluwatar/serializedentity/App.java b/serialized-entity/src/main/java/com/iluwatar/serializedentity/App.java
new file mode 100644
index 000000000000..fb3b383e69f7
--- /dev/null
+++ b/serialized-entity/src/main/java/com/iluwatar/serializedentity/App.java
@@ -0,0 +1,128 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.serializedentity;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import javax.sql.DataSource;
+import lombok.extern.slf4j.Slf4j;
+import org.h2.jdbcx.JdbcDataSource;
+
+
+/**
+ * Serialized Entity Pattern.
+ *
+ * <p> Serialized Entity Pattern allow us to easily persist Java objects to the database. It uses Serializable interface
+ * and DAO pattern. Serialized Entity Pattern will first use Serializable to convert a Java object into a set of bytes,
+ * then it will using DAO pattern to store this set of bytes as BLOB to database.</p>
+ *
+ * <p> In this example, we first initialize two Java objects (Country) "China" and "UnitedArabEmirates", then we
+ * initialize "serializedChina" with "China" object and "serializedUnitedArabEmirates" with "UnitedArabEmirates",
+ * then we use method "serializedChina.insertCountry()" and "serializedUnitedArabEmirates.insertCountry()" to serialize
+ * "China" and "UnitedArabEmirates" and persist them to database.
+ * Last, with "serializedChina.selectCountry()" and "serializedUnitedArabEmirates.selectCountry()" we could read "China"
+ * and "UnitedArabEmirates" from database as sets of bytes, then deserialize them back to Java object (Country). </p>
+ *
+ */
+@Slf4j
+public class App {
+  private static final String DB_URL = "jdbc:h2:~/test";
+
+  private App() {
+
+  }
+
+  /**
+   * Program entry point.
+   * @param args command line args.
+   * @throws IOException if any
+   * @throws ClassNotFoundException if any
+   */
+  public static void main(String[] args) throws IOException, ClassNotFoundException {
+    final var dataSource = createDataSource();
+
+    deleteSchema(dataSource);
+    createSchema(dataSource);
+
+    // Initializing Country Object China
+    final var China = new Country(
+            86,
+            "China",
+            "Asia",
+            "Chinese"
+    );
+
+    // Initializing Country Object UnitedArabEmirates
+    final var UnitedArabEmirates = new Country(
+            971,
+            "United Arab Emirates",
+            "Asia",
+            "Arabic"
+    );
+
+    // Initializing CountrySchemaSql Object with parameter "China" and "dataSource"
+    final var serializedChina = new CountrySchemaSql(China, dataSource);
+    // Initializing CountrySchemaSql Object with parameter "UnitedArabEmirates" and "dataSource"
+    final var serializedUnitedArabEmirates = new CountrySchemaSql(UnitedArabEmirates, dataSource);
+
+    /*
+    By using CountrySchemaSql.insertCountry() method, the private (Country) type variable  within Object
+    CountrySchemaSql will be serialized to a set of bytes and persist to database.
+    For more details of CountrySchemaSql.insertCountry() method please refer to CountrySchemaSql.java file
+    */
+    serializedChina.insertCountry();
+    serializedUnitedArabEmirates.insertCountry();
+
+    /*
+    By using CountrySchemaSql.selectCountry() method, CountrySchemaSql object will read the sets of bytes from database
+    and deserialize it to Country object.
+    For more details of CountrySchemaSql.selectCountry() method please refer to CountrySchemaSql.java file
+    */
+    serializedChina.selectCountry();
+    serializedUnitedArabEmirates.selectCountry();
+  }
+
+  private static void deleteSchema(DataSource dataSource) {
+    try (var connection = dataSource.getConnection();
+         var statement = connection.createStatement()) {
+      statement.execute(CountrySchemaSql.DELETE_SCHEMA_SQL);
+    } catch (SQLException e) {
+      LOGGER.info("Exception thrown " + e.getMessage());
+    }
+  }
+
+  private static void createSchema(DataSource dataSource) {
+    try (var connection = dataSource.getConnection();
+         var statement = connection.createStatement()) {
+      statement.execute(CountrySchemaSql.CREATE_SCHEMA_SQL);
+    } catch (SQLException e) {
+      LOGGER.info("Exception thrown " + e.getMessage());
+    }
+  }
+
+  private static DataSource createDataSource() {
+    var dataSource = new JdbcDataSource();
+    dataSource.setURL(DB_URL);
+    return dataSource;
+  }
+}
diff --git a/serialized-entity/src/main/java/com/iluwatar/serializedentity/Country.java b/serialized-entity/src/main/java/com/iluwatar/serializedentity/Country.java
new file mode 100644
index 000000000000..25ebddf89857
--- /dev/null
+++ b/serialized-entity/src/main/java/com/iluwatar/serializedentity/Country.java
@@ -0,0 +1,47 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.serializedentity;
+import java.io.Serializable;
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * A Country POJO taht represents the data that will serialize and store in database.
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+@ToString
+@AllArgsConstructor
+public class Country implements Serializable {
+
+  private int code;
+  private String name;
+  private String continents;
+  private String language;
+  public static final long serialVersionUID = 7149851;
+
+}
diff --git a/serialized-entity/src/main/java/com/iluwatar/serializedentity/CountryDao.java b/serialized-entity/src/main/java/com/iluwatar/serializedentity/CountryDao.java
new file mode 100644
index 000000000000..f5d91544672a
--- /dev/null
+++ b/serialized-entity/src/main/java/com/iluwatar/serializedentity/CountryDao.java
@@ -0,0 +1,45 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/**
+ * In an application the Data Access Object (DAO) is a part of Data access layer. It is an object
+ * that provides an interface to some type of persistence mechanism. By mapping application calls to
+ * the persistence layer, DAO provides some specific data operations without exposing details of the
+ * database. This isolation supports the Single responsibility principle. It separates what data
+ * accesses the application needs, in terms of domain-specific objects and data types (the public
+ * interface of the DAO), from how these needs can be satisfied with a specific DBMS, database
+ * schema, etc.
+ *
+ * <p>Any change in the way data is stored and retrieved will not change the client code as the
+ * client will be using interface and need not worry about exact source.
+ *
+ * @see InMemoryCustomerDao
+ * @see DbCustomerDao
+ */
+package com.iluwatar.serializedentity;
+
+import java.io.IOException;
+
+public interface CountryDao {
+  int insertCountry() throws IOException;
+  int selectCountry() throws IOException, ClassNotFoundException;
+}
diff --git a/serialized-entity/src/main/java/com/iluwatar/serializedentity/CountrySchemaSql.java b/serialized-entity/src/main/java/com/iluwatar/serializedentity/CountrySchemaSql.java
new file mode 100644
index 000000000000..4887ed9ddfd9
--- /dev/null
+++ b/serialized-entity/src/main/java/com/iluwatar/serializedentity/CountrySchemaSql.java
@@ -0,0 +1,122 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.serializedentity;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.sql.Blob;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import javax.sql.DataSource;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Country Schema SQL Class.
+ */
+@Slf4j
+public class CountrySchemaSql implements CountryDao {
+  public static final String CREATE_SCHEMA_SQL = "CREATE TABLE IF NOT EXISTS WORLD (ID INT PRIMARY KEY, COUNTRY BLOB)";
+
+  public static final String DELETE_SCHEMA_SQL = "DROP TABLE WORLD IF EXISTS";
+
+  private Country country;
+  private DataSource dataSource;
+
+  /**
+   * Public constructor.
+   *
+   * @param dataSource datasource
+   * @param country country
+   */
+  public CountrySchemaSql(Country country, DataSource dataSource) {
+    this.country = new Country(
+            country.getCode(),
+            country.getName(),
+            country.getContinents(),
+            country.getLanguage()
+    );
+    this.dataSource = dataSource;
+  }
+
+  /**
+   * This method will serialize a Country object and store it to database.
+   * @return int type, if successfully insert a serialized object to database then return country code, else return -1.
+   * @throws IOException if any.
+   */
+  @Override
+  public int insertCountry() throws IOException {
+    var sql = "INSERT INTO WORLD (ID, COUNTRY) VALUES (?, ?)";
+    try (var connection = dataSource.getConnection();
+         var preparedStatement = connection.prepareStatement(sql);
+         ByteArrayOutputStream baos = new ByteArrayOutputStream();
+         ObjectOutputStream oss = new ObjectOutputStream(baos)) {
+
+      oss.writeObject(country);
+      oss.flush();
+
+      preparedStatement.setInt(1, country.getCode());
+      preparedStatement.setBlob(2, new ByteArrayInputStream(baos.toByteArray()));
+      preparedStatement.execute();
+      return country.getCode();
+    } catch (SQLException e) {
+      LOGGER.info("Exception thrown " + e.getMessage());
+    }
+    return -1;
+  }
+
+  /**
+   * This method will select a data item from database and deserialize it.
+   * @return int type, if successfully select and deserialized object from database then return country code,
+   *     else return -1.
+   * @throws IOException if any.
+   * @throws ClassNotFoundException if any.
+   */
+  @Override
+  public int selectCountry() throws IOException, ClassNotFoundException {
+    var sql = "SELECT ID, COUNTRY FROM WORLD WHERE ID = ?";
+    try (var connection = dataSource.getConnection();
+         var preparedStatement = connection.prepareStatement(sql)) {
+
+      preparedStatement.setInt(1, country.getCode());
+
+      try (ResultSet rs = preparedStatement.executeQuery()) {
+        if (rs.next()) {
+          Blob countryBlob = rs.getBlob("country");
+          ByteArrayInputStream baos = new ByteArrayInputStream(countryBlob.getBytes(1, (int) countryBlob.length()));
+          ObjectInputStream ois = new ObjectInputStream(baos);
+          country = (Country) ois.readObject();
+          LOGGER.info("Country: " + country);
+        }
+        return rs.getInt("id");
+      }
+    } catch (SQLException e) {
+      LOGGER.info("Exception thrown " + e.getMessage());
+    }
+    return -1;
+  }
+
+}
diff --git a/servant/pom.xml b/servant/pom.xml
index 5e0b139154e3..65650364bd6b 100644
--- a/servant/pom.xml
+++ b/servant/pom.xml
@@ -27,6 +27,7 @@
 -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
+  <groupId>servant</groupId>
   <parent>
     <groupId>com.iluwatar</groupId>
     <artifactId>java-design-patterns</artifactId>


Test patch:
diff --git a/serialized-entity/src/test/java/com/iluwatar/serializedentity/AppTest.java b/serialized-entity/src/test/java/com/iluwatar/serializedentity/AppTest.java
new file mode 100644
index 000000000000..52ac9cd6e026
--- /dev/null
+++ b/serialized-entity/src/test/java/com/iluwatar/serializedentity/AppTest.java
@@ -0,0 +1,23 @@
+package com.iluwatar.serializedentity;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+/**
+ * Tests that Serialized Entity example runs without errors.
+ */
+class AppTest {
+
+    /**
+     * Issue: Add at least one assertion to this test case.
+     *
+     * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+     * throws an exception.
+     */
+
+    @Test
+    void shouldExecuteSerializedEntityWithoutException() {
+        assertDoesNotThrow(() -> App.main(new String[]{}));
+    }
+}
diff --git a/serialized-entity/src/test/java/com/iluwatar/serializedentity/CountryTest.java b/serialized-entity/src/test/java/com/iluwatar/serializedentity/CountryTest.java
new file mode 100644
index 000000000000..85d3ed87fac2
--- /dev/null
+++ b/serialized-entity/src/test/java/com/iluwatar/serializedentity/CountryTest.java
@@ -0,0 +1,101 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.serializedentity;
+import org.junit.jupiter.api.Test;
+
+import java.io.*;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class CountryTest {
+
+  @Test
+  void testGetMethod() {
+      Country China = new Country(
+              86,
+              "China",
+              "Asia",
+              "Chinese"
+      );
+
+      assertEquals(86, China.getCode());
+      assertEquals("China", China.getName());
+      assertEquals("Asia", China.getContinents());
+      assertEquals("Chinese", China.getLanguage());
+  }
+
+  @Test
+  void testSetMethod() {
+      Country country = new Country(
+              86,
+              "China",
+              "Asia",
+              "Chinese"
+      );
+
+      country.setCode(971);
+      country.setName("UAE");
+      country.setContinents("West-Asia");
+      country.setLanguage("Arabic");
+
+      assertEquals(971, country.getCode());
+      assertEquals("UAE", country.getName());
+      assertEquals("West-Asia", country.getContinents());
+      assertEquals("Arabic", country.getLanguage());
+  }
+
+  @Test
+  void testSerializable(){
+      // Serializing Country
+      try {
+          Country country = new Country(
+                  86,
+                  "China",
+                  "Asia",
+                  "Chinese");
+          ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("output.txt"));
+          objectOutputStream.writeObject(country);
+          objectOutputStream.close();
+      } catch (IOException e) {
+          e.printStackTrace();
+      }
+
+      // De-serialize Country
+      try {
+          ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("output.txt"));
+          Country country = (Country) objectInputStream.readObject();
+          objectInputStream.close();
+          System.out.println(country);
+
+          Country China = new Country(
+                  86,
+                  "China",
+                  "Asia",
+                  "Chinese");
+
+          assertEquals(China, country);
+      } catch (Exception e) {
+          e.printStackTrace();
+      }
+  }
+}