Browser docs

Serialized Entity Pattern

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.

 1@Getter
 2@Setter
 3@EqualsAndHashCode
 4@ToString
 5@AllArgsConstructor
 6public class Country implements Serializable {
 7
 8    private int code;
 9    private String name;
10    private String continents;
11    private String language;
12    public static final long serialVersionUID = 7149851;
13    // Constructor ->
14    // getters and setters -> 
15}

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.

 1@Slf4j
 2public class CountrySchemaSql {
 3  public static final String CREATE_SCHEMA_SQL = "CREATE TABLE IF NOT EXISTS WORLD (ID INT PRIMARY KEY, COUNTRY BLOB)";
 4
 5  public static final String DELETE_SCHEMA_SQL = "DROP TABLE WORLD IF EXISTS";
 6
 7  private Country country;
 8  private DataSource dataSource;
 9
10  /**
11   * Public constructor.
12   *
13   * @param dataSource datasource
14   * @param country country
15   */
16  public CountrySchemaSql(Country country, DataSource dataSource) {
17    this.country = new Country(
18            country.getCode(),
19            country.getName(),
20            country.getContinents(),
21            country.getLanguage()
22    );
23    this.dataSource = dataSource;
24  }
25
26  /**
27   * This method will serialize a Country object and store it to database.
28   * @return int type, if successfully insert a serialized object to database then return country code, else return -1.
29   * @throws IOException if any.
30   */
31  public int insertCountry() throws IOException {
32    var sql = "INSERT INTO WORLD (ID, COUNTRY) VALUES (?, ?)";
33    try (var connection = dataSource.getConnection();
34         var preparedStatement = connection.prepareStatement(sql);
35         ByteArrayOutputStream baos = new ByteArrayOutputStream();
36         ObjectOutputStream oss = new ObjectOutputStream(baos)) {
37
38      oss.writeObject(country);
39      oss.flush();
40
41      preparedStatement.setInt(1, country.getCode());
42      preparedStatement.setBlob(2, new ByteArrayInputStream(baos.toByteArray()));
43      preparedStatement.execute();
44      return country.getCode();
45    } catch (SQLException e) {
46      LOGGER.info("Exception thrown " + e.getMessage());
47    }
48    return -1;
49  }
50
51  /**
52   * This method will select a data item from database and deserialize it.
53   * @return int type, if successfully select and deserialized object from database then return country code,
54   *     else return -1.
55   * @throws IOException if any.
56   * @throws ClassNotFoundException if any.
57   */
58  public int selectCountry() throws IOException, ClassNotFoundException {
59    var sql = "SELECT ID, COUNTRY FROM WORLD WHERE ID = ?";
60    try (var connection = dataSource.getConnection();
61         var preparedStatement = connection.prepareStatement(sql)) {
62
63      preparedStatement.setInt(1, country.getCode());
64
65      try (ResultSet rs = preparedStatement.executeQuery()) {
66        if (rs.next()) {
67          Blob countryBlob = rs.getBlob("country");
68          ByteArrayInputStream baos = new ByteArrayInputStream(countryBlob.getBytes(1, (int) countryBlob.length()));
69          ObjectInputStream ois = new ObjectInputStream(baos);
70          country = (Country) ois.readObject();
71          LOGGER.info("Country: " + country);
72        }
73        return rs.getInt("id");
74      }
75    } catch (SQLException e) {
76      LOGGER.info("Exception thrown " + e.getMessage());
77    }
78    return -1;
79  }
80
81}

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.

 1@Slf4j
 2public class App {
 3    private static final String DB_URL = "jdbc:h2:~/test";
 4
 5    private App() {
 6
 7    }
 8
 9    /**
10     * Program entry point.
11     * @param args command line args.
12     * @throws IOException if any
13     * @throws ClassNotFoundException if any
14     */
15    public static void main(String[] args) throws IOException, ClassNotFoundException {
16        final var dataSource = createDataSource();
17
18        deleteSchema(dataSource);
19        createSchema(dataSource);
20
21        final var China = new Country(
22                86,
23                "China",
24                "Asia",
25                "Chinese"
26        );
27
28        final var UnitedArabEmirates = new Country(
29                971,
30                "United Arab Emirates",
31                "Asia",
32                "Arabic"
33        );
34
35        final var serializedChina = new CountrySchemaSql(China, dataSource);
36        final var serializedUnitedArabEmirates = new CountrySchemaSql(UnitedArabEmirates, dataSource);
37        serializedChina.insertCountry();
38        serializedUnitedArabEmirates.insertCountry();
39
40        serializedChina.selectCountry();
41        serializedUnitedArabEmirates.selectCountry();
42    }
43
44    private static void deleteSchema(DataSource dataSource) {
45        try (var connection = dataSource.getConnection();
46             var statement = connection.createStatement()) {
47            statement.execute(CountrySchemaSql.DELETE_SCHEMA_SQL);
48        } catch (SQLException e) {
49            LOGGER.info("Exception thrown " + e.getMessage());
50        }
51    }
52
53    private static void createSchema(DataSource dataSource) {
54        try (var connection = dataSource.getConnection();
55             var statement = connection.createStatement()) {
56            statement.execute(CountrySchemaSql.CREATE_SCHEMA_SQL);
57        } catch (SQLException e) {
58            LOGGER.info("Exception thrown " + e.getMessage());
59        }
60    }
61
62    private static DataSource createDataSource() {
63        var dataSource = new JdbcDataSource();
64        dataSource.setURL(DB_URL);
65        return dataSource;
66    }
67}

Class diagram

alt_text

Applicability

Use the Serialized Entity pattern when

  • You want to easily persist Java objects to the database.

Data Access Object

Credits