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.
+ * + *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).
+ * + */ +@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 000000000..25ebddf89 --- /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 000000000..f5d915446 --- /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. + * + *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 000000000..4887ed9dd
--- /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/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 000000000..52ac9cd6e
--- /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 000000000..85d3ed87f
--- /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();
+ }
+ }
+}
diff --git a/servant/pom.xml b/servant/pom.xml
index 5e0b13915..65650364b 100644
--- a/servant/pom.xml
+++ b/servant/pom.xml
@@ -27,6 +27,7 @@
-->