實現目的:為了存儲了公共字典表主鍵的其他表在查詢的時候不用關聯查詢(所以攔截位置位於mybaits語句查詢得出結果集後) 項目環境 :springboot+mybaits 實現步驟:自定義註解——自定義實現mybaits攔截器——註冊mybaits攔截器 一、自定義註解 1.1 代碼示例 @Targ ...
實現目的:為了存儲了公共字典表主鍵的其他表在查詢的時候不用關聯查詢(所以攔截位置位於mybaits語句查詢得出結果集後)
項目環境 :springboot+mybaits
實現步驟:自定義註解——自定義實現mybaits攔截器——註冊mybaits攔截器
一、自定義註解
1.1 代碼示例
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.FIELD})// @Retention(RetentionPolicy.RUNTIME)//該註解不僅被保存到class文件中,jvm載入class文件之後,仍然存在; @Inherited//允許子類繼承父類的註解。 (子類中可以獲取並使用父類註解) @Documented//指明修飾的註解,可以被例如javadoc此類的工具文檔化,只負責標記,沒有成員取值。 /** * 該自定義註解用於所查詢語句中欄位包含字典表主鍵 並需要將主鍵同時對照成字典表對應的名稱 * 將該註解放置在名稱列,參數為字典表主鍵存儲列的名字 * @ClassName: DictReplace * 描述: TODO 用於字典名稱欄位預設為空,則空則認為字典id欄位名為 字典名稱字典.substring(0,length()-4) 若不為空則認定字典id欄位名稱為參數值 * 作者cy * 時間 2019年3月26日 上午9:02:47 * */ public @interface DictReplace { String dictIdFieldName() default ""; }
@Target 註解
功能:指明瞭修飾的這個註解的使用範圍,即被描述的註解可以用在哪裡。
ElementType的取值包含以下幾種:
- TYPE:類,介面或者枚舉
- FIELD:域,包含枚舉常量
- METHOD:方法
- PARAMETER:參數
- CONSTRUCTOR:構造方法
- LOCAL_VARIABLE:局部變數
- ANNOTATION_TYPE:註解類型
- PACKAGE:包
@Retention 註解
功能:指明修飾的註解的生存周期,即會保留到哪個階段。
RetentionPolicy的取值包含以下三種:
- SOURCE:源碼級別保留,編譯後即丟棄。
- CLASS:編譯級別保留,編譯後的class文件中存在,在jvm運行時丟棄,這是預設值。
- RUNTIME: 運行級別保留,編譯後的class文件中存在,在jvm運行時保留,可以被反射調用。
@Documented 註解
功能:指明修飾的註解,可以被例如javadoc此類的工具文檔化,只負責標記,沒有成員取值。
@Inherited註解
功能:允許子類繼承父類中的註解。
1.2 使用場景
@TableField("runtime_platform") private Integer runtimePlatform; @DictReplace//字典替換註解 @TableField(exist = false) private String runtimePlatformName;
二、自定義mybaits攔截器並註冊
2.1 代碼示例
import java.util.List; import java.util.Properties; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import com.msunsoft.base.common.factory.ConstantFactory; import com.msunsoft.base.common.interceptor.annotation.DictReplace; import com.msunsoft.base.spring.SpringContextHolder; import com.msunsoft.base.util.ToolUtil; import org.apache.ibatis.executor.resultset.ResultSetHandler; import java.lang.reflect.Field; import java.sql.Statement; /** * 字典替換攔截器,當註解方法被執行後攔截並修改查詢後的結果 * @ClassName: DictReplaceInteceptor * 描述: TODO * 作者 * 時間 2019年3月25日 下午7:23:41 * */ @Intercepts({ @Signature(type = ResultSetHandler.class,method = "handleResultSets", args = { Statement.class }) }) public class DictReplaceInteceptor implements Interceptor{ private Properties properties; private SpringContextHolder spring;//實現 ApplicationContextAware 介面的類包含獲取spring容器中的bean的靜態方法 @Override @SuppressWarnings(value = {"all"}) public Object intercept(Invocation invocation) throws Throwable { //因為 handleResultSets 方法執行結束後可以收到一個list類型的數據結果集,所以雖然該方法的目的是用於結束本次攔截,執行預定方法(handleResultSets)方便下次攔截 List<Object> results = (List<Object>)invocation.proceed(); try{
//自定義方法用於判斷對象是否為空 if(ToolUtil.isNotEmpty(results)){
//ConstantFactory 是自定義的包含常用方法的一個類,現在用到的是它包含在其中的通過字典主鍵獲取字典名稱的方法 ConstantFactory constantFactory = spring.getBean(ConstantFactory.class); Class<?> cls = results.get(0).getClass(); Field[] fields = cls.getDeclaredFields();// 獲取private修飾的成員變數 獲得某個類的所有聲明的欄位,即包括public、private和proteced,但是不包括父類的申明欄位。 for(Object result:results){ for (Field field : fields) { //獲取我們自定義的註解 DictReplace dictReplace = field.getAnnotation(DictReplace.class); if(dictReplace!=null){//如果存在這個註解 我們在執行後續方法 String dictIdFieldName = dictReplace.dictIdFieldName();//獲取註解屬性值 Field idField = null; if(ToolUtil.isNotEmpty(dictIdFieldName)){ idField = cls.getDeclaredField(dictIdFieldName);//獲取實體類對應欄位 }else{ String fieldName = field.getName();//獲取實體類欄位名稱 String idFieldName = fieldName.substring(0,fieldName.length()-4); idField = cls.getDeclaredField(idFieldName); } idField.setAccessible(true);//允許我們在用反射時訪問私有變數 Object dictId = idField.get(result);//從返回值中獲得欄位對應的 值 field.setAccessible(true); if(ToolUtil.isNotEmpty(dictId)){ field.set(result, constantFactory.getDictName( Long.valueOf(new String(dictId.toString())) ) ); //用字典id查詢出字典名稱 並替換結果集中的值 } } } } } }catch (Exception e) { e.printStackTrace(); }finally{ return results; } } @Override public Object plugin(Object target) { // 讀取@Signature中的配置,判斷是否需要生成代理類 if (target instanceof ResultSetHandler) { return Plugin.wrap(target, this);//返回代理 } else { return target; } } @Override public void setProperties(Properties properties) { this.properties = properties; } }
2.2 攔截器部分知識點
2.1.1 MyBatis 允許你在已映射語句執行過程中的某一點進行攔截調用。預設情況下,MyBatis 允許使用插件來攔截的方法調用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
2.1.2 MyBatis攔截器的介面定義
一共有三個方法intercept
、plugin
、setProperties
setProperties()
方法主要是用來從配置中獲取屬性。
plugin()
方法用於指定哪些方法可以被此攔截器攔截。
intercept()
方法是用來對攔截的sql
進行具體的操作。
註解實現
MyBatis
攔截器用到了兩個註解:@Intercepts
和@Signature
@Intercepts( { @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), } )
type
的值與類名相同,method
與方法名相同,為了避免方法重載,args
中指定了各個參數的類型和個數,可通過invocation.getArgs()
獲取參數數組。
2.1.3 Spring Boot整合
方法一
如果是使用xml式配置攔截器,可在Mybatis配置文件中添加如下節點,屬性可以以如下方式傳遞
<plugins> <plugin interceptor="tk.mybatis.simple.plugin.XXXInterceptor"> <property name="propl" value="valuel" /> <property name="prop2" value="value2" /> </plugin> </plugins>
方法二
如果在Spring boot
中使用,則需要單獨寫一個配置類,如下:
@Configuration public class MybatisInterceptorConfig { @Bean public String myInterceptor(SqlSessionFactory sqlSessionFactory) { ExecutorInterceptor executorInterceptor = new ExecutorInterceptor(); Properties properties = new Properties(); properties.setProperty("prop1","value1"); executorInterceptor.setProperties(properties); return "interceptor"; } }
OR
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import com.msunsoft.base.common.interceptor.mybaits.DictReplaceInteceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; @Configuration @EnableTransactionManagement @MapperScan("com.msunsoft.**.mapper")//Mapper介面掃描 public class DataSourceConfig { /** * 樂觀鎖mybatis插件 */ @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } /** * mybatis-plus分頁插件 */ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } @Bean public DictReplaceInteceptor dictReplaceInteceptor(){ return new DictReplaceInteceptor(); } }
方法三
在攔截器上加@Component註解
ps:
一、引用並參考
1.《深入理解mybatis原理》 MyBatis的架構設計以及實例分析
https://blog.csdn.net/luanlouis/article/details/40422941
2.關於mybatis攔截器,對結果集進行攔截
https://www.cnblogs.com/SmallHan/articles/8127327.html
3.Springboot2(22)Mybatis攔截器實現
https://blog.csdn.net/cowbin2012/article/details/85256360
二、涉及技術點
spring(註解、AOP) ,java反射與動態代理,mybaits(以上代碼示例用的是mybaits-Plus 3.0.6.jar),