當前位置:
首頁 >
前端技术
> javascript
>内容正文
javascript
SpringBoot Mybatis EnumTypeHandler自定义统一处理器
生活随笔
收集整理的這篇文章主要介紹了
SpringBoot Mybatis EnumTypeHandler自定义统一处理器
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
- 需求
mybatis目前已經內嵌入了springboot中了,這說明其目前在數據訪問層的絕對優勢。而我們在開發的過程中,往往會在程序中使用枚舉(enum) 來表示一些狀態或選項,而在數據庫中使用數字來存儲。這樣做的好處是在程序中使用enum更直觀的可以知道每個值代表的狀態及含義,還可以做國際化的功能。那么這樣會帶來一個問題那就是:程序中的枚舉?與?數據庫中的數字?轉換問題。
- 介紹
抱歉,最近因為實在太忙,所以寫一半就停了。等有空繼續。不將就哈。停了大概一周的時間,在周一的早上繼續來完成這篇文章。
無論是 MyBatis 在預處理語句(PreparedStatement)中設置一個參數時,還是從結果集中取出一個值時,都會用類型處理器將獲取的值以合適的方式轉換成 Java 類型。Mybatis默認為我們實現了許多TypeHandler, 當我們沒有配置指定TypeHandler時,Mybatis會根據參數或者返回結果的不同,默認為我們選擇合適的TypeHandler處理。
對于enum而言,在mybatis中已經存在了EnumTypeHandler和EnumOrdinalTypeHandler兩大處理器,他們都是繼承自BaseTypeHandler處理器,那為何不能使用mybatis自定義的enum處理器而是要自己再定義枚舉處理器呢?下面為您一步一步剖析。
- 分析
首先說說EnumTypeHandler與EnumOrdinalTypeHandler兩者之間的區別吧:
EnumTypeHandler存入數據庫的是枚舉的name,EnumOrdinalTypeHandler存入數據庫的是枚舉的位置。例如下方的枚舉,當我們有一個枚舉值是EStatus.init時,這時我們使用mybatis?EnumTypeHandler存入數據庫的是"init"字符串;而EnumOrdinalTypeHandler存入的是3,因為init是第四個值,第一個值disable的index是0。public enum EStatus {disable("0"), enable("1"), deleted("2"),init("10"), start("11"), wait("12"), end("13");}上面是一個簡單的枚舉。它包含了7個狀態值,當我們沒有設置枚舉處理器時,mybatis默認使用EnumTypeHandler作為缺省處理器,當需要將枚舉存入數據庫時它調用的是枚舉的name()方法從而獲取的是像:disable,enable這樣的值存入了數據庫,并不是我們想要的(value)value存入數據庫,可以看看源碼:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) //package org.apache.ibatis.type;import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;public class EnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {private Class<E> type;public EnumTypeHandler(Class<E> type) {if(type == null) {throw new IllegalArgumentException("Type argument cannot be null");} else {this.type = type;}}public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {if(jdbcType == null) {ps.setString(i, parameter.name());} else {ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE);}}public E getNullableResult(ResultSet rs, String columnName) throws SQLException {String s = rs.getString(columnName);return s == null?null:Enum.valueOf(this.type, s);}public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {String s = rs.getString(columnIndex);return s == null?null:Enum.valueOf(this.type, s);}public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {String s = cs.getString(columnIndex);return s == null?null:Enum.valueOf(this.type, s);} }大家可以看到setNonNullParameter方法中ps.setString(i, parameter.name());調用的確實是name()方法。
而在將數據庫字段轉java Bean字段的時候使用的方法getNullableResult中都是調用Enum.valueOf(this.type, s),而這個方法底層調用的是get(name)方法。
所以EnumTypeHandler對我們來說并不適用,因為它在java Bean轉數據庫數據時獲取的是枚舉的name而不是value。
而EnumOrdinalTypeHandler存入數據庫的是參數的位置,所以這兩個處理器都不是我們想要的。但是我們可以根據EnumTypeHandler的設計思想轉變一下,變成符合我們想要的統一處理器。
- 教程
首先我們定義一個BaseEnumTypeHandler繼承BaseTypeHandler,并實現抽象方法。
package com.cmc.base.handler;import com.cmc.base.enums.EStatus; import com.cmc.base.utils.EnumUtil; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedTypes;import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;public class BaseEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {private Class<E> type;public BaseEnumTypeHandler() {}public BaseEnumTypeHandler(Class<E> type) {this.type = type;}@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {if (jdbcType == null) {ps.setString(i, parameter.toString());} else {ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE);}}@Overridepublic E getNullableResult(ResultSet rs, String columnName) throws SQLException {return get(rs.getString(columnName));}@Overridepublic E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return get(rs.getString(columnIndex));}@Overridepublic E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return get(cs.getString(columnIndex));}private <E extends Enum<E>> E get(String v) {if (v == null) return null;if (StringUtils.isNumeric(v)) {return get(type, Integer.parseInt(v));} else {return Enum.valueOf(type, v);}}private <E extends Enum<E>> E get(Class<E> type, int v) {Method method = null;E result = null;try {method = type.getMethod("get", int.class);result = (E)method.invoke(type, v);} catch (NoSuchMethodException e) {result = Enum.valueOf(type, String.valueOf(v));e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}return result;} }在上面的方法setNonNullParameter中大家可以看到我使用parameter.toString()替換了原來的parameter.name()方法,其實做到能統一處理枚舉的關鍵就是這里,因為toString方法是Object的方法,所以不管傳入的類型是什么都適用。而從數據庫中取出的數據轉Bean我使用的是和默認的處理器一致的方法get()。
所以我們需要在枚舉中定義toString和get方法
package com.cmc.base.enums;/*** 狀態枚舉* @author chenmc**/ public enum EStatus {disable("0"), enable("1"), deleted("2"),init("10"), start("11"), wait("12"), end("13");private final String value;private EStatus(String v) {this.value = v;}public String toString() {return this.value;}public static EStatus get(int v) {String str = String.valueOf(v);return get(str);}public static EStatus get(String str) {for (EStatus e : values()) {if(e.toString().equals(str)) {return e;}}return null;}/*public static String getName(EStatus e, Locale locale) {return I18N.getEnumName(e, locale);}*/ }大家可看到toString方法返回的是當前對象的value值,這樣就是我們想要的了。
既然方法都寫好了,那下面給大家講講如何在springboot中使用。
1.在application.properties或者application.yml中添加
mybatis:mapperLocations: classpath:mapper/*.xmltypeAliasesPackage: com.cmc.schedule.model.entitytypeHandlersPackage: com.cmc.schedule.model.handlertypeHandlersPackage就是你自定義handler的包名,上面我們定義了一個BaseEnumTypeHandler,往往我們不太會在基類中修改或添加,因為基類可能是多個模塊公用的,這時在我們的項目中定義一個EnumTypeHandler去繼承我們的BaseEnumTypeHandler。
package com.cmc.schedule.model.handler;import com.cmc.base.enums.EStatus; import com.cmc.base.handler.BaseEnumTypeHandler; import com.cmc.schedule.model.enums.EFeedback; import org.apache.ibatis.type.MappedTypes;/*** @author chenmc*/@MappedTypes(value = { EFeedback.class, EStatus.class}) public class EnumTypeHandler<E extends Enum<E>> extends BaseEnumTypeHandler<E> {public EnumTypeHandler(Class<E> type) {super(type);} }這樣有多少個enum需要轉換就可以在@MappedTypes注解中添加。是不是很方便了。
- 結語
希望可以幫助到大家,如果你有任何疑問都可以在下面留言,我會每天回復你。
總結
以上是生活随笔為你收集整理的SpringBoot Mybatis EnumTypeHandler自定义统一处理器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL查询昨天的数据
- 下一篇: SpringBoot启动如何加载appl