SpringBoot專案,如何優雅的把介面引數中的空白值替換為null值?

itread01 2021-01-14 17:05:57
SpringBoot NULL 介面 空白


問題發生

我們公司程式碼生成的時候,查詢列表統一都是使用了setEntity() ,查詢寫法如下:

public List<BasReservoirArea> selectList(BasReservoirArea basReservoirArea) { QueryWrapper<BasReservoirArea> where = new QueryWrapper<>(); where.setEntity(basReservoirArea); return baseMapper.selectList(where);}

 

查詢的方法是Get方法:

前端是通過url加引數傳過來的,如果有一個引數值為空的時候,由於setEntity() 並不過濾空白,執行sql的時候 會把""作為引數去當做查詢條件,查詢就出現了問題:

於是我就想把空白轉換為null來解決這個問題了。

初始解決

一開始自然而然想到在setEntity之前先判斷, 如果BasReservoirArea這個例項有欄位的值是空白就設定為null

//1.物件轉mapMap<Object, Object> map = MapUtil.beanToMap(test);//2.移除空值MapUtil.removeNullValue(map);//3.map轉回物件Test entity = JSON.parseObject(JSON.toJSONString(map), Test.class);

 

用到的工具類如下

/*** 將物件屬性轉化為map結合*/public static <T> Map<Object, Object> beanToMap(T bean) { Map<Object, Object> map = new HashMap<>(); if (bean != null) { BeanMap beanMap = BeanMap.create(bean); for (Object key : beanMap.keySet()) { map.put(key, beanMap.get(key)); } } return map;}/*** 移除map中的value空值** @param map* @return*/public static void removeNullValue(Map map) { Set set = map.keySet(); for (Iterator iterator = set.iterator(); iterator.hasNext(); ) { Object obj = (Object) iterator.next(); Object value = (Object) map.get(obj); remove(value, iterator); }}

 

問題解決了。

優化

由於感覺上面的解決方案不夠專業,不夠優雅,所以先尋找更好的解決辦法,在後端接收引數值的時候,如果接收的是空白,直接設定為null, 這樣就不需要再次轉換了。

解決問題首先要考慮兩種情況,一種是前端通過Get請求,路徑上帶引數;另一種是Post請求,帶著Request報文。

Post請求報文體

由於筆者熟悉Post中報文體的轉換,知道是MappingJackson2HttpMessageConverter結合Jackson實現報文體轉換為例項的,而且也研究過Jackson, 所以解決辦法如下

建立一個針對於String.class的Jackson的反序列類:

public class StringDescrializer extends JsonDeserializer<String> { @Override public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { String value = jsonParser.getValueAsString(); if (value == null || "".equals(value.trim())) { return null; } return value; }}

 

建立一個MappingJackson2HttpMessageConverter  Bean:

@Bean@Primarypublic MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() { MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(); //設定解析JSON工具類 ObjectMapper objectMapper = new ObjectMapper(); objectMapper.getSerializerProvider().setNullValueSerializer( new JsonSerializer<Object>() { @Override public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeString(""); } } ); SimpleModule simpleModule = new SimpleModule(); simpleModule.addDeserializer(String.class, new StringDescrializer()); //註冊自定義的StringDescrializer //registerModules函式可以註冊多個Module objectMapper.registerModule(simpleModule); //忽略未知屬性 防止解析報錯 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); jsonConverter.setObjectMapper(objectMapper); List<MediaType> list = new ArrayList<>(); list.add(MediaType.APPLICATION_JSON_UTF8); jsonConverter.setSupportedMediaTypes(list); return jsonConverter;}

 

對於Post報文體來說,測試成功了。

Get路徑帶引數

上面的解決方法不適用於Get方法路徑帶引數的情況,所以需要另外想辦法了。

由於我使用過@InitBinder註解,知道可以注入自定義的PropertyEditor, 在Editor裡面可以自定義格式或者返回值,於是,自定義一個StringEditor來處理空白的問題:、

public class StringEditor extends PropertyEditorSupport { //setAsText完成字串到具體物件型別的轉換, @Override public void setAsText(String text) throws IllegalArgumentException { if (text == null || "".equals(text.trim())) { text = null; } setValue(text); } //getAsText完成具體物件型別到字串的轉換。 @Override public String getAsText() { if (getValue() != null) { return getValue().toString(); } return null; }}

 

想要全域性controller共享這個Databinder:

@ControllerAdvicepublic class GlobalControllerAdiviceController { //WebDataBinder是用來繫結請求引數到指定的屬性編輯器,可以繼承WebBindingInitializer //來實現一個全部controller共享的dataBiner @InitBinder public void dataBind(WebDataBinder binder) { ///給指定型別註冊型別轉換器操作 binder.registerCustomEditor(String.class, new StringEditor()); }}

 

對於Get路徑帶引數來說,測試也成功了

思考

解決完問題後,還是覺得不夠優雅,覺得spring 應該會考慮到這種情況,終於在spring 的文件中查閱到StringTrimmerEditor(https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-beans) 可以實現「Get」方法時引數去除空格:

只不過這個editor預設沒有註冊,需要手工註冊。

@ControllerAdvicepublic class GlobalControllerAdiviceController { //WebDataBinder是用來繫結請求引數到指定的屬性編輯器,可以繼承WebBindingInitializer //來實現一個全部controller共享的dataBiner Java程式碼 @InitBinder public void dataBind(WebDataBinder binder) { ///註冊 binder.registerCustomEditor(String.class, new StringTrimmerEditor(true)); }}

 

注意,StringTrimmerEditor構造方法中有一個引數,如果傳入true,則會將空白轉換為null. 這樣前面寫的StringEditor就不用了,spring 已經幫我們寫好了。

對於「Post」報文體來說,實際上我只需要改變的是「Jackson ObjectMapper」,不需要自定義整個MappingJackson2HttpMessageConverter  ,只需要自定義Jackson ObjectMapper.百度了一下,果然有同學已經有了解決方案:

@Beanpublic Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { return new Jackson2ObjectMapperBuilderCustomizer() { @Override public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) { jacksonObjectMapperBuilder .deserializerByType(String.class, new StdScalarDeserializer<String>(String.class) { @Override public String deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException { // 重點在這兒:如果為空白則返回null String value = jsonParser.getValueAsString(); if (value == null || "".equals(value.trim())) { return null; } return value; } }); } };}

 

把上面的自定義StringDescrializer和MappingJackson2HttpMessageConverter去掉, 只保留上面的就行。

後記

好多問題,其實spring 都已經提供瞭解決方案,但是spring體系目前太龐大了,所以好多API和功能都不為人知。所以碰上問題就記錄下來是個很好的習慣

推薦好文

強大,10k+點讚的 SpringBoot 後臺管理系統竟然出了詳細教程!

分享一套基於SpringBoot和Vue的企業級中後臺開源專案,程式碼很規範!

能掙錢的,開源 SpringBoot 商城系統,功能超全,超漂亮!

版权声明
本文为[itread01]所创,转载请带上原文链接,感谢
https://www.itread01.com/content/1610611324.html

  1. Rocketmq CPP client visual studio 2019 compilation
  2. Usage of data custom attribute in jquery
  3. Common decompression in Linux
  4. Upload large files in Java
  5. Sentry (v20.12.1) k8s cloud native architecture exploration, sentry for JavaScript manual capture event basic usage
  6. Sentry (v20.12.1) k8s cloud native architecture exploration, sentry for JavaScript manual capture event basic usage
  7. Docker + MySQL Cluster + read / write separation + MYCAT Management + vertical sub database + load balancing
  8. Docker + MySQL Cluster + read / write separation + MYCAT Management + vertical sub database + load balancing
  9. Java use interceptor infinite forwarding / redirection infinite loop / redirection times too many error (stack overflow error) solution
  10. Java use interceptor infinite forwarding / redirection infinite loop / redirection times too many error (stack overflow error) solution
  11. 010_ MySQL
  12. 010_ MySQL
  13. Fast integration of imsdk and Huawei offline push
  14. 消息队列之RabbitMQ
  15. Rabbitmq of message queue
  16. 初学java进制转换方面补充学习
  17. Learn java base conversion supplementary learning
  18. 了解一下RPC,为何诞生RPC,和HTTP有什么不同?
  19. 了解一下RPC,为何诞生RPC,和HTTP有什么不同?
  20. 初学java进制转换方面补充学习
  21. Learn about RPC, why RPC was born, and what's the difference between RPC and HTTP?
  22. Learn about RPC, why RPC was born, and what's the difference between RPC and HTTP?
  23. Learn java base conversion supplementary learning
  24. JDBC测试连接数据库
  25. JDBC test connection database
  26. 大厂面试官竟然这么爱问Kafka,一连八个Kafka问题把我问蒙了?
  27. The interviewers of big factories love to ask Kafka so much. I'm blinded by eight Kafka questions in a row?
  28. 安卓开发和java开发有什么区别!2021年BATJ30套大厂Android经典高频面试题,面试必问
  29. Spring Security OAuth2.0認證授權四:分散式系統認證授權
  30. What's the difference between Android development and java development! 2021 batj30 Android classic high frequency interview questions
  31. Spring security oauth2.0 authentication and authorization 4: distributed system authentication and authorization
  32. Java微服务 vs Go微服务,究竟谁更强!?
  33. 大厂面试官竟然这么爱问Kafka,一连八个Kafka问题把我问蒙了?
  34. Who is stronger, Java microservice vs go microservice!?
  35. Java微服务 vs Go微服务,究竟谁更强!?
  36. The interviewers of big factories love to ask Kafka so much. I'm blinded by eight Kafka questions in a row?
  37. Who is stronger, Java microservice vs go microservice!?
  38. springboot异常处理之404
  39. Spring boot exception handling 404
  40. Spring Boot Security 国际化 多语言 i18n 趟过巨坑
  41. springboot异常处理之404
  42. Spring boot security international multilingual I18N
  43. Spring boot exception handling 404
  44. Netty系列化之Google Protobuf编解码
  45. Netty之编解码
  46. Java编解码
  47. Netty解码器
  48. Netty与TCP粘包拆包
  49. Netty开发入门
  50. Java集合遍历时遇到的坑
  51. Spring IOC 源码解析(下)
  52. Spring IoC源码解析(上)
  53. Google protobuf codec of netty serialization
  54. Encoding and decoding of netty
  55. Java codec
  56. Netty decoder
  57. Netty and TCP packet sticking and unpacking
  58. Introduction to netty development
  59. Problems encountered in Java collection traversal
  60. Spring IOC source code analysis (2)