Java基础、中级、高级、架构面试资料

Cause: java.sql.SQLException: Value ‘0000-00-00 00:00:00’ can not be represented as java.sql.Timestamp

JAVA herman 3515浏览 0评论
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:xttblog2,发送下载链接帮助你免费下载!
本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog2,之前的微信号好友位已满,备注:返现
受密码保护的文章请关注“业余草”公众号,回复关键字“0”获得密码
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领
腾讯云】1核2G5M轻量应用服务器50元首年,高性价比,助您轻松上云

今天在使用 Mysql 中的一个 datetime 字段时碰到了一个 Cause: java.sql.SQLException: Value '0000-00-00 00:00:00' can not be represented as java.sql.Timestamp 异常,之前使用都没有问题,今天突然出现故障。所以我就仔细查看了一下代码,看看最近是否有人改动。通过最终的搜索排查,我把整个过程分享给大家!

根据异常信息,我翻译了一下,大概意思是说,'0000-00-00 00:00:00' 这个时间不能用 Java 来表示。虽然数据库中可以存放这个值,但是 Java 中的时间都是从 1970 年开始的。格林威治时间 1970年01月01日00时00分00秒(UTC+8北京时间1970年01月01日08时00分00秒),所以你这个 '0000-00-00 00:00:00' 的时间,Java 表示不了,所以就抛出了这个异常。

完整的异常信息如下:

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.executor.result.ResultMapException: Error attempting to get column 'audit_time' from result set.  Cause: org.apache.ibatis.executor.result.ResultMapException: Error attempting to get column 'audit_time' from result set.  Cause: java.sql.SQLException: Value '0000-00-00 00:00:00' can not be represented as java.sql.Timestamp
    at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77)
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
    at com.sun.proxy.$Proxy127.selectOne(Unknown Source)
    at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:166)
    at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:82)
    at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)
    at com.sun.proxy.$Proxy134.findOrderInfo(Unknown Source)

那么知道这个异常产生的原因后,该如何解决呢?

根据 stackoverflow 上一部分人的回答,我得出可以使用下面的方式连接数据库来解决:

jdbc:mysql://www.xttblog.com:3306/xttblog?zeroDateTimeBehavior=convertToNull

MySQL 的官方资料对 zeroDateTimeBehavior 属性做出了详细的解释,相见参考资料。

设置 zeroDateTimeBehavior 属性,当遇到 DATETIME 值完全由 0 组成时,最终的有效值可以设置为,异常(exception),一个近似值(round),或将这个值转换为 null(convertToNull)。

默认情况为 exception,设置这个属性会抛出一个 SQLException 异常,也就是文章开头所说到的异常。其 SQLSate 码为 S1009。这个状态码在写存储过程处理异常时也可以用到。

convertToNull,返回 null 来替代 0000-00-00 这样的日期。

round,将日期转换为 0001-01-01。

因此,出现 0000-00-00 属于一个无效日期,用 convertToNull 属性即可。

下面我们再一起来回顾一下 Java 中对日期类型的处理方法。

首先 Java 中能表示日期的提供了 4 个类,分别是:java.util.Date、java.sql.Date、java.sql.Time、java.sql.Timestamp。

它们的继承关系如下:

java.lang.Object 
....|__java.util.Date 
..........|__java.sql.Date/java.sql.Timestamp /java.sql.Time

....|__java.security.Timestamp
  • java.util.Date 日期格式为:年月日时分秒 
  • java.sql.Date 日期格式为:年月日[只存储日期数据不存储时间数据] 
  • java.sql.Time 日期格式为:时分秒 
  • java.sql.Timestamp 日期格式为:年月日时分秒纳秒(毫微秒)

从上可以看出 java.util.Date 这个类是 java.sql.Date,  java.sql.Time,  java.slq.Timestamp 这三个类的父类。这三个类对 java.util.Date 类进行了包装。

java.sql.Date 类屏蔽了 java.util.Date 类的时间有关的方法(形如:hh:mm:ss),因此,不可以通过这个类访问时间有关的信息,比如,如果你通过 sqlDate.getHour() 方法去访问小时信息,此方法会抛出一个IllegalArgumentException异常。这是因为 java.sql.Date 在继承 java.util.Date 类的时候对父类进行了重写,禁用了时间访问的方法。之所以这么处理,是为了和数据库的Date数据类型相匹配,数据库的Date数据类行只是保存日期有关的字段。

Java.sql.Time 类屏蔽了 java.util.Date 的日期有关的字段(形如:yyyy-MM-dd),因此,不能通过这个类访问日期有关的信息,比如:如果你通过 sqlTime.getYear() 方法去获取年有关的信息,此方法会抛出一个 IllegalArgumentException 异常。这是因为 java.sql.Time 在继承 java.util.Date 类的时候对父类进行了重写,禁用了日期访问的方法。之所以这么处理,是为了和数据库的 Time 数据类型相匹配,数据库的Time数据类行只是保存时间有关的字段。

Java.sql.Timestamp 字段则对 java.util.Date 这个类进行了扩充,它在 java.util.Date 类的基础上增加了毫秒的时间访问控制,因此,你可以通过 getNanos()方法去获取时间的毫微秒数(注意此处获取的时间是以毫微秒为单位的,1秒等于十亿毫微秒),同样的,这也是为了和数据库中的Timestamp数据类型进行匹配。

理清了上述四个类的关系,那么 java.util.Date 和 java.util.Calendar 类有什么关系呢?

Java.util.Calendar 类是 java.util.Date 类的一个更加深入,更加全面的替代。Java.util.Calendar 类支持 java.util.Date 的所有功能,此外,Calendar 还引入了多语言,多区域的特性,可以根据需要获取不同区域,不同时区的时间,Calendar 还增加了比 Date 更加方便和快捷的许多操作,如获取一年当中的第几个星期,各个月的天数等便捷的方法。

java.util.Calendar 区别与 java.util.Date 的几个地方也需要注意一下:首先,Calendar 增加了毫秒的时间段,通过它可以获取时间点的毫秒值,而 java.util.Date 只是精确到秒。其次,Calendar 过去年的时候是当前年份比如:2010,而 Date 获取年份的时获取到的是当前年份-1900的一个值(2010-1900=110,因此,你调用 getYear 后过去的值就是110)。最后 Calendar 是一个抽象类,之所以能够实例化,是因为此处的 Calendar 充当了一个类似于工厂的作用,在 getInstance 方法中实例化了 Calendar 子类 GregorianCalendar,并把它返回给用户使用。

针对不同的数据库选用不同的日期类型 。例如:Oracle的Date类型,只需要年月日,选择使用java.sql.Date类型;MySQL 和 Sqlserver 数据库的 DateTime 类型,需要年月日时分秒,选择 java.sql.Timestamp 类型。

参考资料

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号:xttblog2。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!

本文原文出处:业余草: » Cause: java.sql.SQLException: Value ‘0000-00-00 00:00:00’ can not be represented as java.sql.Timestamp