http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html
mybatis的原身是ibatis,現在已經脫離了apache基金會,新官網是http://www.mybatis.org/。
MyBatis 3 User Guide中的最后一章描述了注解的簡單用法,但是對於這幾個Provider的具體使用方式並沒有說的很清楚,特別是參數傳遞的方式,完全沒有提及,對於初次使用的同學來說,會造成不小的困擾。
經過一些嘗試后,我總結了一些Provider的使用經驗,下面以@SelectProvider為例,依次描述幾種典型的使用場景。
1.使用@SelectProvider
2 @SelectProvider(type = SqlProvider.class, method = "selectUser")
3 @ResultMap("userMap")
4 public User getUser(long userId);
5 }
這是一個很簡單很常用的查詢場景:根據key來查詢記錄並將結果封裝成實體bean。其中:
@ResultMap注解用於從查詢結果集RecordSet中取數據然后拼裝實體bean。
2.定義拼裝sql的類
2 public String selectUser(long userId) {
3 return "select * from user where userId=" + userId;
4 }
5 }
3.無參數@SelectProvide方法
在Mapper接口方法上和@SelectProvide指定類方法上,均無參數:
UserMapper.java:
2 @ResultMap("userMap")
3 public List<User> getAllUser();
2 return "select * from user";
3 }
4.一個參數的@SelectProvide方法
對於只有一個參數的情況,可以直接使用,參見前面的getUser和selectUser。
但是,如果在getUser方法中,對userId方法使用了@Param注解的話,那么相應selectUser方法必須接受Map<String, Object>做為參數:
UserMapper.java:
2 @ResultMap("userMap")
3 public User getUser2(@Param("userId") long userId);
SqlProvider.java:
2 return "select * from user where userId=" + para.get("userId");
3 }
5.更多參數的@SelectProvide方法
在超過一個參數的情況下,@SelectProvide方法必須接受Map<String, Object>做為參數,
如果參數使用了@Param注解,那么參數在Map中以@Param的值為key,如下例中的userId;
如果參數沒有使用@Param注解,那么參數在Map中以參數的順序為key,如下例中的password:
2 @ResultMap("userMap")
3 public User getUserCheck(@Param("userId") long userId, String password);
2 return "select * from user where userId=" + para.get("userId") + " and password='" + para.get("1") + "'";
3 }
6.一些限制
在Mapper接口和@SelectProvide方法類中,不要使用重載,也就是說,不要使用方法名相同參數不同的方法,以避免發生詭異問題。
http://www.blogjava.net/dbstar/archive/2011/08/08/355825.html
最近在總結過去一年所做的項目,由於開發周期或者對需求的把握不是太到位,每個項目隨着所做的項目進度,從需求分析到code階段總或多或少有一些自己感覺不是太完美或沒有盡善盡美的地方,使用開源框架和第三方接口只知道接口調用,對於其內部實現機理總是存在疑惑(這家伙是怎么做的,我怎么沒有想到),雖然各個項目完成后一瘸一拐的還是能滿足當初的開發需求。但是對於追求完美、刨根問底性選手,心中總有根刺,感覺不爽(不知道大家有沒有這種感覺)。下面通過自己的理解使用java原生的注解方式實現 spring aop的運行機理 (還沒看過spring /mybatis的源代碼,過年的時候研究一下大俠們有好的想法可以共享一下,話說獨樂樂不如眾樂樂哦)
先說說spring AOP使用上的一些限制:
1.如果切點函數有重載時,定義的通知類中方法不能實現方法重載
2.spring AOP只支持方法層面的切口定義,當然這個也是spring的基本原則如spring mvc 與struts2的區別之一就是spring mvc是基於方法的設計struts2是基於類的設計;
3.spring aop 不支持參數級的切口定義,如有時候需要對傳入切口的參數進行安全性,規范性、合法性處理的時候是不支持的。當然對參數處理涉及到解析參數類型獲取、參數類型判斷,對於使用反射機制獲取這個是有一定難度滴。
下面通過詳細的代碼,來講解如何通過使用java的annotation自定義切點接口和通過反射機制實現spring AOP機制。
第一步:自定義AOP中需要的注解接口
class層注解定義:
用途:類接口切點注解定義
package com.dbc.yangg.project;
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * * @ClassName: MyClassAnnotation * @Description: 類層注解定義 * @author guoyang2011@gmail.com * @date 2014年1月18日 下午11:37:51 * */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface MyClassAnnotation { String value() default "firstAnno"; }
method層advice定義類似於Spring AOP中@Before,@After,@Around,@AfterThrowing,@AfterReturning等
用途:方法切點接口注解定義
package com.dbc.yangg.project;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * * @ClassName: MySecondMethodAnnotation * @Description: 方法層注解定義 * @author guoyang2011@gmail.com * @date 2014年1月18日 下午11:44:44 * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyPointcutAnnotation { /** * * @Title: className * @Description: TODO * @param @return advicer class type * @return Class<?> * @throws */ Class<?> className(); /** * * @Title: method * @Description: TODO * @param @return advicer method name * @return String * @throws */ String method();// }
method parameters 層advicer定義類似mybatis中@Param注解
功能:參數層面切點接口注解定義
package com.dbc.yangg.project;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * * @ClassName: MyParameterAnnotation * @Description: 方法中參數層注解定義 * @author guoyang2011@gmail.com * @date 2014年1月18日 下午11:45:15 * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface MyParameterAnnotation { /** * * @Title: opType * @Description: method 參數類型檢查 * @param @return * @return MyAopParameterOPType[] * @throws */ MyAopParameterOPType[] opType(); /** * * @Title: paraType * @Description: method 參數類型 * @param @return * @return Class<?> * @throws */ Class<?> paraType(); }
處理MyAopParameterOPType類型定義
功能:定義參數切口定義注解處理的類型
package com.dbc.yangg.project;
/** * * @ClassName: MyAopParameterOPType * @Description: TODO * @author guoyang2011@gmail.com * @date 2014年1月18日 下午12:07:25 * */ public enum MyAopParameterOPType { checkForDB("DB"),//特殊字符處理 ,通用轉碼或者其他處理 checkForSecurity("security"),//參數是否合法等操作 checkDeleteAuthority("DeleteAuthority"), checkUpdateAuthority("UpdateAuthority"); private String value; private MyAopParameterOPType(String value){ this.value=value; } public String getValue(){ return this.value; } }
第二步:自定義AOP管理模塊
主要功能:自定義AOP處理類,負責解析實際調用切點函數定義的通知
package com.dbc.yangg.project;
import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.apache.ibatis.annotations.SelectProvider; /** * * @ClassName: MyAOPUtils * @Description: * @author guoyang2011@gmail.com * @date 2014年1月18日 下午12:01:05 * */ public class MyAOPUtils { /** * * @Title: myAopUtilsManager * @Description: 切點函數參數觸發通知事件,所有通知事件的處理接口 * @param @param advicerManagerClass * @param @param indexArg * @param @param paraValues * @param @param paraTypes * @param @return * @return boolean * @throws */ private static boolean parameterAdvicerUtils(Annotation advicerManagerClass,int indexArg,Object[] paraValues, Class<?>... paraTypes){ if(advicerManagerClass instanceof MyPointcutAnnotation){ //接口參數驗證通過后執行 //切點通知處理 MyPointcutAnnotation AdvicerClass=(MyPointcutAnnotation)advicerManagerClass; Class<?> adviceClass=AdvicerClass.className(); try { Method adviceMethod=adviceClass.getMethod(AdvicerClass.method(),paraTypes); adviceMethod.invoke(adviceClass.newInstance(), paraValues); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } }else if(advicerManagerClass instanceof MyParameterAnnotation){ //對輸入的參數做一些通用處理和安全性檢查等等 //一般用在DAO層sql拼裝特殊字符檢查,數據格式合法性檢查,轉碼或對調用者使用定義切點接口使用權限,安全性,等信息進行檢查和確認等場合;當切點參數通過通知類中定義的接口處理后再調用切點方法;如MyBatis中通過注解@SelectProvider方式生成合法的SQL語句需要對拼裝的sql語句傳入的參數進行驗證等,還有就是從安全新考慮對傳入的模塊調用者身份進行檢查,MyBatis通過注解方式定義接口實現動態SQL生成中關鍵就是對數據類型的解析 if(indexArg<0){ return true; } MyParameterAnnotation AdvicerClass=(MyParameterAnnotation)advicerManagerClass; //處理切點方法參數的通知,大概思路如下: //1.解析參數類型 //2.獲取參數值 //3.調用通知處理接口檢查參數合法性 //4.返回檢查結果 true:false } return true; } /** * * @Title: myAopUtilsManager * @Description: Pointcut method execute advice interface * @param @param advicerManagerClass 類型 * @param @param paraValues pointcut傳入參數 * @param @param paraTypes pointcut傳入參數類型 * @return void * @throws */ private static boolean methodAdvicerUtils(Annotation advicerManagerClass,Object[] paraValues, Class<?>... paraTypes){ return parameterAdvicerUtils(advicerManagerClass,-