mybatis-3.4.x 从源码看configuration[笔记一]

前提小知识

  1. 数据库操作的常规步骤
1.加载数据库驱动
2.根据认证信息获取数据库连接
3.开启事务
4.创建statement
5.执行sql
6.处理结果集
7.提交事务
8.关闭资源
  1. mybatis官方学习文档地址
http://www.mybatis.org/mybatis-3/

从源码看mybatis configuration 中几个主要的配置都是什么作用

/**
   * 解析mybatis配置文件,从根节点configuration开始解析
   * @param root
   */
  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      /*读取配置的属性信息 */
      propertiesElement(root.evalNode("properties"));
      /*解析setting节点*/
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      /*实体类型别名注册*/
      typeAliasesElement(root.evalNode("typeAliases"));
      /*拦截器注册*/
      pluginElement(root.evalNode("plugins"));
      /*对象工厂*/
      objectFactoryElement(root.evalNode("objectFactory"));
      /*对象包装工厂*/
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      /*自定义反射器工厂类*/
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      /*多数据库厂商 数据库ID的生成实现类*/
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      /*注册Java类型 与 数据库字段类型的对应关系 处理器*/
      typeHandlerElement(root.evalNode("typeHandlers"));
      /*注册数据库操作的接口*/
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

typeAliases 实体别名配置 这个理解和使用都比较简单【注:大小写不敏感 别名全部会转成小写】

1.注册类的别名

  /*****详见TypeAliasRegistry.java start*******/
  public void registerAlias(Class<?> type) {
    /*默认是类的简单名称*/
    String alias = type.getSimpleName();
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    if (aliasAnnotation != null) {
      /*如果有Alias注解且值不为空,则使用注解配置的别名注册*/
      alias = aliasAnnotation.value();
    } 
    registerAlias(alias, type);
  }
  
  public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // issue #748
    /*转成小写*/
    String key = alias.toLowerCase(Locale.ENGLISH);
    if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
    }
    TYPE_ALIASES.put(key, value);
  }
  /*****详见TypeAliasRegistry.java end*******/

2.别名的使用

  /*****详见BaseBuilder.java start*******/
  protected Class<?> resolveAlias(String alias) {
    return typeAliasRegistry.resolveAlias(alias);
  }
   /*****详见BaseBuilder.java end*******/
  
  /*****详见TypeAliasRegistry.java start*******/
  public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {
        return null;
      }
      // issue #748
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      if (TYPE_ALIASES.containsKey(key)) {
        /*如果存在别名就直接按照别名获取class*/
        value = (Class<T>) TYPE_ALIASES.get(key);
      } else {
        /*不存在别名配置则按照全路径获取class*/
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
  }
  /*****详见TypeAliasRegistry.java end*******/
/*****别名注册简单示例 start*******/
package typeAlias;

import org.apache.ibatis.type.Alias;

@Alias("testAlias")
public class TestAlias {
    private String test;

    public String getTest() {
        return test;
    }

    public void setTest(String test) {
        this.test = test;
    }
}
/*****别名注册简单示例 end*******/
<!--在mapper xml里面可以使用别名的地方 【不限于下面这些地方可以使用别名】-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="test.CachedAuthorMapper">
  <!--别名方式 -->
  <parameterMap id="s" type="testAlias" >
     <parameter property="test" javaType="string" jdbcType="VARCHAR"/>
  </parameterMap>
  <resultMap id="BASE_MAP" type="testAlias">
     <result jdbcType="VARCHAR" javaType="string" property="test" column="username"/>
  </resultMap>
  <select id="testAlias" resultType="testAlias" parameterType="testAlias">
  </select>
  <!--全路径方式 -->
  <select id="selectAuthorWithInlineParams"
          parameterType="int"
          resultType="org.apache.ibatis.domain.blog.Author">
    select * from author where id = #{id}
  </select>
</mapper>

plugins mybatis拦截器

  1. 拦截器的注册
<!-- 配置文件方式加入拦截器 -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    ...
    <typeAliases>
        <package name="typeAlias"/>
    </typeAliases>
    <plugins>
       <!-- 可以直接使用别名 -->
        <plugin interceptor="testPlugin">
            <property name="testProd" value="hello  mybatis plugin"/>
        </plugin>
    </plugins>
  ...
</configuration>

拦截器自定义实现简单示例

@Alias("testPlugin")
/*声明要拦截的类和方法【明确指定方法参数个数和类型】*/
@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})
})
public class TestPlugin implements Interceptor {
    private final Logger logger = LoggerFactory.getLogger(TestPlugin.class);
    private String testProd;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        logger.info(testProd);
        return invocation.getMethod().invoke(invocation.getTarget(),invocation.getArgs());
    }

    @Override
    public Object plugin(Object target) {
        /*使用mybatis为我们提供好的默认处理方式*/
        return Plugin.wrap(target,this);
    }

    @Override
    public void setProperties(Properties properties) {
       this.testProd = properties.getProperty("testProd");
    }
}

注册拦截器的解析入口

  /************详见XMLConfigBuilder.java******************************* */
  private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        String interceptor = child.getStringAttribute("interceptor");
        Properties properties = child.getChildrenAsProperties();
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        interceptorInstance.setProperties(properties);
        //在InterceptorChain中注册
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

拦截器的实际注册类

 public class InterceptorChain {

 private final List<Interceptor> interceptors = new ArrayList<Interceptor>();

 /*为拦截对象返回代理对象*/
 public Object pluginAll(Object target) {
   for (Interceptor interceptor : interceptors) {
     target = interceptor.plugin(target);
   }
   return target;
 }
 
 /**
 *注册拦截器
 */
 public void addInterceptor(Interceptor interceptor) {
   interceptors.add(interceptor);
 }
 
 public List<Interceptor> getInterceptors() {
   return Collections.unmodifiableList(interceptors);
 }
}

2.拦截器的使用
mybatis默认会在以下四个对象上使用plugin

  • ParameterHandler
  • ResultSetHandler
  • StatementHandler
  • Executor
 /***************详见Configuration.java start**************************/
 /**
 *为ParameterHandler对象生成代理对象
 */
 public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }
  /***************详见Configuration.java end**************************/
  
  /***************InterceptorChain.java start**************************/
  /**
  * 调用拦截器plugin方法生成代理对象 默认实现为Plugin.wrap 详见下文
  */
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }
  /***************InterceptorChain.java end**************************/
  

拦截器代理对象InvocationHandler实现,真正处理切面逻辑的地方

package org.apache.ibatis.plugin;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.ibatis.reflection.ExceptionUtil;

/**
 * @author Clinton Begin
 */
public class Plugin implements InvocationHandler {

  private final Object target;
  private final Interceptor interceptor;
  private final Map<Class<?>, Set<Method>> signatureMap;

  private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
    this.target = target;
    this.interceptor = interceptor;
    this.signatureMap = signatureMap;
  }

  /**
   * 返回对象的代理对象
   * @param target
   * @param interceptor
   * @return
   */
  public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    /*如果方法是拦截器要拦截的方法,则调用拦截器的拦截方法*/
    try {
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      if (methods != null && methods.contains(method)) {
        return interceptor.intercept(new Invocation(target, method, args));
      }
      /*不在拦截列表则不做任何处理*/
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }

  /**
   * 获取拦截器拦截的方法列表
   * @param interceptor
   * @return
   */
  private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
    // issue #251
    if (interceptsAnnotation == null) {
      throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());      
    }
    Signature[] sigs = interceptsAnnotation.value();
    Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
    for (Signature sig : sigs) {
      Set<Method> methods = signatureMap.get(sig.type());
      if (methods == null) {
        methods = new HashSet<Method>();
        signatureMap.put(sig.type(), methods);
      }
      try {
        Method method = sig.type().getMethod(sig.method(), sig.args());
        methods.add(method);
      } catch (NoSuchMethodException e) {
        throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
      }
    }
    return signatureMap;
  }

  private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
    Set<Class<?>> interfaces = new HashSet<Class<?>>();
    while (type != null) {
      for (Class<?> c : type.getInterfaces()) {
        if (signatureMap.containsKey(c)) {
          interfaces.add(c);
        }
      }
      type = type.getSuperclass();
    }
    return interfaces.toArray(new Class<?>[interfaces.size()]);
  }
}

objectFactory 返回结果对象生成工厂 下面是默认实现

package org.apache.ibatis.reflection.factory;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.ibatis.reflection.ReflectionException;

/**
 * @author Clinton Begin
 */
public class DefaultObjectFactory implements ObjectFactory, Serializable {

  private static final long serialVersionUID = -8855120656740914948L;

  @Override
  public <T> T create(Class<T> type) {
    return create(type, null, null);
  }

  @SuppressWarnings("unchecked")
  @Override
  public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    Class<?> classToCreate = resolveInterface(type);
    // we know types are assignable
    return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
  }

  @Override
  public void setProperties(Properties properties) {
    // no props for default
  }

  private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    try {
      Constructor<T> constructor;
      if (constructorArgTypes == null || constructorArgs == null) {
        constructor = type.getDeclaredConstructor();
        if (!constructor.isAccessible()) {
          constructor.setAccessible(true);
        }
        return constructor.newInstance();
      }
      constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
      if (!constructor.isAccessible()) {
        constructor.setAccessible(true);
      }
      return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
    } catch (Exception e) {
      StringBuilder argTypes = new StringBuilder();
      if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
        for (Class<?> argType : constructorArgTypes) {
          argTypes.append(argType.getSimpleName());
          argTypes.append(",");
        }
        argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
      }
      StringBuilder argValues = new StringBuilder();
      if (constructorArgs != null && !constructorArgs.isEmpty()) {
        for (Object argValue : constructorArgs) {
          argValues.append(String.valueOf(argValue));
          argValues.append(",");
        }
        argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
      }
      throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
    }
  }

  protected Class<?> resolveInterface(Class<?> type) {
    Class<?> classToCreate;
    if (type == List.class || type == Collection.class || type == Iterable.class) {
      classToCreate = ArrayList.class;
    } else if (type == Map.class) {
      classToCreate = HashMap.class;
    } else if (type == SortedSet.class) { // issue #510 Collections Support
      classToCreate = TreeSet.class;
    } else if (type == Set.class) {
      classToCreate = HashSet.class;
    } else {
      classToCreate = type;
    }
    return classToCreate;
  }

  @Override
  public <T> boolean isCollection(Class<T> type) {
    return Collection.class.isAssignableFrom(type);
  }

}

environments 环境 顾名思义 可以配置多个隔离的环境 -> 开发/ 测试/ 预发/ 生产

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    ...
     <!-- 默认环境-->
    <environments default="dev">
        <!-- 开发-->
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://xxx.xxx.xxx:3306/test?useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
         <!--测试 -->
        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://xxx.xxx.xxx:3306/test?useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
         <!--生产 -->
        <environment id="prod">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://xxx.xxx.xxx:3306/test?useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
   ...
</configuration>

简单使用示例

 public static void main(String[] args) {
        InputStream inputStream;
        try {
            inputStream = Resources.getResourceAsStream("mybatis.xml");
            //开发 
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream,"dev");
            // 测试 
    //SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream,"test");
    //生产
       // SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream,"prod");
            SqlSession sqlSession = sqlSessionFactory.openSession();
            CachedAuthorMapper cachedAuthorMapper = sqlSession.getMapper(CachedAuthorMapper.class);
            Author author = cachedAuthorMapper.selectAllAuthors(1);

            sqlSession.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

databaseIdProvider 生成数据库厂商标识

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://xxx.xxx.xxx:3306/test?useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--DB_VENDOR 为 VendorDatabaseIdProvider 别名 -->
    <databaseIdProvider type="DB_VENDOR">
        <!-- mysql数据库标识-->
        <property name="MySQL" value="mysql"/>
        <!-- Oracle数据库标识-->
        <property name="oracle" value="oracle"/>
    </databaseIdProvider>
    <mappers>
        <mapper resource="test/CachedAuthorMapper.xml"/>
    </mappers>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="ddshuai.CachedAuthorMapper">
  <!--当数据库为MySQL 执行这一条-->
  <select id="searchNow" databaseId="mysql" resultType="date">
    select now() from dual
  </select>
<!--当数据库为Oracle 执行这一条-->
  <select id="searchNow" databaseId="oracle" resultType="date">
    select sysdate() from dual
  </select>
</mapper>

typeHandler java类型 与 数据库类型 映射处理器

简单实现一个typeHandler 下面的typeHandler负责加密数据库的自增主键 并实现可逆转换

@Alias("idHandler")
public final class IdTypeHandler extends BaseTypeHandler<String> {

	@Override
	public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
			throws SQLException {
		ps.setLong(i, IDEncodeUtil.decode(parameter));
	}

	@Override
	public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
		final long l = rs.getLong(columnName);
		return IDEncodeUtil.encode(l);
	}

	@Override
	public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
		final long l = rs.getLong(columnIndex);
		return IDEncodeUtil.encode(l);
	}

	@Override
	public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
		final long l = cs.getLong(columnIndex);
		return IDEncodeUtil.encode(l);
	}
	
}
public abstract class IDEncodeUtil {

	public static String encode(long l) {
		if (l < 0) {
			return Long.toString(l);
		} else{
			l = mix(l);
			return Long.toString(l, 36);
		}
	}

	public static long decode(String s) {
		if(s.startsWith("-")) {
			return Long.parseLong(s);
		} else {
			return demix(Long.parseLong(s, 36));
		}
	}

	private static long mix(long l) {
		final long[] vs = doMix(l);
		return setVersion(vs);
	}

	private static long[] doMix(long l) {
		final long version = 1L;
		long ret = l;
		int digit = 0;
		while (ret > 0) {
			digit++;
			ret = ret >> 3;
		}
		int i = 0, md = (digit - 1) / 5 + 1;
		final int mix = (int) (l & ((1 << (3 * md)) - 1));
		ret = 0;
		while (digit > 0) {
			ret += (((l & ((1 << 15) - 1)) + ((mix & (((1 << 3) - 1) << (3 * --md))) << (15 - 3 * md))) << i);
			l = (l >> 15);
			digit -= 5;
			i += 18;
		}
		l = ret;

		return new long[] { version, l };
	}

	private static long demix(long l) {
		final long[] vs = getVersion(l);
		l = vs[1];
		switch ((int) vs[0]) {
		case 1:
			long dig = 0,
			ret = 0;
			while (l > 0) {
				ret += ((l & ((1 << 15) - 1)) << dig);
				l = (l >> 18);
				dig += 15;
			}
			l = ret;
			break;
		}
		return l;
	}

	private static long setVersion(long[] vs) {
		// return vs[1] / 256 * 4096 + vs[0] * 256 + vs[1] % 256;
		return ((vs[1] >> 8) << 12) + (vs[0] << 8) + (vs[1] & 255);
	}

	private static long[] getVersion(long l) {
		// return new long[] { (l / 256) % 16, (l / 4096) * 256 + l % 256 };
		return new long[] { (l >> 8) & 15, ((l >> 12) << 8) + (l & 255) };
	}
}

mybatis注册上面的typeHandler

/******************详见Configuration.java****************************/
private void typeHandlerElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String typeHandlerPackage = child.getStringAttribute("name");
          typeHandlerRegistry.register(typeHandlerPackage);
        } else {
          String javaTypeName = child.getStringAttribute("javaType");
          String jdbcTypeName = child.getStringAttribute("jdbcType");
          String handlerTypeName = child.getStringAttribute("handler");
          Class<?> javaTypeClass = resolveClass(javaTypeName);
          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
          Class<?> typeHandlerClass = resolveClass(handlerTypeName);
          if (javaTypeClass != null) {
            if (jdbcType == null) {
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
            typeHandlerRegistry.register(typeHandlerClass);
          }
        }
      }
    }
  }
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
...
    <typeAliases>
        <package name="typeAlias"/>
    </typeAliases>
    <typeHandlers>
        <typeHandler handler="idHandler" javaType="string" jdbcType="long"/>
    </typeHandlers>
 ...
</configuration>

结果

 @Override
  public String toString() {
    return "Author : " + id + " : " + username + " : " + email;
  }
  
  数据库记录
  id    username  email            bio
  1 	ddshuai	  ddshuai@139.com	sdssd
  

DEBUG [main] - ==>  Preparing: select * from author where id = ?; 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
Author : b8qp : ddshuai : ddshuai@139.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值