杰瑞科技汇

Java的Date与MySQL的Date如何正确转换?

核心概念对比

我们要理解这两者分别是什么。

Java的Date与MySQL的Date如何正确转换?-图1
(图片来源网络,侵删)

MySQL 中的日期时间类型

MySQL 主要提供三种与日期时间相关的类型:

类型 格式 范围 精度 用途
DATE YYYY-MM-DD 1000-01-019999-12-31 仅存储日期,不存储时间,生日、纪念日。
DATETIME YYYY-MM-DD HH:MM:SS 1000-01-01 00:00:009999-12-31 23:59:59 同时存储日期和时间,与时区无关。
TIMESTAMP YYYY-MM-DD HH:MM:SS 1970-01-01 00:00:01 UTC 到 2038-01-19 03:14:07 UTC 同时存储日期和时间,依赖于时区,通常用于记录记录的创建和更新时间。

关键区别:

  • DATETIME vs TIMESTAMP:最大的区别是时区处理DATETIME 存储的是你插入时它所代表的“绝对时间值”,不会自动转换时区,而 TIMESTAMP 在存储时会转换为 UTC 时间,在查询时又会根据当前连接的时区转换回本地时间,这使得 TIMESTAMP 在不同服务器间同步时非常有用。
  • DATE vs DATETIMEDATE 只关心日期,DATETIME 关心日期和时间。

Java 中的日期时间 API

Java 的日期时间 API 经历了从旧到新的演变。

旧 API: java.util.Date (不推荐在新的业务逻辑中使用)

  • 本质:它是一个“时间戳”的包装类,内部存储的是一个自 1970 年 1 月 1 日 00:00:00 GMT (UTC) 以来的毫秒数
  • 问题
    1. 设计混乱:它的名称是 Date,但它同时包含了日期和时间信息,它还包含了很多过时的、与日期格式化无关的方法(如 getHours(), getDate()),这些方法已被废弃。
    2. 线程不安全SimpleDateFormat 等格式化类是线程不安全的。
    3. 时区处理困难:对时区的支持非常原始和麻烦。

新 API: java.time (Java 8+,推荐使用)

Java 8 引入了全新的 java.time 包,彻底解决了旧 API 的问题,这是现代 Java 开发中处理日期时间的标准。

Java的Date与MySQL的Date如何正确转换?-图2
(图片来源网络,侵删)
描述
LocalDate 表示一个日期(年、月、日),不包含时间信息,对应 MySQL 的 DATE
LocalTime 表示一个时间(时、分、秒、纳秒),不包含日期信息。
LocalDateTime 表示一个日期和时间(年、月、日、时、分、秒、纳秒),不包含时区信息,对应 MySQL 的 DATETIME
ZonedDateTime 表示一个带时区的日期和时间,对应 MySQL 的 TIMESTAMP(在概念上)。
Instant 表示一个时间轴上的瞬时点,与 java.util.Date 类似,基于 UTC 时间戳。

映射关系与转换

我们来看如何将 Java 的类型与 MySQL 的类型进行映射和转换。

Java 类型 MySQL 类型 说明
java.time.LocalDate DATE 最直接的映射,两者都只表示日期。
java.time.LocalDateTime DATETIME 最直接的映射,两者都表示日期和时间,且都不依赖时区。
java.util.Date DATETIME Date 内部是毫秒时间戳,可以无损地转换为 DATETIME,这是旧 JDBC 驱动默认的行为。
java.time.Instant TIMESTAMP Instant 是 UTC 时间戳,与 TIMESTAMP 的核心行为(存储为 UTC)非常匹配。
java.time.ZonedDateTime TIMESTAMP 带时区的日期时间,在存入数据库时,JDBC 驱动会将其转换为 UTC 的 TIMESTAMP

代码实践 (JDBC 操作)

下面我们通过代码示例来看具体的转换过程。

场景1:使用旧 API (java.util.Date)

假设我们有一个 User 表:

CREATE TABLE `user_old` (
  `id` INT PRIMARY KEY AUTO_INCREMENT,
  `name` VARCHAR(50),
  `create_time` DATETIME  -- 使用 DATETIME
);

Java 代码 (JDBC):

Java的Date与MySQL的Date如何正确转换?-图3
(图片来源网络,侵删)
import java.sql.*;
import java.util.Date;
public class JdbcDateExampleOld {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=UTC";
        String user = "your_username";
        String password = "your_password";
        // 1. Java 对象 -> MySQL DATETIME (插入)
        Date javaDate = new Date(); // 获取当前时间
        String insertSql = "INSERT INTO user_old (name, create_time) VALUES (?, ?)";
        try (Connection conn = DriverManager.getConnection(url, user, password);
             PreparedStatement pstmt = conn.prepareStatement(insertSql)) {
            pstmt.setString(1, "Alice");
            // setObject 会自动将 java.util.Date 转换为 SQL 的 Timestamp 类型
            pstmt.setObject(2, javaDate); 
            pstmt.executeUpdate();
            System.out.println("插入成功,Java Date: " + javaDate);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        // 2. MySQL DATETIME -> Java Date (查询)
        String selectSql = "SELECT id, name, create_time FROM user_old WHERE name = ?";
        try (Connection conn = DriverManager.getConnection(url, user, password);
             PreparedStatement pstmt = conn.prepareStatement(selectSql)) {
            pstmt.setString(1, "Alice");
            ResultSet rs = pstmt.executeQuery();
            if (rs.next()) {
                // 从 ResultSet 中获取时,getTimestamp 返回的是 java.sql.Timestamp
                // 它是 java.util.Date 的子类,可以直接赋值
                Timestamp timestamp = rs.getTimestamp("create_time");
                Date retrievedDate = timestamp; 
                System.out.println("查询成功,从 MySQL 读取的 Date: " + retrievedDate);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

关键点:

  • 在 JDBC 中,处理日期时间推荐使用 java.sql.Date, java.sql.Time, java.sql.Timestamp,它们是 java.util.Date 的子类,专门用于与数据库交互。
  • PreparedStatement.setObject()ResultSet.getTimestamp() 是进行转换的关键方法。

场景2:使用新 API (java.time)

这是现代 Java 开发推荐的方式,确保你的 JDBC 驱动版本较新(如 mysql-connector-j 8.0+),它原生支持 java.time

假设我们有一个 order 表:

CREATE TABLE `order_new` (
  `id` INT PRIMARY KEY AUTO_INCREMENT,
  `order_date` DATE,       -- 对应 LocalDate
  `created_at` DATETIME,   -- 对应 LocalDateTime
  `last_updated` TIMESTAMP -- 对应 ZonedDateTime 或 Instant
);

Java 代码 (JDBC):

import java.sql.*;
import java.time.*;
public class JdbcDateExampleNew {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=UTC";
        String user = "your_username";
        String password = "your_password";
        // 1. Java 对象 -> MySQL 类型 (插入)
        LocalDate orderDate = LocalDate.now();
        LocalDateTime createdAt = LocalDateTime.now();
        ZonedDateTime updatedAt = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")); // 假设服务器在中国
        String insertSql = "INSERT INTO order_new (order_date, created_at, last_updated) VALUES (?, ?, ?)";
        try (Connection conn = DriverManager.getConnection(url, user, password);
             PreparedStatement pstmt = conn.prepareStatement(insertSql)) {
分享:
扫描分享到社交APP
上一篇
下一篇