在項目開發中,爲了減小json傳輸的數據量,加快響應速度,一般當字段值爲null時,咱們不會把字段返回給前端。但在實際開發中可能像Android 與iOS 更但願咱們能夠返回完整的數據,html
在mybatis 中,返回map字段值爲null 時是有返回的,例如:前端
<result column="name" property="name" jdbcType="VARCHAR" javaType="java.lang.String"/>
在mapper.xml 文件中使用以上的格式返回名稱爲name的數據,若是name的值爲null ,那麼返回值也爲null,並不會無端的消失掉,因此咱們若是須要字段值爲null的字段不回傳,須要用到另外的jar 包
我當前項目中使用的jar 爲:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
如下貼上json 代碼
1 package module.spring; 2 3 import com.fasterxml.jackson.annotation.JsonInclude; 4 import com.fasterxml.jackson.core.JsonGenerator; 5 import com.fasterxml.jackson.databind.JsonSerializer; 6 import com.fasterxml.jackson.databind.ObjectMapper; 7 import com.fasterxml.jackson.databind.SerializationFeature; 8 import com.fasterxml.jackson.databind.SerializerProvider; 9 import com.fasterxml.jackson.databind.introspect.Annotated; 10 import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; 11 import com.fasterxml.jackson.databind.module.SimpleModule; 12 import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; 13 import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; 14 import message.Message; 15 import org.apache.commons.lang3.ClassUtils; 16 import org.springframework.http.HttpOutputMessage; 17 import org.springframework.http.converter.HttpMessageNotWritableException; 18 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 19 20 import java.io.IOException; 21 import java.lang.reflect.Type; 22 import java.util.*; 23 24 public class JSON extends MappingJackson2HttpMessageConverter { 25 26 private static final ThreadLocal<Map<Class, Filter>> LOCAL_FILTER = new ThreadLocal<>(); 27 28 private ObjectMapper ObjectMapperCopy; 29 30 public JSON() { 31 } 32 33 public JSON(ObjectMapper objectMapper) { 34 super(objectMapper); 35 objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 36 objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); 37 objectMapper.registerModule(new SimpleModule().addSerializer(Boolean.class,new BooleanJsonSerializer())); 38 // objectMapper.registerModule(new SimpleModule().addSerializer(Message.class,new MessageJsonSerializer())); 39 this.ObjectMapperCopy = objectMapper.copy(); 40 } 41 42 @Override 43 protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { 44 this.objectMapper = ObjectMapperCopy.copy(); 45 SimpleFilterProvider filterProvider = new SimpleFilterProvider(); 46 if (getLocalFilter().size() > 0) { 47 for (Map.Entry<Class, Filter> filter : getLocalFilter().entrySet()) { 48 if(filter.getValue().mode == Filter.Mode.EXCEPT){ 49 filterProvider.addFilter(filter.getKey().getName(), SimpleBeanPropertyFilter.serializeAllExcept(filter.getValue().field)); 50 }else if(filter.getValue().mode == Filter.Mode.INCLUDE){ 51 filterProvider.addFilter(filter.getKey().getName(), SimpleBeanPropertyFilter.filterOutAllExcept(filter.getValue().field)); 52 } 53 } 54 this.objectMapper.setFilterProvider(filterProvider); 55 this.objectMapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() { 56 @Override 57 public Object findFilterId(Annotated a) { 58 return (ClassUtils.isAssignable(a.getClass(), com.fasterxml.jackson.databind.introspect.AnnotatedClass.class) && getLocalFilter().containsKey(a.getType().getRawClass())) ? a.getName() : null; 59 } 60 }); 61 } 62 super.writeInternal(object, type, outputMessage); 63 clearLocalFilter(); 64 } 65 66 67 public static void except(Class aClass, String... field){ 68 if(getLocalFilter().containsKey(aClass)){ 69 throw new FilterException("filter exists"); 70 } 71 getLocalFilter().put(aClass,new Filter(field, Filter.Mode.EXCEPT)); 72 } 73 74 public static void include(Class aClass, String... field){ 75 if(getLocalFilter().containsKey(aClass)){ 76 throw new FilterException("filter exists"); 77 } 78 getLocalFilter().put(aClass,new Filter(field, Filter.Mode.INCLUDE)); 79 } 80 81 private static Map<Class,Filter> getLocalFilter() { 82 if(LOCAL_FILTER.get() == null){ 83 LOCAL_FILTER.set(new HashMap<>()); 84 } 85 return LOCAL_FILTER.get(); 86 } 87 88 private static void clearLocalFilter(){ 89 LOCAL_FILTER.remove(); 90 } 91 92 private static class Filter { 93 private String[] field; 94 private Filter.Mode mode; 95 96 private Filter(String[] field,Filter.Mode mode){ 97 this.field = field; 98 this.mode = mode; 99 } 100 101 private enum Mode{ 102 EXCEPT, 103 INCLUDE 104 } 105 } 106 107 private final static class FilterException extends RuntimeException { 108 109 private FilterException(String message) { 110 super(message); 111 } 112 } 113 114 private final class BooleanJsonSerializer extends JsonSerializer<Boolean> { 115 116 @Override 117 public void serialize(Boolean value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 118 gen.writeNumber(value ? 1 : 0); 119 } 120 } 121 122 private final class MessageJsonSerializer extends JsonSerializer<Message> { 123 124 @Override 125 public void serialize(Message message, JsonGenerator gen, SerializerProvider serializers) throws IOException { 126 if( message.getData() instanceof List){ 127 gen.writeNumber(0); 128 } 129 if( message.getData() instanceof Map){ 130 Map map=(Map)message.getData(); 131 Set<String> set = map.keySet(); 132 for (String str:set 133 ) { 134 if(map.get(str) instanceof String){ 135 map.put(str,""); 136 } 137 if(map.get(str) instanceof Date){ 138 map.put(str,0); 139 } 140 if(map.get(str) instanceof Number){ 141 map.put(str,0); 142 } 143 144 } 145 } 146 gen.writeNumber(com.alibaba.fastjson.JSON.toJSONString(message)); 147 } 148 } 149 }
上面被我註釋的代碼並非錯誤,我註釋它只由於我根本用不到它,跟個人需求不符:java
我是爲了給返回值設定默認值,剛開始個人思路爲若是返回的字段類型爲String 類型而且返回值爲null,則返回 「」 ,返回的字段類型爲Integer 類型而且返回值爲null,則返回 0 ,""和 0 是我自定義的能夠隨意的更改。。mysql
可是我發現返回值爲null時 字段沒有類型,對是沒有類型 就是null,我只能對null進行處理。git
JsonSerializer<Message> 這裏的 Message 類型是我返回值得包裝類,裏面有返回值 ,狀態碼等...github
不過也不是全無收穫,起碼若是我想要對返回值中的一些字段的值進行過濾的話可能比較方便...web
既然這兒不行我只能往更上游,能夠拿到返回的字段的類型的地方實現我返回自定義默認值的目標。spring
我找到了mybatis 的 BaseTypeHandler 在這兒對mybatis 從數據庫取出來的字段進行設置默認值。BaseTypeHandler 有不少的子類,分別處理不一樣的返回類型。例如:DateTypeHandler、StringTypeHandler ..sql
全部的數據類型都有它的子類的實現。 我直接繼承了類DateTypeHandler,把默認返回null 改成 默認返回 new Date(0), 發現好使??????注意這裏我並無更改配置,也沒有在mapper.xml 中使用 typeHandler="" 這個屬性引用我寫好的子類數據庫
package dao.typehandler.myTypeHander; import exception.SimpleException; import org.apache.ibatis.executor.result.ResultMapException; import org.apache.ibatis.type.DateTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import java.sql.*; import java.util.Date; /** * @author tianyuting * @date 2019/11/110:59 */ //@MappedJdbcTypes({JdbcType.DATE,JdbcType.TIMESTAMP}) 這個註解在一開始是沒有加的。若是不加須要在mapper.xml中使用typeHandler="" 進行引用
public class MyDateTypeHandler extends DateTypeHandler { @Override public Date getResult(ResultSet rs, String columnName) throws SQLException { Date result; try { result = getNullableResult(rs, columnName); } catch (Exception e) { throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e); } return result; } @Override public Date getNullableResult(ResultSet rs, String columnName) throws SQLException { Timestamp sqlTimestamp = rs.getTimestamp(columnName); if (sqlTimestamp != null) { // throw new SimpleException("fffff"); return new Date(sqlTimestamp.getTime()); } return new Date(0); } @Override public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException { Timestamp sqlTimestamp = rs.getTimestamp(columnIndex); if (sqlTimestamp != null) { return new Date(sqlTimestamp.getTime()); } return new Date(0); } @Override public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { Timestamp sqlTimestamp = cs.getTimestamp(columnIndex); if (sqlTimestamp != null) { return new Date(sqlTimestamp.getTime()); } return new Date(0); } }
想着既然這樣可行 那就把String 也弄個默認爲「」 的子類。好嘛..不行,String死活不行,必須得使用 typeHandler="" 在mapper.xml 中進行引用。可我就想不明白了,爲何其中一個能夠一個就不行呢?若是有知道的大佬,還請您不吝指點一番...在下感激涕零
按個人理解Java的繼承 與多態 在這兒徹底不夠用,代碼jar 已經寫好了我如今的子類實現就是一個沒人調用的存在,多態的父類引用指向子類對象在我這裏徹底的被違背。感受哪兒出了問題,是否是個人眼界太窄,或許人家使用了反射什麼的???
若是有大佬有思路實現一個只經過繼承就能夠覆蓋父類的方法執行子類方法,若是沒有子類就執行父類方法的程序,可不能夠分享一下 ...我是徹底沒有思路啊。只能說本身辣雞一個。
1 package dao.typehandler.myTypeHander; 2 3 import org.apache.ibatis.executor.result.ResultMapException; 4 import org.apache.ibatis.type.JdbcType; 5 import org.apache.ibatis.type.MappedJdbcTypes; 6 import org.apache.ibatis.type.StringTypeHandler; 7 8 import java.sql.*; 9 10 /** 11 * 12 */ 13 //@MappedJdbcTypes(JdbcType.VARCHAR) 這個註解在一開始是沒有加的。若是不加須要在mapper.xml中使用typeHandler="" 進行引用 14 public class MyStringTypeHandler extends StringTypeHandler { 15 16 @Override 17 public String getResult(ResultSet rs, String columnName) throws SQLException { 18 String result; 19 try { 20 result = getNullableResult(rs, columnName); 21 } catch (Exception e) { 22 throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e); 23 } 24 return result; 25 26 } 27 @Override 28 public String getNullableResult(ResultSet rs, String columnName) 29 throws SQLException { 30 String rsString = rs.getString(columnName); 31 if(rsString==null){ 32 return ""; 33 } 34 return rsString; 35 } 36 37 @Override 38 public String getNullableResult(ResultSet rs, int columnIndex) 39 throws SQLException { 40 String rsString = rs.getString(columnIndex); 41 if(rsString==null){ 42 return ""; 43 } 44 return rsString; 45 } 46 47 @Override 48 public String getNullableResult(CallableStatement cs, int columnIndex) 49 throws SQLException { 50 String rsString = cs.getString(columnIndex); 51 if(rsString==null){ 52 return ""; 53 } 54 return rsString; 55 } 56 }
我感受在mapper.xml中使用 typeHandler="" 是有一點太麻煩了,每個我都要加一個typeHandler="",還要把字段一一的都寫到 resultMap 中去,實在是感受不咋地.
因此我繼續上網搜索 typeHandler 這個東西,順便說一句,看官方文檔,啥都有,有不瞭解的直接找官方文檔是最好的。想走捷徑,除非這個東西處處都是,分分鐘就已經解決..分分鐘不了就去看官方吧.貼個連接mybatis官方文檔(中文)
好了,須要把配置也該一改,
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL Map Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="LOG4J2"/>
<!--<setting name="logImpl" value="NO_LOGGING" />-->
<!--<setting name="cacheEnabled" value="false"/>-->
<setting name="useGeneratedKeys" value="true"/>
<setting name="defaultExecutorType" value="REUSE"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="cacheEnabled" value="true"/>
<setting name="callSettersOnNulls" value="true"/>
</settings>
<!--<typeHandlers>-->
<!--<package name="dao.typehandler"/>-->
<!--</typeHandlers>-->
<typeHandlers>
<!--
當配置package的時候,mybatis會去配置的package掃描TypeHandler
<package name="com.dy.demo"/>
-->
<!-- handler屬性直接配置咱們要指定的TypeHandler -->
<!--<typeHandler handler=""/>-->
<!-- javaType 配置java類型,例如String, 若是配上javaType, 那麼指定的typeHandler就只做用於指定的類型 -->
<typeHandler javaType="java.lang.String" handler="dao.typehandler.myTypeHander.MyStringTypeHandler"/>
<!-- jdbcType 配置數據庫基本數據類型,例如varchar, 若是配上jdbcType, 那麼指定的typeHandler就只做用於指定的類型 -->
<typeHandler javaType="java.util.Date" handler="dao.typehandler.myTypeHander.MyDateTypeHandler"/>
</typeHandlers>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
<!--
<property name="offsetAsPageNum" value="true"/>
<property name="rowBoundsWithCount" value="true"/>
-->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
</configuration>
改完很差使,還得再類上加註解 @MappedJdbcTypes ,好了這個東西能夠自定義默認值了。。。 除了業務代碼,難一點的東西就很吃力,如今工做都快2年了,源碼看的迷迷糊糊的。難過....大家有過這樣的感受嗎?