写在前面

今天继续讲Java连接MySQL数据库和Java中的对象流(序列化与反序列化)的知识!

Java 与 MySQL 连接

要使用 Java 操作 MySQL 数据库,我们需要通过 JDBC(Java Database Connectivity)进行连接。JDK 本身不提供对具体数据库的操作能力,因此需要使用数据库提供的驱动包来实现。

JDBC 编程 6 步

  1. 注册驱动
    告诉 Java 程序即将连接的数据库类型。

    Class.forName("com.mysql.cj.jdbc.Driver");
    
  2. 获取连接
    打开 JVM 进程和数据库进程之间的通道。

    Connection conn = DriverManager.getConnection(
        "jdbc:mysql://主机的IP地址:3306/数据库名?useUnicode=true&characterEncoding=utf-8&useSSL=false",
        "root", "123456");
    
  3. 获取数据库操作对象
    创建用于执行 SQL 语句的对象。

    Statement state = conn.createStatement();
    

    注意:进行查询操作时,推荐使用 PreparedStatement,以防止 SQL 注入。

    PreparedStatement ps = conn.prepareStatement(
        "UPDATE user SET password=? WHERE username=?");
    
  4. 执行 SQL 语句

    • executeQuery: 用于查询(DQL)。
    • executeUpdate: 用于增删改(DML)。
    // 执行查询
    ResultSet resultSet = state.executeQuery("SELECT * FROM tableName");
    
    // 执行更新
    int rowsAffected = state.executeUpdate("UPDATE tableName SET column=value WHERE condition");
    
  5. 处理查询结果集

    • 仅在执行查询语句(SELECT)时需要处理。
    while (resultSet.next()) {
        // 获取每列的值
        int eno = resultSet.getInt("eno");
    }
    
  6. 释放资源
    使用完资源后关闭连接。

    state.close();
    conn.close();
    

MySQL Connector/J 版本对比

  • MySQL Connector/J 5.1

    • 支持 Java 5 及以上版本。
    • 支持 MySQL 5.6、5.7 和 8.0。
    • 支持 JDBC 3.0、4.0、4.1 和 4.2。
    • Driver 实现类:com.mysql.jdbc.Driver.
  • MySQL Connector/J 8.0

    • 支持 Java 8 及以上版本。
    • 支持 MySQL 5.6、5.7 和 8.0。
    • 支持 JDBC 4.2。
    • Driver 实现类:com.mysql.cj.jdbc.Driver.

Properties 集合

Properties 类用于封装和管理与 MySQL 连接相关的参数配置。

Properties 概述

Properties 类是 Map 集合的一个特殊实现,主要用于处理键值对形式的配置数据。它提供了对配置文件的读取和写入功能。

Properties 的特殊功能

  • 设置属性

    public Object setProperty(String key, String value);
    

    用于将指定的键值对添加到 Properties 集合中。

  • 获取属性

    public String getProperty(String key);
    

    根据键获取对应的值。

  • 获取所有键名

    public Set<String> stringPropertyNames();
    

    获取所有的键名集合。

Properties 和 IO 流的结合使用

  • 加载属性

    public void load(Reader reader);
    

    从指定的 Reader 中读取属性列表(键值对)。

  • 存储属性

    public void store(Writer writer, String comments);
    

    将属性列表(键值对)写入到指定的 Writer 中,带有注释。

使用示例

  1. 创建 Properties 类对象

    Properties properties = new Properties();
    
  2. 指定要读取的配置文件

    properties.load(new BufferedReader(new FileReader("路径/info.properties")));
    
  3. 获取配置项

    String url = properties.getProperty("url");
    String username = properties.getProperty("user");
    String password = properties.getProperty("password");
    
  4. 使用获取的参数建立数据库连接

    Connection conn = MysqlTool.getConnection(url, username, password);
    System.out.println("成功获取与数据库的链接对象:" + conn);
    

示例配置文件 (info.properties)

password=123456
user=root
url=jdbc:mysql://主机的IP地址:3306/具体数据库名?useUnicode=true&characterEncoding=utf-8&useSSL=false

序列化与反序列化

序列化流

  • 序列化:将对象转换为可以通过网络传输的字节流。

    • 对象输出流:ObjectOutputStream
  • 反序列化:将字节流还原为对象。

    • 对象输入流:ObjectInputStream

示例代码

读取对象

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("和输出文件路径一样的路径,编码不一样也会出现不能反序列化"));
Object o = ois.readObject(); // 例如 new Student("魏一民", 18);
Student student = (Student) o;
System.out.println(student);

写入对象

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("输入文件路径"));
Student s1 = new Student("魏一民", 18, "安徽合肥", "110");
oos.writeObject(s1);
oos.flush(); // 确保数据写入文件

序列化操作问题

  • 为什么要实现序列化?

    • 持久化:将对象状态保存到磁盘或数据库中,以便后续恢复。
    • 通信:在网络中将对象通过字节流发送到远程计算机。
    • 深度复制:通过序列化和反序列化实现对象的深度复制。
  • 如何实现序列化?

    • 类必须实现 java.io.Serializable 接口。这个接口是一个标记接口,没有方法需要实现。
    import java.io.Serializable;
    
    public class MyClass implements Serializable {
        private static final long serialVersionUID = 1L; // 推荐使用
    
        private String name;
        private int age;
    
        // Getter 和 Setter 方法
    }
    
    • serialVersionUID:用于版本控制,确保序列化和反序列化时的类版本一致。
  • 使用 transient 关键字声明不需要序列化的成员变量

    import java.io.Serializable;
    
    public class MyClass implements Serializable {
        private static final long serialVersionUID = 1L;
    
        private String name;
        private transient int age; // 不会被序列化
    
        // Getter 和 Setter 方法
    }
    
  • 序列化数据后,再次修改类文件,如何解决问题?

    • 版本控制:使用 serialVersionUID 来控制版本匹配,确保序列化和反序列化的类版本一致。
    • 自定义 readObjectwriteObject 方法:处理类的版本变化。
    • transient 关键字:标记不需要序列化的字段,避免因字段缺失导致问题。