1. 程式人生 > >java.sql.SQLException: Value ‘0000-00-00 00:00:00’ can not be represented as java.sql.Timestamp

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

今天在使用 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 上一部分人的回答,我得出可以使用下面的方式連線資料庫來解決:

1

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。

它們的繼承關係如下:

1

2

3

4

5

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 型別。