最近学习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的源码注解内容如下:
/**
* A thin wrapper around a millisecond value that allows
* JDBC to identify this as an SQL DATE
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 DATE
, the
* millisecond values wrapped by a java.sql.Date
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日期数据类型
日期类型 | 占用空间 | 日期格式 | 范围 |
---|---|---|---|
DATETIME | 8字节 | yyyy-MM-dd HH:mm:ss | 1000-01-01 00:00:00~~9999-12-31 23:59:59 |
TIMESTAMP | 4字节 | yyyy-MM-dd HH:mm:ss | 1970-01-01 08:00:01~~2038年 |
DATE | 4字节 | yyyy-MM-dd | 1000-01-01~~9999-12-31 |
TIME | 3字节 | HH:mm:ss | -838:59:59~~838:59:59 |
正是因为两个数据库有着不同的日期字段类型,Java也有自己的日期类型,所以不同类型在转换的时候容易出现错误。
-
问题:为什么要把util.date转成sql.date?
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")
@DatetimeFormat(pattern=“yyyy-MM-dd”)是将String转换成Date,一般前台给后台传值时用
@JsonFormat(pattern=“yyyy-MM-dd”) 将Date转换成String 一般后台传值给前台时