一、發現經歷

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

看到這景象啊,我第一件事情就是檢視堆疊,也沒找到自己寫的程式碼啊,好好的咋就報錯了。

於是,我第一件事情,複製報錯資訊找到百度網站。複製貼上,往上一懟!好傢伙,竟然找不到一個和我症狀一樣的。

那我只能自己處理了。

二、問題定位

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

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

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、如果,值不為空,進行原來的強轉邏輯。

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

四、心得體會

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

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

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