Comparison between class serialized storage and class native storage
Java serialization is to convert an object into a string of binary byte arrays, and achieve the purpose of persistence by saving or transferring these byte data. The essence of serialization is to save the current state information of the object, and there is no need to save the complete structural information.
The native storage of Java classes is to map objects to the relationship of the database. An entity class corresponds to a table in the database, and member variables in the class correspond to fields in the database. The purpose of data persistence is achieved through the container of the database.
Both of the above two methods can achieve the purpose of data persistence, so is there any difference in performance between the two?
We will use a comparative experiment to verify whether there is a difference in performance between the two, design a Student class with 12 attributes and implement a serializable interface, and design its corresponding database table and table for storing its serialization, respectively. Observe the difference in time efficiency and space efficiency between the two during the process of accessing records from the database.
Student class that implements serialization interface
import java.io.Serializable;
import java.util.Date;
/*
* 实现可序列化接口
*/
public class Student implements Serializable{
private Integer id;
private String name;
private int age;
private int gender;
private String dept;
private String tel;
private String email;
private String hobby;
private String school;
private double wallet;
private int zodiac;
private String address;
public Student() {
}
public Student(Integer id, String name, int age, int gender, String dept, String tel, String email, String hobby, String school, double wallet, int zodiac, String address) {
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
this.dept = dept;
this.tel = tel;
this.email = email;
this.hobby = hobby;
this.school = school;
this.wallet = wallet;
this.zodiac = zodiac;
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getGender() {
return gender;
}
public void setGender(int gender) {
this.gender = gender;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
public double getWallet() {
return wallet;
}
public void setWallet(double wallet) {
this.wallet = wallet;
}
public int getZodiac() {
return zodiac;
}
public void setZodiac(int zodiac) {
this.zodiac = zodiac;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", gender=" + gender +
", dept='" + dept + '\'' +
", tel='" + tel + '\'' +
", email='" + email + '\'' +
", hobby='" + hobby + '\'' +
", school='" + school + '\'' +
", wallet=" + wallet +
", zodiac=" + zodiac +
", address='" + address + '\'' +
'}';
}
}
Student table structure for storing entity classes
create table if not exists student (
id int primary key,
name varchar(10),
age int,
gender int,
dept varchar(10) not null,
tel varchar(11) unique,
email varchar(18) unique,
hobby varchar(10),
school varchar(10) not null,
wallet double(10,2),
zodiac int,
address varchar(20)
);
Store serialized objtest table structure
create table if not exists objtest(
id int primary key,
obj blob not null
);
Design the DBHelper class to implement the operation of the database connection pool. Its functions include data connection pool configuration, recycling, database table insertion, database table query, database table emptying, and returning the space occupied by the table in memory.
DBHelper class
Database connection pool configuration
public class DBHelper {
private static Connection conn;
private static PreparedStatement pres;
private static ResultSet rs;
private static ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
static {
try {
Class.forName(bundle.getString("Driver"));
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
conn = DriverManager.getConnection(url, user, password);
if (conn != null) {
System.out.println("数据库连接成功");
} else System.out.println("数据库连接失败");
} catch (Exception e) {
e.printStackTrace();
}
}
Database connection pool recycling
public static void free(Connection conn, PreparedStatement pres, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (pres != null) {
try {
pres.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
Student object serialized storage
public static void save(List<Student> students){
long startTime = System.currentTimeMillis();
String sql = "insert into objtest(obj) values(?)";
try {
pres=conn.prepareStatement(sql);
for(int i = 0; i < students.size(); i ++){
pres.setObject(1, students.get(i));
pres.addBatch();
}
pres.executeBatch();
} catch (SQLException e) {
e.printStackTrace();
} finally {
free(conn, pres, null);
}
long endTime = System.currentTimeMillis();
System.out.println("序列化存储运行时间:" + (endTime - startTime));
}
Student object serialization query, deserialization
public static List<Student> read(){
long startTime = System.currentTimeMillis();
List<Student> list = new ArrayList<Student>();
String sql="select obj from objtest";
try {
pres = conn.prepareStatement(sql);
rs = pres.executeQuery();
while(rs.next()){
Blob blob = rs.getBlob(1);
InputStream is = blob.getBinaryStream();
BufferedInputStream buffer = new BufferedInputStream(is);
byte[] buff = new byte[(int) blob.length()];
while((buffer.read(buff, 0, buff.length)) != -1) {
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buff));
Student student = (Student) in.readObject();
list.add(student);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
free(conn, pres, rs);
}
long endTime = System.currentTimeMillis();
System.out.println("序列化读取运行时间:" + (endTime - startTime));
return list;
}
Student object native storage
public static void saveStudent(List<Student> students) {
long startTime = System.currentTimeMillis();
String sql = "insert into student(id, name, age, gender, dept, tel, email, hobby, school, wallet, zodiac, address) values (?,?,?,?,?,?,?,?,?,?,?,?)";
try {
pres = conn.prepareStatement(sql);
for (Student student : students) {
pres.setInt(1, student.getId());
pres.setString(2, student.getName());
pres.setInt(3, student.getAge());
pres.setInt(4, student.getGender());
pres.setString(5, student.getDept());
pres.setString(6, student.getTel());
pres.setString(7, student.getEmail());
pres.setString(8, student.getHobby());
pres.setString(9, student.getSchool());
pres.setDouble(10, student.getWallet());
pres.setInt(11, student.getZodiac());
pres.setString(12, student.getAddress());
pres.addBatch();
}
pres.executeBatch();
} catch (SQLException e) {
throw new RuntimeException(e);
}
long endTime = System.currentTimeMillis();
System.out.println("直接存储运行时间:" + (endTime - startTime) + "ms");
}
Student Object Native Query
public static List<Student> readStudent() {
long startTime = System.currentTimeMillis();
List<Student> list = new ArrayList<Student>();
String sql = "select * from student";
try {
pres = conn.prepareStatement(sql);
rs = pres.executeQuery();
while (rs.next()) {
Integer id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
int gender = rs.getInt("gender");
String dept = rs.getString("dept");
String tel = rs.getString("tel");
String email = rs.getString("email");
String hobby = rs.getString("hobby");
String school = rs.getString("school");
double wallet = rs.getDouble("wallet");
int zodiac = rs.getInt("zodiac");
String address = rs.getString("address");
Student student = new Student(id, name, age, gender, dept, tel, email, hobby, school, wallet, zodiac, address);
list.add(student);
}
} catch (SQLException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("直接读取运行时间:" + (endTime - startTime) + "ms");
return list;
}
Clear the database table
public static void cleanTable() {
String sql1 = "truncate table student";
String sql2 = "truncate table objtest";
try {
pres = conn.prepareStatement(sql1);
pres.executeUpdate();
pres = conn.prepareStatement(sql2);
pres.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
free(conn, pres, null);
}
}
Memory space occupied by query records
public static void getMemory(String tableName) {
double memory = 0;
int rows = 0;
String sql = "SELECT TABLE_NAME,DATA_LENGTH+INDEX_LENGTH memory,TABLE_ROWS FROM TABLES WHERE TABLE_SCHEMA='jdbc' AND TABLE_NAME=?";
try {
pres = conn.prepareStatement(sql);
pres.setString(1, tableName);
rs = pres.executeQuery();
if (rs.next()) {
memory = rs.getDouble("memory");
rows = rs.getInt("TABLE_ROWS");
}
System.out.println("表中记录数: " + rows + " 占用内存空间:" + memory / 1024 + "KB");
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
experiment procedure
Design the Test test class to repeatedly insert and query the student entity classes with different numbers of records, and conduct a control experiment by observing the time stamps consumed in the process of storing and querying in the database.
Experimental sample
Set the number of records in five groups as 1, 5, 200, 1000, 5000, and 10,000, repeat the storage and query 5 times, and take the average value of the running time and memory space.
Test class
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int count = scanner.nextInt();
List<Student> list = new ArrayList<>();
DBHelper.cleanTable(); // 清空数据库表
for (int i = 1; i <= count; i++) {
list.add(new Student(i, "zhangsan", 18, 1, "计算机系", "10086", "[email protected]", "basketball", "温州大学", 3.8, 5, "温州大学计算机与人工智能学院"));
}
System.out.println("记录数为 "+ count +"条时:");
DBHelper.save(list); // 学生类序列化存储
DBHelper.saveStudent(list); // 学生类对象存储
DBHelper.read(); // 学生类序列化查询
DBHelper.readStudent(); // 学生类对象查询
}
}
Experimental results
Number of records/article | Native (average time/ms) | Serialization (average time/ms) |
1 | 10.8 | 27.2 |
200 | 666.4 | 734.2 |
1000 | 3038.4 | 3211.8 |
5000 | 14080.8 | 14384.2 |
10000 | 27909.4 | 28020.4 |
Number of records/article | Native (average time/ms) | Serialization (average time/ms) |
1 | 4.6 | 24.0 |
200 | 48.2 | 95.0 |
1000 | 120.2 | 195.6 |
5000 | 226.0 | 440.6 |
10000 | 320.4 | 759.4 |
Number of records/article | Native (memory size/kb) | Serialization (memory size/kb) |
1 | 16 | 16 |
200 | 64 | 64 |
1000 | 192 | 496 |
5000 | 1552 | 2576 |
10000 | 2576 | 5648 |
Through experiments, it is found that the storage time of the serialized data is similar to that of the original storage as the number of records increases, but the time spent in the query process is about twice that of the original query; in terms of memory space, with the As the number of records increases, the memory occupied by the serialized data also increases. It can be seen that for the persistent storage of entity objects, native storage performs better in efficiency than serialized storage.
The possible reasons for the difference in serialization efficiency are:
1. It takes a certain amount of time to serialize and deserialize the class itself;
2. The serialized data is a binary stream, which occupies a larger space (about 412bytes/item), and consumes more time during database query and cross-domain return after query.
personal opinion
For the two persistent storage methods, it is clearer to directly use the mapping between entity classes and database tables, which is convenient for performing various query, modification and deletion operations, and can easily obtain the required query conditions; in terms of data security, no The mapping of the encrypted entity class on the database table will be displayed in the form of plain text, and a set of incomprehensible byte streams obtained through serialization of the object, but it can still be deserialized and restored to the original state in the JVM environment. There is form. However, in inter-process communication, object transfer between processes is realized, and the byte stream generated after serialization can show the characteristics of high efficiency, convenience, and durability during transmission.