Java 中的序列化实体模式:简化数据持久化和交换
也称为
- 对象序列化
序列化实体设计模式的意图
Java 中的序列化实体模式允许使用对象序列化来简化数据传输和存储。它使 Java 对象能够轻松地转换为序列化格式和从序列化格式转换,从而允许有效地存储和传输它们。
序列化实体模式的详细解释以及现实世界中的例子
现实世界中的例子
在现实世界中,序列化实体设计模式的一个例子可以在视频游戏中保存和加载游戏状态的过程中找到。当玩家决定保存他们的进度时,游戏当前状态(包括玩家的位置、库存和成就)将被序列化为 JSON 或二进制等文件格式。然后,该文件可以存储在玩家的设备上或云服务器上。当玩家想要恢复游戏时,序列化数据将被反序列化回游戏的对象,从而允许玩家从上次离开的地方继续。这种机制确保了复杂的游戏状态可以轻松保存和恢复,从而提供无缝的游戏体验。
通俗地说
序列化实体设计模式允许将 Java 对象转换为序列化格式和从序列化格式转换,以方便存储和传输。
维基百科说
在计算中,序列化是指将数据结构或对象状态转换为可存储(例如,辅助存储设备中的文件、主存储设备中的数据缓冲区)或传输(例如,通过计算机网络传输的数据流)并稍后重建(可能在不同的计算机环境中)的格式的过程。当根据序列化格式重新读取生成的位序列时,它可以用于创建原始对象的语义上相同的克隆。对于许多复杂对象,例如那些大量使用引用的对象,此过程并不简单。对象的序列化不包括它们以前与之链接的任何关联方法。
Java 中序列化实体模式的编程示例
序列化实体设计模式是一种轻松地将 Java 对象持久化到数据库的方法。它使用 `Serializable` 接口和 DAO(数据访问对象)模式。该模式首先使用 `Serializable` 将 Java 对象转换为字节集,然后使用 DAO 模式将该字节集存储为数据库中的 BLOB(二进制大型对象)。
首先,我们有 `Country` 类,它是一个简单的 POJO(普通旧 Java 对象),代表将被序列化并存储在数据库中的数据。在序列化实体设计模式中实现 Java `Serializable` 接口至关重要,这意味着该对象可以转换为字节流并从中恢复。
@Getter
@Setter
@EqualsAndHashCode
@ToString
@AllArgsConstructor
public class Country implements Serializable {
private int code;
private String name;
private String continents;
private String language;
@Serial
private static final long serialVersionUID = 7149851;
}
接下来,我们有 `CountryDao` 接口,它定义了将 `Country` 对象持久化到数据库和从数据库中检索 `Country` 对象的方法。
public interface CountryDao {
int insertCountry() throws IOException;
int selectCountry() throws IOException, ClassNotFoundException;
}
`CountrySchemaSql` 类实现 `CountryDao` 接口。它使用 `DataSource` 连接到数据库,并使用将要序列化并存储在数据库中的 `Country` 对象。
@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 CountrySchemaSql(Country country, DataSource dataSource) {
this.country = new Country(
country.getCode(),
country.getName(),
country.getContinents(),
country.getLanguage()
);
this.dataSource = dataSource;
}
@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;
}
@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;
}
}
最后,在 `App` 类中,我们创建 `Country` 对象和 `CountrySchemaSql` 对象。然后,我们调用 `insertCountry` 方法来序列化 `Country` 对象并将它们存储在数据库中。我们还调用 `selectCountry` 方法来从数据库中检索序列化后的 `Country` 对象并将它们反序列化回 `Country` 对象。
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();
}
控制台输出
11:55:32.842 [main] INFO com.iluwatar.serializedentity.CountrySchemaSql -- Country: Country(code=86, name=China, continents=Asia, language=Chinese)
11:55:32.870 [main] INFO com.iluwatar.serializedentity.CountrySchemaSql -- Country: Country(code=971, name=United Arab Emirates, continents=Asia, language=Arabic)
这是一个序列化实体设计模式的基本示例。它展示了如何序列化 Java 对象、将它们存储在数据库中,然后检索和反序列化它们。
何时在 Java 中使用序列化实体模式
- 这种模式对于需要在各种 Java 环境中的状态之间进行数据持久化的应用程序特别有用。
- 在需要通过网络共享对象或将对象保存到文件的情况下很有用。
Java 中序列化实体模式的实际应用
- Java 使用 `Serializable` 接口的内置序列化机制。
- 在 Web 应用程序中存储会话数据。
- 缓存对象以提高性能。
- 使用 RMI 或其他 RPC 机制在分布式系统中传输对象。
序列化实体模式的优缺点
优点
- 简化了保存和恢复对象状态的过程。
- 便于对象在系统不同部分之间传输。
- 减少了手动序列化和反序列化的样板代码。
权衡
- 如果处理不当,可能会引入安全风险(例如,反序列化攻击)。
- 序列化格式可能不容易被人阅读或编辑。
- 类结构的更改可能会破坏与先前序列化数据的兼容性。
相关的 Java 设计模式
- 数据传输对象 (DTO):用于封装数据并将其发送到网络。通常序列化用于传输。
- 备忘录:提供一种捕获和恢复对象状态的方法,通常使用序列化。
- 代理:代理可以序列化请求以与远程对象交互。