【MybatisPlus】數據庫的datetime類型字段為空的時候,報錯空指針?


一、發現經歷

事情是這樣的,我今天本來要演示系統,就去前端同學的頁面上點一點。不小心點到了其他同事編寫的服務,然后界面就報錯了。這給我嚇得,這還能演示嗎這。然后,我就去服務器查看了一下日志,發現了如下景象:

 

 

看到這景象啊,我第一件事情就是查看堆棧,也沒找到自己寫的代碼啊,好好的咋就報錯了。

於是,我第一件事情,復制報錯信息找到百度網站。復制粘貼,往上一懟!好家伙,竟然找不到一個和我症狀一樣的。

那我只能自己處理了。

 

二、問題定位

從堆棧信息定位一個問題的位置其實是很簡單的,但是要明白為什么錯誤,還是得多看一會兒的。

我定睛看了一陣子,發現了

 at org.apache.ibatis.type.LocalDateTimeTypeHandler.getNullableResult(LocalDateTimeTypeHandler.java:38)

 

這一行報錯,於是我就跟進源碼查看。這個源碼,寫的也簡單。

 1 /**
 2  *    Copyright 2009-2019 the original author or authors.
 3  *
 4  *    Licensed under the Apache License, Version 2.0 (the "License");
 5  *    you may not use this file except in compliance with the License.
 6  *    You may obtain a copy of the License at
 7  *
 8  *       http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 package org.apache.ibatis.type;
17 
18 import java.sql.CallableStatement;
19 import java.sql.PreparedStatement;
20 import java.sql.ResultSet;
21 import java.sql.SQLException;
22 import java.time.LocalDateTime;
23 
24 /**
25  * @since 3.4.5
26  * @author Tomas Rohovsky
27  */
28 public class LocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> {
29 
30   @Override
31   public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType)
32           throws SQLException {
33     ps.setObject(i, parameter);
34   }
35 
36   @Override
37   public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
38     return rs.getObject(columnName, LocalDateTime.class);
39   }
40 
41   @Override
42   public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
43     return rs.getObject(columnIndex, LocalDateTime.class);
44   }
45 
46   @Override
47   public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
48     return cs.getObject(columnIndex, LocalDateTime.class);
49   }
50 }

一共也就這么幾行。報錯的是:

public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getObject(columnName, LocalDateTime.class);
}
這個方法。這里面發生了空指針。

這時候,我發現還是最細的堆棧信息。於是,我到最細的那一層。

 

 

 

也就是這一行。

 

看到這里,我明白了。實際上就是

getTimestamp(columnIndex) 

這個獲取時間的時候,數據庫的submit_time字段沒有值,然后cast的時候,發生了空指針。

 

三、問題解決

弄清了病症,也就好下葯治療了。我腦中浮現出兩個想法:

1、讓submit_time有值

這不簡單嗎,沒值的時候有問題,我讓你有值不就完事了。哈哈。開個玩笑,要結合業務處理的哈。這種方法明顯不可取。

2、重寫LocalDateTimeTypeHandler這個類,然后添加空指針判斷。

這個方法不錯,可以解決實際問題。

於是,我查看了其他類型的處理。

 1 /**
 2  *    Copyright 2009-2018 the original author or authors.
 3  *
 4  *    Licensed under the Apache License, Version 2.0 (the "License");
 5  *    you may not use this file except in compliance with the License.
 6  *    You may obtain a copy of the License at
 7  *
 8  *       http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 package org.apache.ibatis.type;
17 
18 import java.sql.CallableStatement;
19 import java.sql.PreparedStatement;
20 import java.sql.ResultSet;
21 import java.sql.SQLException;
22 
23 /**
24  * @author Clinton Begin
25  */
26 public class DoubleTypeHandler extends BaseTypeHandler<Double> {
27 
28   @Override
29   public void setNonNullParameter(PreparedStatement ps, int i, Double parameter, JdbcType jdbcType)
30       throws SQLException {
31     ps.setDouble(i, parameter);
32   }
33 
34   @Override
35   public Double getNullableResult(ResultSet rs, String columnName)
36       throws SQLException {
37     double result = rs.getDouble(columnName);
38     return result == 0 && rs.wasNull() ? null : result;
39   }
40 
41   @Override
42   public Double getNullableResult(ResultSet rs, int columnIndex)
43       throws SQLException {
44     double result = rs.getDouble(columnIndex);
45     return result == 0 && rs.wasNull() ? null : result;
46   }
47 
48   @Override
49   public Double getNullableResult(CallableStatement cs, int columnIndex)
50       throws SQLException {
51     double result = cs.getDouble(columnIndex);
52     return result == 0 && cs.wasNull() ? null : result;
53   }
54 
55 }

 

 看了Double的類型處理【1、wasNull設置標志位 2、獲取的外面通過標志位判空】,我又看了Integer的類型處理。。。

 1  /**
2
* Copyright 2009-2018 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package org.apache.ibatis.type; 17 18 import java.sql.CallableStatement; 19 import java.sql.PreparedStatement; 20 import java.sql.ResultSet; 21 import java.sql.SQLException; 22 23 /** 24 * @author Clinton Begin 25 */ 26 public class IntegerTypeHandler extends BaseTypeHandler<Integer> { 27 28 @Override 29 public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType) 30 throws SQLException { 31 ps.setInt(i, parameter); 32 } 33 34 @Override 35 public Integer getNullableResult(ResultSet rs, String columnName) 36 throws SQLException { 37 int result = rs.getInt(columnName); 38 return result == 0 && rs.wasNull() ? null : result; 39 } 40 41 @Override 42 public Integer getNullableResult(ResultSet rs, int columnIndex) 43 throws SQLException { 44 int result = rs.getInt(columnIndex); 45 return result == 0 && rs.wasNull() ? null : result; 46 } 47 48 @Override 49 public Integer getNullableResult(CallableStatement cs, int columnIndex) 50 throws SQLException { 51 int result = cs.getInt(columnIndex); 52 return result == 0 && cs.wasNull() ? null : result; 53 } 54 }

 

 Integer的類型處理也是【1、wasNull設置標志位 2、獲取的外面通過標志位判空】。

 

這真是好家伙,坐不住了。其他的類型都處理了,合着就我用的LocalDateTime沒做空指針處理。這哪能行。重寫,必須重寫。

 1 package com.vsofo;
 2 
 3 import org.apache.ibatis.type.LocalDateTimeTypeHandler;
 4 import org.springframework.stereotype.Component;
 5 
 6 import java.sql.ResultSet;
 7 import java.sql.SQLException;
 8 import java.time.LocalDateTime;
 9 
10 @Component
11 public class LocalDateTimeTypeHandlerPlus extends LocalDateTimeTypeHandler {
12 
13     @Override
14     public LocalDateTime getResult(ResultSet rs, String columnName) throws SQLException {
15         Object object = rs.getObject(columnName);
16         if(object==null){
17             return  null;
18         }
19         return super.getResult(rs, columnName);
20     }
21 }

我直接繼承,然后重寫獲取邏輯。

1、先獲取數據庫的值

2、判空,如果值為空,直接返回。

3、如果,值不為空,進行原來的強轉邏輯。

 

重寫之后,問題完美解決了。

 

=============================================================================

2021-08-18 最近在做另外一個項目的時候,也發現了這個問題,然而一樣的步驟失效了,原因了,沒有使用到這個自定義的LocalDateTime處理器。后面,我使用類替換的形式代替,解決了該問題。復制出原來的LocalDateTimeTypeHandler放在同一個包下 ,依舊是重寫getResult(ResultSet rs, String columnName)方法解決了該問題,這使用的方式是直接替換了源碼中的類。特此記錄!

 1 /**
 2  *    Copyright 2009-2019 the original author or authors.
 3  *
 4  *    Licensed under the Apache License, Version 2.0 (the "License");
 5  *    you may not use this file except in compliance with the License.
 6  *    You may obtain a copy of the License at
 7  *
 8  *       http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 package org.apache.ibatis.type;
17 
18 import java.sql.CallableStatement;
19 import java.sql.PreparedStatement;
20 import java.sql.ResultSet;
21 import java.sql.SQLException;
22 import java.time.LocalDateTime;
23 
24 /**
25  * @since 3.4.5
26  * @author Tomas Rohovsky
27  */
28 public class LocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> {
29   
30   @Override
31   public LocalDateTime getResult(ResultSet rs, String columnName) throws SQLException {
32     Object object = rs.getObject(columnName);
33     if(object==null){
34       return  null;
35     }
36     return super.getResult(rs, columnName);
37   }
38 
39   @Override
40   public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType)
41           throws SQLException {
42     ps.setObject(i, parameter);
43   }
44 
45   @Override
46   public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
47     return rs.getObject(columnName, LocalDateTime.class);
48   }
49 
50   @Override
51   public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
52     return rs.getObject(columnIndex, LocalDateTime.class);
53   }
54 
55   @Override
56   public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
57     return cs.getObject(columnIndex, LocalDateTime.class);
58   }
59 }

 或者,是在字段上的注解@TableField設置該字段的類型轉換器

   /**
     * 下發時間
     */
    @TableField(value = "oper_time",typeHandler =LocalDateTimeTypeHandlerPlus.class) 
   private LocalDateTime operTime;

如果是xml的話,也有該配置

<resultMap id="myParam" type="com.base.slave.entity.UserStaticUserinfo" >
<result column="plat_oper_time" property="platOperTime" typeHandler="cn.lxw.LocalDateTimeTypeHandlerPlus"/>
</resultMap>

 

最后,還有一種方式,那就是把對應映射的字段類型改成java.util.Date類型,因為Date類型的源碼已經做了特殊處理,只是這樣要動到一些涉及的代碼而增加一些風險。

四、心得體會

我覺着吧,框架是個好東西,能讓我們開發項目事半功倍。但是如果給你埋了一個大雷,也可能讓你事倍功半。

所以,我們用框架開發的過程中,應該多用多測多實踐。找出影響項目的問題,然后解決它。

分享結束,謝謝大家觀看哈!





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM