轉載的。。。。。http://zhenghuazhi.iteye.com/blog/1468992
頁面輸入:男,數據庫保存male,女,數據庫保存為female。
使用interceptor,typeHandler
- package cn.dcr.mybatis.util;
 - import java.util.Properties;
 - import org.apache.ibatis.executor.Executor;
 - import org.apache.ibatis.mapping.MappedStatement;
 - 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 org.apache.ibatis.session.ResultHandler;
 - import org.apache.ibatis.session.RowBounds;
 - import com.test.pojos.User;
 - @Intercepts({
 - @Signature(type = Executor.class, method = "update", args = {
 - MappedStatement.class, Object.class }),
 - @Signature(type = Executor.class, method = "query", args = {
 - MappedStatement.class, Object.class, RowBounds.class,
 - ResultHandler.class }) })
 - public class MyInterceptor implements Interceptor {
 - private static final String R_FEMALE = "女";
 - private static final String R_MALE = "男";
 - private static final String FEMALE = "female";
 - private static final String MALE = "male";
 - private Properties properties;
 - /*
 - * (non-Javadoc)
 - *
 - * @see
 - * org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin
 - * .Invocation)
 - */
 - public Object intercept(Invocation invocation) throws Throwable {
 - MappedStatement mappedStatement = (MappedStatement) invocation
 - .getArgs()[0];
 - String sqlId = mappedStatement.getId();
 - String namespace = sqlId.substring(0, sqlId.indexOf('.'));
 - Executor exe = (Executor) invocation.getTarget();
 - String methodName = invocation.getMethod().getName();
 - if (methodName.equals("query")) {
 - Object parameter = invocation.getArgs()[1];
 - RowBounds rowBounds = (RowBounds) invocation.getArgs()[2];
 - }
 - else if(methodName.equals("update")){
 - Object parameter = invocation.getArgs()[1];
 - if(parameter instanceof User)
 - ((User)parameter).setGender(saveValueToDb(((User)parameter).getGender()));
 - }
 - return invocation.proceed();
 - }
 - /*
 - * (non-Javadoc)
 - *
 - * @see org.apache.ibatis.plugin.Interceptor#plugin(java.lang.Object)
 - */
 - public Object plugin(Object target) {
 - return Plugin.wrap(target, this);
 - }
 - /*
 - * (non-Javadoc)
 - *
 - * @see
 - * org.apache.ibatis.plugin.Interceptor#setProperties(java.util.Properties)
 - */
 - public void setProperties(Properties properties) {
 - this.properties = properties;
 - }
 - /**插入數據庫
 - * @param value
 - * @return
 - */
 - private String saveValueToDb(String value) {
 - if (value.equals(R_FEMALE)) {
 - return FEMALE;
 - } else if (value.equals(R_MALE)) {
 - return MALE;
 - } else {
 - throw new IllegalArgumentException("數據庫異常!" + value);
 - }
 - }
 - }
 
- @Intercepts({
 - @Signature(type = Executor.class, method = "update", args = {
 - MappedStatement.class, Object.class }),
 - @Signature(type = Executor.class, method = "query", args = {
 - MappedStatement.class, Object.class, RowBounds.class,
 - ResultHandler.class }) })
 
mybatis 攔截器好像只能使用注解,而且對於接口Executor,method只定義了update,query,flushStatements,commit,rollback,createCacheKey,isCached,clearLocalCache,deferLoad,getTransaction,close,isClosed這幾個方法,沒有delete和insert方法。
具體詳見:
- package org.apache.ibatis.executor;
 - import org.apache.ibatis.cache.CacheKey;
 - import org.apache.ibatis.mapping.MappedStatement;
 - import org.apache.ibatis.reflection.MetaObject;
 - import org.apache.ibatis.session.ResultHandler;
 - import org.apache.ibatis.session.RowBounds;
 - import org.apache.ibatis.transaction.Transaction;
 - import java.sql.SQLException;
 - import java.util.List;
 - public interface Executor {
 - ResultHandler NO_RESULT_HANDLER = null;
 - int update(MappedStatement ms, Object parameter) throws SQLException;
 - List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
 - List<BatchResult> flushStatements() throws SQLException;
 - void commit(boolean required) throws SQLException;
 - void rollback(boolean required) throws SQLException;
 - CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds);
 - boolean isCached(MappedStatement ms, CacheKey key);
 - void clearLocalCache();
 - void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key);
 - Transaction getTransaction();
 - void close(boolean forceRollback);
 - boolean isClosed();
 - }
 
當我疑惑為什么mybatis沒有在insert和delete的時候攔截呢?
看到了mybatis googleCode上的issue16。
| Issue 16: | Update interceptor (plugin) fires for inserts | 
- Reporter: Eli Kizhnerman
 - The iBatis plugin does not seem to be resolving the target methods to
 - intercept properly. An interceptor that is defined to intercept only
 - updates is also executed on inserts.
 - I have the following definition:
 - @Intercepts({@Signature(
 - type=Executor.class,
 - method="update",
 - args={MappedStatement.class,Object.class})})
 - public class UpdateInterceptor implements Interceptor {
 - ...
 - }
 - In Plugin.invoke you have the following code:
 - Set<Method> methods = signatureMap.get(method.getDeclaringClass());
 - if (methods != null && methods.contains(method)) {
 - return interceptor.intercept(new Invocation(target, method, args));
 - }
 - DefaultSqlSession.insert internally calls an update method:
 - public int insert(String statement, Object parameter) {
 - return update(statement, parameter);
 - }
 - I suspect that the Method that is being passed to plugin.invoke is actually
 - the update method and therefore the interceptor is executed.
 
原來在DefaultSqlSession的insert,delete方法也是調用了update方法。
- public int insert(String statement, Object parameter) {
 - return update(statement, parameter);
 - }
 
- public int delete(String statement, Object parameter) {
 - return update(statement, wrapCollection(parameter));
 - }
 
mybatis太不厚道了,它的文檔也沒有具體的說明。
有人提出這是個bug,不過mybatis沒有承認。
- Clinton Begin added a comment - 10/Dec/09 03:58 PM
 - Executor only has update and query methods. Thus inserts and deletes are also
 - considered updates. It sounds like to do what you want, we need another interception
 - point, at the session level. You can do so right now with a proxy class between the
 - SqlSession interface and the default implementation.
 - [ Show » ]
 - Clinton Begin added a comment - 10/Dec/09 03:58 PM Executor only has update and query
 - methods. Thus inserts and deletes are also considered updates. It sounds like to do
 - what you want, we need another interception point, at the session level. You can do
 - so right now with a proxy class between the SqlSession interface and the default
 - implementation.
 - [ Permalink | Delete | « Hide ]
 - Eli Kizhnerman added a comment - 11/Dec/09 12:28 PM
 - This is not working the way I expected it but that it is not a bug. I can get what I
 - need from the MappedStatement SqlCommandType.
 
- package cn.dcr.mybatis.util;
 - import java.sql.CallableStatement;
 - import java.sql.PreparedStatement;
 - import java.sql.ResultSet;
 - import java.sql.SQLException;
 - import org.apache.ibatis.type.BaseTypeHandler;
 - import org.apache.ibatis.type.JdbcType;
 - /**自定義typeHandler<br/>
 - * 1 插入數據庫,男轉為male
 - * 2 查詢,maile轉為男
 - * @author Administrator
 - *
 - */
 - public class GenderTypeHandlerCallback extends BaseTypeHandler<String> {
 - private static final String R_FEMALE = "女";
 - private static final String R_MALE = "男";
 - private static final String FEMALE = "female";
 - private static final String MALE = "male";
 - @Override
 - public String getNullableResult(CallableStatement cs, int columnIndex)
 - throws SQLException {
 - return cs.getString(columnIndex);
 - }
 - @Override
 - public String getNullableResult(ResultSet rs, String columnName)
 - throws SQLException {
 - String t = rs.getString(columnName);
 - return converSex(t);
 - }
 - @Override
 - public void setNonNullParameter(PreparedStatement preparedStatement, int i,
 - String str, JdbcType jdbcType) throws SQLException {
 - preparedStatement.setString(i, saveValueToDb(str));
 - }
 - /**插入數據庫
 - * @param value
 - * @return
 - */
 - private String saveValueToDb(String value) {
 - if (value.equals(R_FEMALE)) {
 - return FEMALE;
 - } else if (value.equals(R_MALE)) {
 - return MALE;
 - } else {
 - throw new IllegalArgumentException("數據庫異常!" + value);
 - }
 - }
 - /** 從數據庫讀出
 - * @param value
 - * @return
 - */
 - private String converSex(String value){
 - if (value.equals(FEMALE)) {
 - return R_FEMALE;
 - } else if (value.equals(MALE)) {
 - return R_MALE;
 - } else {
 - throw new IllegalArgumentException("數據庫異常!" + value);
 - }
 - }
 - }
 
在自定義typeHandler的getNullableResult方法中調整自己的結果集,可以說是在sql執行后,IOC的理論叫后置通知,interceptor應該是前置通知。
- @Override
 - public String getNullableResult(ResultSet rs, String columnName)
 - throws SQLException {
 - String t = rs.getString(columnName);
 - return converSex(t);
 - }
 
在typeHandler的setNonNullParameter,應該是參數傳入前轉換參數的功能,但是實際操作中沒有被調用,不知道為什么。
- @Override
 - public void setNonNullParameter(PreparedStatement preparedStatement, int i,
 - String str, JdbcType jdbcType) throws SQLException {
 - preparedStatement.setString(i, saveValueToDb(str));
 - }
 
以上是對mybatis 的切入點的實踐。總體感覺和struts2的攔截器比,還有差距,至少我理解struts2的攔截器比理解它的要快。

