一、发现经历

事情是这样的,我今天本来要演示系统,就去前端同学的页面上点一点。不小心点到了其他同事编写的服务,然后界面就报错了。这给我吓得,这还能演示吗这。然后,我就去服务器查看了一下日志,发现了如下景象:

【MybatisPlus】数据库的datetime类型字段为空的时候,报错空指针?-LMLPHP

看到这景象啊,我第一件事情就是查看堆栈,也没找到自己写的代码啊,好好的咋就报错了。

于是,我第一件事情,复制报错信息找到百度网站。复制粘贴,往上一怼!好家伙,竟然找不到一个和我症状一样的。

那我只能自己处理了。

二、问题定位

从堆栈信息定位一个问题的位置其实是很简单的,但是要明白为什么错误,还是得多看一会儿的。

我定睛看了一阵子,发现了

 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);
}
这个方法。这里面发生了空指针。

这时候,我发现还是最细的堆栈信息。于是,我到最细的那一层。

【MybatisPlus】数据库的datetime类型字段为空的时候,报错空指针?-LMLPHP

也就是这一行。

看到这里,我明白了。实际上就是

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 }

【MybatisPlus】数据库的datetime类型字段为空的时候,报错空指针?-LMLPHP

 看了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 }

【MybatisPlus】数据库的datetime类型字段为空的时候,报错空指针?-LMLPHP

 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、如果,值不为空,进行原来的强转逻辑。

重写之后,问题完美解决了。

四、心得体会

我觉着吧,框架是个好东西,能让我们开发项目事半功倍。但是如果给你埋了一个大雷,也可能让你事倍功半。

所以,我们用框架开发的过程中,应该多用多测多实践。找出影响项目的问题,然后解决它。

分享结束,谢谢大家观看哈!




06-05 06:00