`
qq123zhz
  • 浏览: 534343 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

mybatis 拦截器,改变默认行为

 
阅读更多

 页面输入:男,数据库保存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的拦截器比理解它的要快。

分享到:
评论
2 楼 renhanxiang 2015-03-09  
好像不能运行啊
1 楼 chanedi 2013-06-19  
好详细,做下来慢慢看

相关推荐

    Mybatis Interceptor 拦截器的实现

    Mybatis Interceptor 拦截器的实现是 Mybatis 框架中的一种重要机制,通过拦截器可以改变 Mybatis 的默认行为,例如 SQL 重写等。拦截器的实现主要基于责任链模式,通过动态代理组织多个拦截器(插件),这些拦截器...

    mybatis工具

    - 默认情况下,MyBatis使用JDBC的默认事务管理,可以通过配置文件切换为其他的事务管理器。 - 事务的隔离级别和传播行为可通过全局配置文件设置。 7. **MyBatis插件**: - MyBatis提供插件机制,可以拦截...

    MyBatis_plugin

    MyBatis插件机制基于Java的动态代理,通过拦截器(Interceptor)来改变MyBatis的默认行为。每个插件都包含一个或多个拦截器,拦截器会在特定的方法执行前后插入自定义的逻辑。例如,我们可以通过插件实现日志记录、...

    Mybatis中文参考文档

    - **插件机制**:MyBatis支持插件扩展,可以自定义拦截器来增强或改变默认的行为,如日志记录、性能监控等。 #### 十二、环境配置 - **多环境支持**:MyBatis支持多环境配置,可以在同一个配置文件中配置不同的...

    MyBatis 分页源码简单

    如果 PageHelper 的默认行为不能满足需求,可以通过继承 `PageInterceptor` 并重写部分方法来自定义分页逻辑。例如,可以改变 SQL 语句的生成方式,或者在分页前后执行特定操作。 分页源码解析: 在 PageHelper ...

    Mybatis插件机制详解

    Mybatis插件本质上是一种拦截器,它通过责任链模式组织多个插件,允许开发者在已映射语句执行过程中的特定点进行拦截调用,从而改变Mybatis的默认行为。 #### 二、插件功能概览 Mybatis插件主要功能包括: 1. **...

    Mybatis-Study.zip

    - **Mybatis 的插件机制**:通过实现 Interceptor 接口,你可以自定义拦截器来改变 Mybatis 的行为,例如性能监控、日志记录等。 学习 Mybatis 需要结合实际项目,动手实践,理解 SQL 映射的原理,熟悉各种标签的...

    MyBatis3--开发指南(附JavaDB实例)

    - **plugins元素**:允许开发者自定义拦截器,以扩展或修改MyBatis的行为。 ```xml ``` - **Environments元素**:定义环境变量,例如不同的数据库配置。 ```xml ${driver}"/&gt; ${url}"/&gt; ...

    若依 修改默认跳转页面 demo

    6. **自定义过滤器**:在Shiro配置中,可以创建自定义过滤器,通过拦截请求并重定向到特定页面来实现默认跳转的改变。 7. **测试与调试**:完成修改后,进行单元测试和集成测试以确保改动无误,并在不同场景下验证...

    SSM框架面试题.pdf

    - 需要在SpringMVC的配置文件中定义相关的配置信息,如视图解析器、拦截器等。 64. **在SSM架构中,如何配置和使用事务管理** - 通常通过Spring的`PlatformTransactionManager`接口来配置事务管理。可以通过`@...

    lamp-cloud微服务脚手架

    使用Mybatis拦截器实现对所有SQL的拦截,修改默认的Schema,从而实现多租户数据隔离的目的。 并且支持可插拔。 9、二级缓存 采用J2Cache操作缓存,第一级缓存使用内存(Caffeine),第二级缓存使用 Redis。 由于大量的...

    1000道 互联网大厂Java工程师面试题.pdf

    13. MyBatis插件编写及原理:通过拦截器拦截接口方法的调用,可以动态改变方法的行为。 ZooKeeper面试题的知识点包括: 1. ZooKeeper提供的功能:分布式协调服务,处理分布式锁、配置维护、集群管理等。 2. ...

    Spring 设计模式总结1

    Spring AOP中的代理(JDK动态代理或CGLIB代理)就是装饰器模式的应用,可以为Bean添加拦截器来实现事务管理、日志记录等功能。 6. **代理模式**:Spring的AOP代理(JDK或CGLIB)实现了代理模式,允许在调用目标方法...

    lamp-cloud微服务脚手架-其他

    使用Mybatis拦截器实现对所有SQL的拦截,修改默认的Schema,从而实现多租户数据隔离的目的。 并且支持可插拔。 9、二级缓存 采用J2Cache操作缓存,第一级缓存使用内存(Caffeine),第二级缓存使用 Redis。 由于大量的...

    掌握Spring设计模式:Java工程师必备指南

    8. **策略模式**:Spring MVC的拦截器和Spring Data的多数据源访问策略是策略模式的例子,可以根据策略选择不同的执行路径。 9. **模板方法模式**:Spring的JdbcTemplate和RestTemplate等提供了模板方法,定义了...

    springMVC框架学习

    application-mvc.xml文件是SpringMVC的核心配置文件,包含了SpringMVC的各种配置信息,如Bean定义、拦截器配置等。 2.4 **编写Controller类及Handler方法** 在SpringMVC中,Controller类是处理用户请求的主要...

    百度2019年最新面试题库

    它的主要功能包括表单验证、国际化支持、拦截器、视图层渲染等。 #### 什么是N层架构 N层架构是一种软件架构模式,将应用程序分为多个逻辑层次。常见的有三层架构(表示层、业务逻辑层、数据访问层)。这种架构有...

    java面试题大全.pdf

    - **`equals`**: `equals`方法最初定义在`Object`类中,其默认行为是比较两个对象的引用是否相同。然而,在很多情况下,子类会重写`equals`方法以便根据实际的业务需求来比较对象的内容或状态。例如,`String`类就...

    头条陕西Java面试题附答案

    - **装饰器模式**:在不改变对象本身的基础上增加额外的功能。 - **观察者模式**:多个对象监听一个主题对象,当主题对象的状态发生改变时,所有依赖的对象都将得到通知并自动更新。 ### 16. MyBatis框架的优缺点 ...

Global site tag (gtag.js) - Google Analytics