转载的。。。。。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的拦截器比理解它的要快。