mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-05-15 18:59:10 +00:00
feature: Implement Serialized Entity Pattern (#2150)
* Add files: - App.java - Country.java - CountryDao.java - CountrySchemaSql.java - CountryTest.java - README.md - serialize-entity.urm.puml - pom.xml (inherit from parent pom.xml) Update files: - pom.xml (parent pom.xml, add 1 module called serialized-entity) * Fix duplicated error in pom.xml * Update: - pom.xml (module servant, changed by error, now fixed) - pom.xml (module serialized-entity) * Update: - pom.xml (changed, artifactId) * Update: - pom.xml Add: AppTest.java * Resolved changes required by the reviewer Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user