【MybatisPlus】数据库的datetime类型字段为空的时候,报错空指针?
⼀、发现经历
事情是这样的,我今天本来要演⽰系统,就去前端同学的页⾯上点⼀点。不⼩⼼点到了其他同事编写的服务,然后界⾯就报错了。这给我吓得,这还能演⽰吗这。然后,我就去服务器查看了⼀下⽇志,发现了如下景象:
看到这景象啊,我第⼀件事情就是查看堆栈,也没到⾃⼰写的代码啊,好好的咋就报错了。
于是,我第⼀件事情,复制报错信息到百度⽹站。复制粘贴,往上⼀怼!好家伙,竟然不到⼀个和我症状⼀样的。
那我只能⾃⼰处理了。
⼆、问题定位
从堆栈信息定位⼀个问题的位置其实是很简单的,但是要明⽩为什么错误,还是得多看⼀会⼉的。
我定睛看了⼀阵⼦,发现了
at org.NullableResult(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 *      /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*/
16package org.pe;
17
18import java.sql.CallableStatement;
19import java.sql.PreparedStatement;
20import java.sql.ResultSet;
21import java.sql.SQLException;
22import java.time.LocalDateTime;
23
24/**
25 * @since 3.4.5
26 * @author Tomas Rohovsky
27*/
28public class LocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> {
29
30  @Override
31public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType)
32throws SQLException {
33    ps.setObject(i, parameter);
34  }
35
36  @Override
37public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
Object(columnName, LocalDateTime.class);
39  }
40
41  @Override
42public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
Object(columnIndex, LocalDateTime.class);
44  }
45
46  @Override
47public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
Object(columnIndex, LocalDateTime.class);
49  }
50 }
⼀共也就这么⼏⾏。报错的是:
public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
Object(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 *      /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*/
16package org.pe;
17
18import java.sql.CallableStatement;
数据库简单吗19import java.sql.PreparedStatement;
20import java.sql.ResultSet;
21import java.sql.SQLException;
22
23/**
24 * @author Clinton Begin
25*/
26public class DoubleTypeHandler extends BaseTypeHandler<Double> {
27
28  @Override
29public void setNonNullParameter(PreparedStatement ps, int i, Double parameter, JdbcType jdbcType)
30throws SQLException {
31    ps.setDouble(i, parameter);
32  }
33
34  @Override
35public Double getNullableResult(ResultSet rs, String columnName)
36throws SQLException {
37double result = rs.getDouble(columnName);
38return result == 0 && rs.wasNull() ? null : result;
39  }
40
41  @Override
42public Double getNullableResult(ResultSet rs, int columnIndex)
43throws SQLException {
44double result = rs.getDouble(columnIndex);
45return result == 0 && rs.wasNull() ? null : result;
46  }
47
48  @Override
49public Double getNullableResult(CallableStatement cs, int columnIndex)
50throws SQLException {
51double result = cs.getDouble(columnIndex);
52return 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 *      /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*/
16package org.pe;
17
18import java.sql.CallableStatement;
19import java.sql.PreparedStatement;
20import java.sql.ResultSet;
21import java.sql.SQLException;
22
23/**
24 * @author Clinton Begin
25*/
26public class IntegerTypeHandler extends BaseTypeHandler<Integer> {
27
28  @Override
29public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
30throws SQLException {
31    ps.setInt(i, parameter);
32  }
33
34  @Override
35public Integer getNullableResult(ResultSet rs, String columnName)
36throws SQLException {
37int result = rs.getInt(columnName);
38return result == 0 && rs.wasNull() ? null : result;
39  }
40
41  @Override
42public Integer getNullableResult(ResultSet rs, int columnIndex)
43throws SQLException {
44int result = rs.getInt(columnIndex);
45return result == 0 && rs.wasNull() ? null : result;
46  }
47
48  @Override
49public Integer getNullableResult(CallableStatement cs, int columnIndex)
50throws SQLException {
51int result = cs.getInt(columnIndex);
52return result == 0 && cs.wasNull() ? null : result;
53  }
54 }
Integer的类型处理也是【1、wasNull设置标志位 2、获取的外⾯通过标志位判空】。
这真是好家伙,坐不住了。其他的类型都处理了,合着就我⽤的LocalDateTime没做空指针处理。这哪能⾏。重写,必须重写。
1package com.vsofo;
2
3import org.pe.LocalDateTimeTypeHandler;
4import org.springframework.stereotype.Component;
5
6import java.sql.ResultSet;
7import java.sql.SQLException;
8import java.time.LocalDateTime;
9
10 @Component
11public class LocalDateTimeTypeHandlerPlus extends LocalDateTimeTypeHandler {
12
13    @Override
14public LocalDateTime getResult(ResultSet rs, String columnName) throws SQLException {
15        Object object = rs.getObject(columnName);
16if(object==null){
17return null;
18        }
Result(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 *      /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*/
16package org.pe;
17
18import java.sql.CallableStatement;
19import java.sql.PreparedStatement;
20import java.sql.ResultSet;
21import java.sql.SQLException;
22import java.time.LocalDateTime;
23
24/**
25 * @since 3.4.5
26 * @author Tomas Rohovsky
27*/
28public class LocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> {
29
30  @Override
31public LocalDateTime getResult(ResultSet rs, String columnName) throws SQLException {
32    Object object = rs.getObject(columnName);
33if(object==null){
34return null;
35    }
Result(rs, columnName);
37  }
38
39  @Override
40public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType)
41throws SQLException {
42    ps.setObject(i, parameter);
43  }
44
45  @Override
46public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
Object(columnName, LocalDateTime.class);
48  }
49
50  @Override
51public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
Object(columnIndex, LocalDateTime.class);
53  }
54
55  @Override
56public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
Object(columnIndex, LocalDateTime.class);
58  }
59 }
或者,是在字段上的注解@TableField设置该字段的类型转换器
  /**
* 下发时间
*/
@TableField(value = "oper_time",typeHandler =LocalDateTimeTypeHandlerPlus.class)
  private LocalDateTime operTime;
如果是xml的话,也有该配置
<resultMap id="myParam" type="com.ity.UserStaticUserinfo" >
<result column="plat_oper_time" property="platOperTime" typeHandler="cn.lxw.LocalDateTimeTypeHandlerPlus"/>
</resultMap>
最后,还有⼀种⽅式,那就是把对应映射的字段类型改成java.util.Date类型,因为Date类型的源码已经做了特殊处理,只是这样要动到⼀些涉及的代码⽽增加⼀些风险。
四、⼼得体会
我觉着吧,框架是个好东西,能让我们开发项⽬事半功倍。但是如果给你埋了⼀个⼤雷,也可能让你事倍功半。
所以,我们⽤框架开发的过程中,应该多⽤多测多实践。出影响项⽬的问题,然后解决它。
分享结束,谢谢⼤家观看哈!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。