Java、MySQL、Oracle的Date类型相关问题分析

最近学习Java,在使用sql.data还是util.date上有了疑问,在查阅了相关资料后,把找到的知识点和自己理解的内容记录下来,方便其他有同样疑问的童鞋参考

特别感谢以下大佬的文章:

Oracle、MySQL与java的日期类型浅析
oracle 数据类型详解---日期型
MyBatis处理MySQL字段类型date与datetime
java.sql.Date和java.util.Date区别及使用

问题:sql.date和util.date有什么区别?

首先,sql.date是util.date的子类,sql.date的源码注解内容如下:

* <P>A thin wrapper around a millisecond value that allows * JDBC to identify this as an SQL <code>DATE</code> value. A * milliseconds value represents the number of milliseconds that * have passed since January 1, 1970 00:00:00.000 GMT. * To conform with the definition of SQL <code>DATE</code>, the * millisecond values wrapped by a <code>java.sql.Date</code> instance * must be 'normalized' by setting the * hours, minutes, seconds, and milliseconds to zero in the particular * time zone with which the instance is associated. public class Date extends java.util.Date {

翻译过来就是:
一个包装了毫秒值的瘦包装器 (thin wrapper),它允许 JDBC 将毫秒值标识为 SQL DATE 值。毫秒值表示自 1970 年 1 月 1 日 00:00:00 GMT 以来经过的毫秒数。
为了与 SQL DATE 的定义一致,由 java.sql.Date 实例包装的毫秒值必须通过将小时、分钟、秒和毫秒设置为与该实例相关的特定时区中的零来“规范化”。
需要注意的是:
sql.date只包含年月日信息,时分秒毫秒都会清零。当我们调用ResultSet的getDate()方法来获得返回值时,java程序会参照sql.date的“规范”来格式化数据库中的数值。数据库中存在时分秒毫秒部分将会被截取,也就是说,如果你是 2020-08-05 11:30:10 这样的时间存取数据,那么存在数据库中的值就是:2020-08-05 00:00:00

问题:Java开发的过程中,向MySQL、Oracle读取或插入日期数据会碰到格式或类型转换错误?

我们先来了解一下两个数据的时间类型分别是什么样的
1. Oracle日期数据类型
1.1、Date
Date可以保存日期和时间。
Date表示的日期范围是公元前4712年1月1日至公元9999年12月31日。
Date类型在数据库中的存储固定为7个字节。
第1字节:世纪+100
第2字节:年
第3字节:月
第4字节:天
第5字节:小时+1
第6字节:分+1
第7字节:秒+1
1.2、Timestamp
Timestamp不仅可以保 存日期和时间,还能保存小数秒,小数位数可以指定为0-9,默认为6位,所以最高精度可以到ns(纳秒)。
Timestamp在数据库用7或者11个字节存储,如果精度为 0,则用7字节存储,与Date类型功能相同,如果精度大于0则用11字节存储。
Timestamp表示的日期范围是1970-01-01 00:00:01到2038-01-19 03:14:07
第1字节:世纪+100
第2字节:年
第3字节:月
第4字节:天
第5字节:小时+1
第6字节:分+1
第7字节:秒+1
第8-11字节:纳秒,采用4个字节存储,内部运算类型为整形
注:Timestamp日期类型如果与数值进行加减运算会自动转换为Date型,小数秒会自动去除。

2.MySQL日期数据类型

sql.date是Java为了适配数据库中的Date字段,所以需要将util.date转成sql.date,由于sql.date精度不足,在需要高时间精度的场景也可以使用sql.timestamp,转换方式如下:

//字符串转java.util.date
public java.util.Date getDate(String str) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        Date result = null;
        try {
            result = sdf.parse(str);
        } catch (ParseException e) {
            e.printStackTrace();
        return result;
//java.util.Date转为java.sql.Date
public java.sql.Date getSqlDate(java.util.Date date){
        return new java.sql.Date(date.getTime());

在MyBatis框架中可以不用转换,直接使用util.date,因为MyBatis框架会根据jdbcType的参数直接转换,
只有日期:jdbcType="DATE"
只有时间:jdbcType="TIME"
日期+时间:jdbcType="TIMESTAMP"

在数据库语句中可以使用如下方法进行转换:
1.Oracle中需要使用to_date()去转换字符串

-- 注意:oracle数据库中MM和mm都是月份,mi才表示分钟,hh24和hh分别表示24小时制和12小时制
insert into test_date values(to_date('1998/11/11 13:13:13','yyyy/MM/dd hh24:mi:ss'));
insert into test_date values(to_date('1998/11/11 01:13:13','yyyy/MM/dd hh:mi:ss'));

2.MySQL可以直接插入字符串

insert into test_date values("2220-02-02 12:12:12");

总的来说,util.date 就是Java的日期对象,sql.date 是针对SQL语句使用的,只包含日期而没有时间部分。

顺便说一下有时候从数据库取出来的时间需要加8个小时,这是因为没有指定时区,默认时区是UTC(世界统一时间),我国是属于东八区,比UTC早8个小时,可以通过如下方式来进行处理:
1.在连接数据库的配置中添加时区:

spring
    datasource:
        url:jdbc:mysql://localhost/test?serverTimezone=Asia/Shanghai(或者GMT%2B8)
spring.datasource.url=jdbc:mysql://localhost/test?serverTimezone=Asia/Shanghai(或者GMT%2B8)

2.在注解中进行配置:

@JsonFormat(pattern=“yyyy-MM-dd”, timezone="GMT+8")