Mybatis原理和代码剖析
参考资料(官方)
Mybatis官方文档: https://mybatis.org/mybatis-3/
Mybatis-Parent : https://github.com/mybatis/parent.git
Mybatis-3 : https://github.com/mybatis/mybatis-3.git
Mybatis-Spring : https://github.com/mybatis/spring.git
Mybatis博客: https://blog.mybatis.org/
整体介绍
什么是 MyBatis?
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录 。
Mybatis3 目前最新版本为 3.5.7 , 发布于 2021 年 4 月
Mybatis起源
iBatis 是由 Clinton Begin 于2002年发起的开源项目 , iBatis 曾是开源软件组 Apache 推出的一种轻量级的对象关系映射持久层(ORM)框架 , 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis (引用: 我们朝着与 Apache 软件基金会不同的方向发展,因此团队投票决定离开 ASF) , 至此可以理解为Mybatis3是Ibatis2 的升级版 , 2013 年 11 月 Mybatis- 3 从 google code 讲代码迁移至GitHub 。iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
为什么要使用 MyBatis
- 消除了大量 JDBC 样板代码
- 低学习曲线
- 支持与第三方缓存库集成
- 拥抱 SQL
- 更好的性能
Mybatis 优势
- Mybatis实现了接口绑定,使用更加方便
- 对象关系映射的改进,效率更高
- MyBatis采用功能强大的基于OGNL的表达式来消除其他元素
版本特性
3.5.x
3.5.0 发布于2019 年 1 月
- 支持java.util.Optional作为返回值
- 摒弃了繁琐的@Results和@ConstructorArgs注解 , 可以叠加@Result和@Arg
- 新增了一个新的事务隔离级别SQL_SERVER_SNAPSHOT, 支持SQL Server的SNAPSHOT
- 支持了Log4J 版本 2.6+
- 支持在Sql构建器中使用 OFFSET / LIMIT 方法
3.4.x
3.4.0 发布于2016 年 4 月 , 需要JDK 1.8 的支持
- 3.4.0 发布于2016 年 4 月
- 增加对 Java 8 日期与时间类(JSR-310)的支持
- 继承了Spring的事务超时时间
- 支持org.apache.ibatis.cursor.Cursor作为返回值
- Sql Provider 注解方式支持多个参数 @Param
3.3.x
3.3.0 发布于2015 年 5 月
- 默认代理工具现在是 Javassist 并包含在 mybatis jar 中
- 支持批量插入回写自增主键的功能
3.2.x
3.2.0 发布于 2013 年 2 月 , 需要 Jdk 1.6
- 支持可插拔的脚本引擎
- 支持可扩展字节码提供器和Java辅助类
- 缓存嵌套查询
- 改善日志
- @SelectKey 支持多个Key属性返回
3.1.x
3.1.0 发布于 2012 年 3 月
- 多数据库支持
- Scala 支持
- 多日志框架支持
- Join语句父子关系支持
- 性能优化
3.0.x
较早的版本
核心组件

SqlSessionFactory
/**
 * Creates an {@link SqlSession} out of a connection or a DataSource
 *
 * @author Clinton Begin
 */
public interface SqlSessionFactory {
  SqlSession openSession();
  SqlSession openSession(boolean autoCommit);
  SqlSession openSession(Connection connection);
  SqlSession openSession(TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType);
  SqlSession openSession(ExecutorType execType, boolean autoCommit);
  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType, Connection connection);
  Configuration getConfiguration();
}
SqlSession
/**
 * The primary Java interface for working with MyBatis.
 * Through this interface you can execute commands, get mappers and manage transactions.
 *
 * @author Clinton Begin
 */
public interface SqlSession extends Closeable {
  void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
  int insert(String statement);
  int update(String statement);
  int delete(String statement);
  void commit();
  void rollback();
  List<BatchResult> flushStatements();
  @Override
  void close();
  void clearCache();
  Configuration getConfiguration();
  /**
   * Retrieves a mapper.
   * @param <T> the mapper type
   * @param type Mapper interface class
   * @return a mapper bound to this SqlSession
   */
  <T> T getMapper(Class<T> type);
  Connection getConnection();
}
SqlSource
/**
 * Represents the content of a mapped statement read from an XML file or an annotation.
 * It creates the SQL that will be passed to the database out of the input parameter received from the user.
 *
 * @author Clinton Begin
 */
public interface SqlSource {
  BoundSql getBoundSql(Object parameterObject);
}
解释:内部封装了用户输入的SQL,这个SQL可以表现为SQL节点,也可以表现为静态SQL
1. RawSqlSource
/**
 * Static SqlSource. It is faster than {@link DynamicSqlSource} because mappings are
 * calculated during startup.
 *
 * @since 3.2.0
 * @author Eduardo Macarron
 */
public class RawSqlSource implements SqlSource {
  private final SqlSource sqlSource;
  public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
    this(configuration, getSql(configuration, rootSqlNode), parameterType);
  }
  public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> clazz = parameterType == null ? Object.class : parameterType;
    sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<String, Object>());
  }
  private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
    DynamicContext context = new DynamicContext(configuration, null);
    rootSqlNode.apply(context);
    return context.getSql();
  }
  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    return sqlSource.getBoundSql(parameterObject);
  }
}
该对象包含已解析完成的SQL,这个SQL使用Statement对象直接可以执行。 RawSqlSource 在执行过程中比DynamicSqlSource对象快,因为在框架启动过程中就已经把需要执行的SQL解析完成了。
2. DynamicSqlSource
/**
 * @author Clinton Begin
 */
public class DynamicSqlSource implements SqlSource {
  private final Configuration configuration;
  private final SqlNode rootSqlNode;
  public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
    this.configuration = configuration;
    this.rootSqlNode = rootSqlNode;
  }
  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    rootSqlNode.apply(context);
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
    }
    return boundSql;
  }
}
该对象包含需要动态解析的SQL,例如需要进行字符串拼接、条件判断、循环等动作才能计算好要执行的SQL语句
3. ProviderSqlSource
/**
 * @author Clinton Begin
 * @author Kazuki Shimizu
 */
public class ProviderSqlSource implements SqlSource {
  private final Configuration configuration;
  private final SqlSourceBuilder sqlSourceParser;
  private final Class<?> providerType;
  private Method providerMethod;
  private String[] providerMethodArgumentNames;
  private Class<?>[] providerMethodParameterTypes;
  private ProviderContext providerContext;
  private Integer providerContextIndex;
@xxxProvider的实现
BoundSql
/**
 * An actual SQL String got from an {@link SqlSource} after having processed any dynamic content.
 * The SQL may have SQL placeholders "?" and an list (ordered) of an parameter mappings
 * with the additional information for each parameter (at least the property name of the input object to read
 * the value from).
 * </br>
 * Can also have additional parameters that are created by the dynamic language (for loops, bind...).
 *
 * @author Clinton Begin
 */
public class BoundSql {
  private final String sql;
  private final List<ParameterMapping> parameterMappings;
  private final Object parameterObject;
  private final Map<String, Object> additionalParameters;
  private final MetaObject metaParameters;
  public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
    this.sql = sql;
    this.parameterMappings = parameterMappings;
    this.parameterObject = parameterObject;
    this.additionalParameters = new HashMap<String, Object>();
    this.metaParameters = configuration.newMetaObject(additionalParameters);
  }
  public String getSql() {
    return sql;
  }
  public List<ParameterMapping> getParameterMappings() {
    return parameterMappings;
  }
  public Object getParameterObject() {
    return parameterObject;
  }
  public boolean hasAdditionalParameter(String name) {
    String paramName = new PropertyTokenizer(name).getName();
    return additionalParameters.containsKey(paramName);
  }
  public void setAdditionalParameter(String name, Object value) {
    metaParameters.setValue(name, value);
  }
  public Object getAdditionalParameter(String name) {
    return metaParameters.getValue(name);
  }
}
可执行SQL的包装,具体还包含:
- 执行参数
- 参数的映射信息
- 附加参数
StatementHandler对象执行时需要使用该对象
TypeHandler
处理执行SQL时的出参和出参、比较简单,不做过多解释
/**
 * @author Clinton Begin
 */
public interface TypeHandler<T> {
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
  T getResult(ResultSet rs, String columnName) throws SQLException;
  T getResult(ResultSet rs, int columnIndex) throws SQLException;
  T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
	register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());
    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());
    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(JdbcType.SMALLINT, new ShortTypeHandler());
    register(Integer.class, new IntegerTypeHandler());
    register(int.class, new IntegerTypeHandler());
    register(JdbcType.INTEGER, new IntegerTypeHandler());
    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());
    register(Float.class, new FloatTypeHandler());
    register(float.class, new FloatTypeHandler());
    register(JdbcType.FLOAT, new FloatTypeHandler());
    register(Double.class, new DoubleTypeHandler());
    register(double.class, new DoubleTypeHandler());
    register(JdbcType.DOUBLE, new DoubleTypeHandler());
    register(Reader.class, new ClobReaderTypeHandler());
    register(String.class, new StringTypeHandler());
    register(String.class, JdbcType.CHAR, new StringTypeHandler());
    register(String.class, JdbcType.CLOB, new ClobTypeHandler());
    register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
    register(JdbcType.CHAR, new StringTypeHandler());
    register(JdbcType.VARCHAR, new StringTypeHandler());
    register(JdbcType.CLOB, new ClobTypeHandler());
    register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(JdbcType.NVARCHAR, new NStringTypeHandler());
    register(JdbcType.NCHAR, new NStringTypeHandler());
    register(JdbcType.NCLOB, new NClobTypeHandler());
    register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
    register(JdbcType.ARRAY, new ArrayTypeHandler());
    register(BigInteger.class, new BigIntegerTypeHandler());
    register(JdbcType.BIGINT, new LongTypeHandler());
    register(BigDecimal.class, new BigDecimalTypeHandler());
    register(JdbcType.REAL, new BigDecimalTypeHandler());
    register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
    register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
    register(InputStream.class, new BlobInputStreamTypeHandler());
    register(Byte[].class, new ByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
    register(byte[].class, new ByteArrayTypeHandler());
    register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
    register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.BLOB, new BlobTypeHandler());
    register(Object.class, UNKNOWN_TYPE_HANDLER);
    register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
    register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
    register(Date.class, new DateTypeHandler());
    register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
    register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
    register(JdbcType.TIMESTAMP, new DateTypeHandler());
    register(JdbcType.DATE, new DateOnlyTypeHandler());
    register(JdbcType.TIME, new TimeOnlyTypeHandler());
    register(java.sql.Date.class, new SqlDateTypeHandler());
    register(java.sql.Time.class, new SqlTimeTypeHandler());
    register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
    // mybatis-typehandlers-jsr310
    if (Jdk.dateAndTimeApiExists) {
      Java8TypeHandlersRegistrar.registerDateAndTimeHandlers(this);
    }
    // issue #273
    register(Character.class, new CharacterTypeHandler());
    register(char.class, new CharacterTypeHandler());
TypeAlias
类型别名
	registerAlias("string", String.class);
    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);
    registerAlias("byte[]", Byte[].class);
    registerAlias("long[]", Long[].class);
    registerAlias("short[]", Short[].class);
    registerAlias("int[]", Integer[].class);
    registerAlias("integer[]", Integer[].class);
    registerAlias("double[]", Double[].class);
    registerAlias("float[]", Float[].class);
    registerAlias("boolean[]", Boolean[].class);
    registerAlias("_byte", byte.class);
    registerAlias("_long", long.class);
    registerAlias("_short", short.class);
    registerAlias("_int", int.class);
    registerAlias("_integer", int.class);
    registerAlias("_double", double.class);
    registerAlias("_float", float.class);
    registerAlias("_boolean", boolean.class);
    registerAlias("_byte[]", byte[].class);
    registerAlias("_long[]", long[].class);
    registerAlias("_short[]", short[].class);
    registerAlias("_int[]", int[].class);
    registerAlias("_integer[]", int[].class);
    registerAlias("_double[]", double[].class);
    registerAlias("_float[]", float[].class);
    registerAlias("_boolean[]", boolean[].class);
    registerAlias("date", Date.class);
    registerAlias("decimal", BigDecimal.class);
    registerAlias("bigdecimal", BigDecimal.class);
    registerAlias("biginteger", BigInteger.class);
    registerAlias("object", Object.class);
    registerAlias("date[]", Date[].class);
    registerAlias("decimal[]", BigDecimal[].class);
    registerAlias("bigdecimal[]", BigDecimal[].class);
    registerAlias("biginteger[]", BigInteger[].class);
    registerAlias("object[]", Object[].class);
    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class);
    registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);
    registerAlias("ResultSet", ResultSet.class);
MappedStatement
一个SQL标签的封装
/**
 * @author Clinton Begin
 */
public final class MappedStatement {
  private String resource;
  private Configuration configuration;
  private String id;
  private Integer fetchSize;
  private Integer timeout;
  private StatementType statementType;
  private ResultSetType resultSetType;
  private SqlSource sqlSource;
  private Cache cache;
  private ParameterMap parameterMap;
  private List<ResultMap> resultMaps;
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;
}
MapperProxy
Mapper接口的代理,目前只支持JDB动态代理
public class MapperProxy<T> implements InvocationHandler, Serializable {
  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;
  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
}
四大组件
- Executor : MyBatis的执行器,用于执行增删改查操作
- ParameterHandler : 处理SQL的参数对象
- StatementHandler : 数据库的处理对象,用于执行SQL语句
- ResultSetHandler : 处理SQL的返回结果集

1、Executor
public interface Executor {
  ResultHandler NO_RESULT_HANDLER = null;
  int update(MappedStatement ms, Object parameter) throws SQLException;
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
  <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) 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, BoundSql boundSql);
  boolean isCached(MappedStatement ms, CacheKey key);
  void clearLocalCache();
  void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
  Transaction getTransaction();
  void close(boolean forceRollback);
  boolean isClosed();
  void setExecutorWrapper(Executor executor);
}
1). SimpleExecutor
2). ReuseExecutor
3). BatchExecutor
2、ParameterHandler
/**
 * A parameter handler sets the parameters of the {@code PreparedStatement}
 *
 * @author Clinton Begin
 */
public interface ParameterHandler {
  Object getParameterObject();
  void setParameters(PreparedStatement ps)
      throws SQLException;
}
实现类
- DefaultParameterHandler
3、StatementHandler
/**
 * @author Clinton Begin
 */
public interface StatementHandler {
  Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;
  void parameterize(Statement statement)
      throws SQLException;
  void batch(Statement statement)
      throws SQLException;
  int update(Statement statement)
      throws SQLException;
  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;
  <E> Cursor<E> queryCursor(Statement statement)
      throws SQLException;
  BoundSql getBoundSql();
  ParameterHandler getParameterHandler();
}
实现类
- SimpleStatementHandler
- PreparedStatementHandler
- CallableStatementHandler
4、ResultSetHandler
/**
 * @author Clinton Begin
 */
public interface ResultSetHandler {
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;
  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
  void handleOutputParameters(CallableStatement cs) throws SQLException;
}
实现类
- DefaultResultSetHandler
拦截器
/**
 * @author Clinton Begin
 */
public interface Interceptor {
  Object intercept(Invocation invocation) throws Throwable;
  Object plugin(Object target);
  void setProperties(Properties properties);
}
拦截接口
- org.apache.ibatis.session.Configuration#newExecutor
- org.apache.ibatis.session.Configuration#newParameterHandler
- org.apache.ibatis.session.Configuration#newResultSetHandler
- org.apache.ibatis.session.Configuration#newStatementHandler
工具类
Wrap.java
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;
  }
  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);
    }
  }
}
Cache
一级缓存
public abstract class BaseExecutor implements Executor {
  private static final Log log = LogFactory.getLog(BaseExecutor.class);
  protected Transaction transaction;
  // 一级缓存
  protected PerpetualCache localCache;
}
PerpetualCache.java
public class PerpetualCache implements Cache {
  private final String id;
  private Map<Object, Object> cache = new HashMap<Object, Object>();
  public PerpetualCache(String id) {
    this.id = id;
  }
}
优点
- 设计简单
- 默认开启
缺点
- 命中率低(任意一个update语句都会清空缓存)
- 容易造成缓存穿透
- 没有淘汰策略
二级缓存
日志集成
MyBatis支持如下日志框架
- Slf4j
- JCL
- log4j
- log4j2
- JUL
- stdout
如何做到兼容众多框架??
public final class LogFactory {
  /**
   * Marker to be used by logging implementations that support markers
   */
  public static final String MARKER = "MYBATIS";
  private static Constructor<? extends Log> logConstructor;
  static {
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useSlf4jLogging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useCommonsLogging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useLog4J2Logging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useLog4JLogging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useJdkLogging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useNoLogging();
      }
    });
  }
}
Spring集成Mybatis
配置SqlSessionFactoryBean
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:/org/mybatis/spring/demo/mybatis-cfg.xml"/>
        <property name="mapperLocations" value="classpath:/org/mybatis/spring/demo/*Mapper.xml"/>
</bean>
配置MapperScannerConfigurer
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="org.mybatis.spring.demo"/>
        <property name="annotationClass" value="org.springframework.stereotype.Repository"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
设计模式
一、工厂方法
DataSourceFactory
/**
 * @author Clinton Begin
 */
public interface DataSourceFactory {
  void setProperties(Properties props);
  DataSource getDataSource();
}
public class UnpooledDataSourceFactory implements DataSourceFactory {
  protected DataSource dataSource;
  public UnpooledDataSourceFactory() {
    this.dataSource = new UnpooledDataSource();
  }
}
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
  public PooledDataSourceFactory() {
    this.dataSource = new PooledDataSource();
  }
}
二、模板模式
BaseExecutor
public abstract class BaseExecutor implements Executor {
  .... 省略部分代码片段
  protected abstract int doUpdate(MappedStatement ms, Object parameter)
      throws SQLException;
  protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
      throws SQLException;
  protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException;
  protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
      throws SQLException;
}
三、装饰器模式
FifoCache
/**
 * FIFO (first in, first out) cache decorator
 *
 * @author Clinton Begin
 */
public class FifoCache implements Cache {
  private final Cache delegate;
  private final Deque<Object> keyList;
  private int size;
  public FifoCache(Cache delegate) {
    this.delegate = delegate;
    this.keyList = new LinkedList<Object>();
    this.size = 1024;
  }
  @Override
  public String getId() {
    return delegate.getId();
  }
  @Override
  public int getSize() {
    return delegate.getSize();
  }
  public void setSize(int size) {
    this.size = size;
  }
  @Override
  public void putObject(Object key, Object value) {
    cycleKeyList(key);
    delegate.putObject(key, value);
  }
  @Override
  public Object getObject(Object key) {
    return delegate.getObject(key);
  }
  @Override
  public Object removeObject(Object key) {
    return delegate.removeObject(key);
  }
  @Override
  public void clear() {
    delegate.clear();
    keyList.clear();
  }
  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }
  private void cycleKeyList(Object key) {
    keyList.addLast(key);
    if (keyList.size() > size) {
      Object oldestKey = keyList.removeFirst();
      delegate.removeObject(oldestKey);
    }
  }
}
四、构建者模式
ResultMap.Builder
public class ResultMap {
  private Configuration configuration;
  private String id;
  private Class<?> type;
  private List<ResultMapping> resultMappings;
  private List<ResultMapping> idResultMappings;
  private List<ResultMapping> constructorResultMappings;
  private List<ResultMapping> propertyResultMappings;
  private Set<String> mappedColumns;
  private Set<String> mappedProperties;
  private Discriminator discriminator;
  private boolean hasNestedResultMaps;
  private boolean hasNestedQueries;
  private Boolean autoMapping;
  private ResultMap() {
  }
  public static class Builder {
    private static final Log log = LogFactory.getLog(Builder.class);
    private ResultMap resultMap = new ResultMap();
    public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings) {
      this(configuration, id, type, resultMappings, null);
    }
    public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings, Boolean autoMapping) {
      resultMap.configuration = configuration;
      resultMap.id = id;
      resultMap.type = type;
      resultMap.resultMappings = resultMappings;
      resultMap.autoMapping = autoMapping;
    }
    public Builder discriminator(Discriminator discriminator) {
      resultMap.discriminator = discriminator;
      return this;
    }
    public Class<?> type() {
      return resultMap.type;
    }
    public ResultMap build() {
      if (resultMap.id == null) {
        throw new IllegalArgumentException("ResultMaps must have an id");
      }
      resultMap.mappedColumns = new HashSet<String>();
      resultMap.mappedProperties = new HashSet<String>();
      resultMap.idResultMappings = new ArrayList<ResultMapping>();
      resultMap.constructorResultMappings = new ArrayList<ResultMapping>();
      resultMap.propertyResultMappings = new ArrayList<ResultMapping>();
      final List<String> constructorArgNames = new ArrayList<String>();
      for (ResultMapping resultMapping : resultMap.resultMappings) {
        resultMap.hasNestedQueries = resultMap.hasNestedQueries || resultMapping.getNestedQueryId() != null;
        resultMap.hasNestedResultMaps = resultMap.hasNestedResultMaps || (resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null);
      // .....省略部分代码
      return resultMap;
    }
  public String getId() {
    return id;
  }
  public boolean hasNestedResultMaps() {
    return hasNestedResultMaps;
  }
  public boolean hasNestedQueries() {
    return hasNestedQueries;
  }
  public Class<?> getType() {
    return type;
  }
  public List<ResultMapping> getResultMappings() {
    return resultMappings;
  }
}
五、代理模式
ConnectionLogger
protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }
public final class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler {
  private final Connection connection;
  @Override
  public Object invoke(Object proxy, Method method, Object[] params)
      throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, params);
      }
      if ("prepareStatement".equals(method.getName())) {
        if (isDebugEnabled()) {
          debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
        }
        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
        stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
        return stmt;
      } else if ("prepareCall".equals(method.getName())) {
        if (isDebugEnabled()) {
          debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
        }
        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
        stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
        return stmt;
      } else if ("createStatement".equals(method.getName())) {
        Statement stmt = (Statement) method.invoke(connection, params);
        stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
        return stmt;
      } else {
        return method.invoke(connection, params);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
  public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
    InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
    ClassLoader cl = Connection.class.getClassLoader();
    return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
  }
}
六、策略模式
RoutingStatementHandler
public class RoutingStatementHandler implements StatementHandler {
  private final StatementHandler delegate;
  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }
  }
  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    return delegate.prepare(connection, transactionTimeout);
  }
  @Override
  public void parameterize(Statement statement) throws SQLException {
    delegate.parameterize(statement);
  }
七、组合模式
SqlNode
  public SqlSource parseScriptNode() {
    List<SqlNode> contents = parseDynamicTags(context);
    MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
    SqlSource sqlSource = null;
    if (isDynamic) {
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }
  List<SqlNode> parseDynamicTags(XNode node) {
    List<SqlNode> contents = new ArrayList<SqlNode>();
    NodeList children = node.getNode().getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      XNode child = node.newXNode(children.item(i));
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
        String data = child.getStringBody("");
        TextSqlNode textSqlNode = new TextSqlNode(data);
        if (textSqlNode.isDynamic()) {
          contents.add(textSqlNode);
          isDynamic = true;
        } else {
          contents.add(new StaticTextSqlNode(data));
        }
      } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
        String nodeName = child.getNode().getNodeName();
        NodeHandler handler = nodeHandlers(nodeName);
        if (handler == null) {
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        handler.handleNode(child, contents);
        isDynamic = true;
      }
    }
    return contents;
  }
  NodeHandler nodeHandlers(String nodeName) {
    Map<String, NodeHandler> map = new HashMap<String, NodeHandler>();
    map.put("trim", new TrimHandler());
    map.put("where", new WhereHandler());
    map.put("set", new SetHandler());
    map.put("foreach", new ForEachHandler());
    map.put("if", new IfHandler());
    map.put("choose", new ChooseHandler());
    map.put("when", new IfHandler());
    map.put("otherwise", new OtherwiseHandler());
    map.put("bind", new BindHandler());
    return map.get(nodeName);
  }
}
八、责任链模式
InterceptorChain
/**
 * @author Clinton Begin
 */
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);
  }
}
九、适配器模式
Log
public class Slf4jImpl implements Log {
  private Log log;
  public Slf4jImpl(String clazz) {
    Logger logger = LoggerFactory.getLogger(clazz);
    if (logger instanceof LocationAwareLogger) {
      try {
        // check for slf4j >= 1.6 method signature
        logger.getClass().getMethod("log", Marker.class, String.class, int.class, String.class, Object[].class, Throwable.class);
        log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger) logger);
        return;
      } catch (SecurityException e) {
        // fail-back to Slf4jLoggerImpl
      } catch (NoSuchMethodException e) {
        // fail-back to Slf4jLoggerImpl
      }
    }
    // Logger is not LocationAwareLogger or slf4j version < 1.6
    log = new Slf4jLoggerImpl(logger);
  }
}
十、迭代器模式
Cursor
/**
 * Cursor contract to handle fetching items lazily using an Iterator.
 * Cursors are a perfect fit to handle millions of items queries that would not normally fits in memory.
 * Cursor SQL queries must be ordered (resultOrdered="true") using the id columns of the resultMap.
 *
 * @author Guillaume Darmont / guillaume@dropinocean.com
 */
public interface Cursor<T> extends Closeable, Iterable<T> {
    /**
     * @return true if the cursor has started to fetch items from database.
     */
    boolean isOpen();
    /**
     *
     * @return true if the cursor is fully consumed and has returned all elements matching the query.
     */
    boolean isConsumed();
    /**
     * Get the current item index. The first item has the index 0.
     * @return -1 if the first cursor item has not been retrieved. The index of the current item retrieved.
     */
    int getCurrentIndex();
}
多数据源方案
mybatis-cfg.xml
  <databaseIdProvider type="DB_VENDOR">
    <property name="MySQL" value="mysql"/>
    <property name="Oracle" value="oracle"/>
  </databaseIdProvider>
WarehouseMapper.xml
  <select id="selectAll" resultMap="BaseResultMap" databaseId="mysql">
    select
      <include refid="Base_Column_List" />
    from t_warehouse where id = 1;
  </select>
  <select id="selectAll" resultMap="BaseResultMap" databaseId="oracle">
    select
    <include refid="Base_Column_List" />
    from t_warehouse where id = 2;
  </select>
MyBatis优化
NodeHandler单例化
List<SqlNode> parseDynamicTags(XNode node) {
    List<SqlNode> contents = new ArrayList<SqlNode>();
    NodeList children = node.getNode().getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      XNode child = node.newXNode(children.item(i));
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
       // .............
      } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
        String nodeName = child.getNode().getNodeName();
        NodeHandler handler = nodeHandlers(nodeName);
        if (handler == null) {
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        handler.handleNode(child, contents);
        isDynamic = true;
      }
    }
    return contents;
  }
NodeHandler nodeHandlers(String nodeName) {
    Map<String, NodeHandler> map = new HashMap<String, NodeHandler>();
    map.put("trim", new TrimHandler());
    map.put("where", new WhereHandler());
    map.put("set", new SetHandler());
    map.put("foreach", new ForEachHandler());
    map.put("if", new IfHandler());
    map.put("choose", new ChooseHandler());
    map.put("when", new IfHandler());
    map.put("otherwise", new OtherwiseHandler());
    map.put("bind", new BindHandler());
    return map.get(nodeName);
  }
MapperProxy优化
/**
 * @author Lasse Voss
 */
public class MapperProxyFactory<T> {
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }
  public Class<T> getMapperInterface() {
    return mapperInterface;
  }
  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
}
常见反例
1. 未合理使用where标签

2. 未合理使用@Param
WarehouseMapper.java
public interface WarehouseMapper {
    Warehouse selectByCodeAndName(String code, String name);
}
WarehouseMapper.xml
  <select id="selectByCodeAndName" resultMap="BaseResultMap">
    select
    	id, gmt_create, gmt_modify, name, code, version
    from t_warehouse where code = #{param1} and name = #{param2}
  </select>
或者
  <select id="selectByCodeAndName" resultMap="BaseResultMap">
    select
    	id, gmt_create, gmt_modify, name, code, version
    from t_warehouse where code = #{0} and name = #{1}
  </select>
3. 重复定义 Statement

4. 返回List时做判空
List<Warehouse> list = warehouseMapper.selectAll();
	if (list != null) {
		// do something....
}
SDK增强
| 名称 | Star | Fork | 
|---|---|---|
| MyBatis-Plus | GitHub 11.5K | 3.1k | 
| Tk-Mapper | GitHub 6.5K | 1.5k | 
| Fluent Mybatis | 415 | 39 | 
| Cdal | - | - | 
MyBatis-Plus
为简化开发而生, Mybatis 增强工具包 - 只做增强不做改变,简化CRUD操作
Tk-Mapper
Fluent Mybatis
Cdal
菜鸟内部框架
Mybatis原理和代码剖析的更多相关文章
- HDFS集中式的缓存管理原理与代码剖析
		转载自:http://www.infoq.com/cn/articles/hdfs-centralized-cache/ HDFS集中式的缓存管理原理与代码剖析 Hadoop 2.3.0已经发布了,其 ... 
- HDFS集中式的缓存管理原理与代码剖析--转载
		原文地址:http://yanbohappy.sinaapp.com/?p=468 Hadoop 2.3.0已经发布了,其中最大的亮点就是集中式的缓存管理(HDFS centralized cache ... 
- MyBatis原理总结(代码实现流程)
		我们在实际开发中,越简单越好,所以都是采用不写Dao实现类的方式.不管是使用xml还是直接配置. 但是MyBatis是支持写Dao实现类的 注意sqlSession是这里面的一个灵魂,有很多执行api ... 
- 《深入理解mybatis原理》 MyBatis事务管理机制
		MyBatis作为Java语言的数据库框架,对数据库的事务管理是其很重要的一个方面.本文将讲述MyBatis的事务管理的实现机制. 首先介绍MyBatis的事务Transaction的接口设计以及其不 ... 
- 《深入理解mybatis原理》 Mybatis初始化机制具体解释
		对于不论什么框架而言.在使用前都要进行一系列的初始化,MyBatis也不例外. 本章将通过下面几点具体介绍MyBatis的初始化过程. 1.MyBatis的初始化做了什么 2. MyBatis基于XM ... 
- [Spark内核] 第32课:Spark Worker原理和源码剖析解密:Worker工作流程图、Worker启动Driver源码解密、Worker启动Executor源码解密等
		本課主題 Spark Worker 原理 Worker 启动 Driver 源码鉴赏 Worker 启动 Executor 源码鉴赏 Worker 与 Master 的交互关系 [引言部份:你希望读者 ... 
- 《深入理解mybatis原理》 MyBatis的架构设计以及实例分析
		作者博客:http://blog.csdn.net/u010349169/article/category/2309433 MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简 ... 
- 深入理解mybatis原理, Mybatis初始化SqlSessionFactory机制详解(转)
		文章转自http://blog.csdn.net/l454822901/article/details/51829785 对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章 ... 
- 《深入理解mybatis原理6》 MyBatis的一级缓存实现详解 及使用注意事项
		<深入理解mybatis原理> MyBatis的一级缓存实现详解 及使用注意事项 0.写在前面 MyBatis是一个简单,小巧但功能非常强大的ORM开源框架,它的功能强大也体现在它的缓 ... 
随机推荐
- python框架之Flask
			介绍:Flask是一个使用 Python 编写的轻量级 Web 应用框架.其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 . WSGl:Web Server Gateway ... 
- 第十七篇 -- QTreeWidget与QDockWidget
			效果图: 目录和工具条的创建在前面几节就已经学过了,所以目录和工具条的布局可以自己画. 那么下面的部分,左侧是一个DockWidget,里面放置一个TreeWidget.右边是一个ScrollArea ... 
- argparse模块基本用法
			argparse模块基本用法 在 python 编写的程序中,我们经常会看到的 argparse 相关代码,而它究竟怎么使用呢?接招! argparse 是一个命令行参数解析模块 现在提出需求,我需要 ... 
- 共享内存 & Actor并发模型哪个更快?
			HI,前几天被.NET圈纪检委@懒得勤快问到共享内存和Actor并发模型哪个速度更快. 前文传送门: 说实在,我内心10w头羊驼跑过...... 先说结论 首先两者对于并发的风格模型不一样. 共享内存 ... 
- 解决 OnDropFiles 可能无响应的问题【转】
			大多数程序都有接收拖放文件的功能,即是用鼠标把文件拖放到程序窗口上方,符合格式的文件就会自动被程序打开.最近自己对编写的程序增加了一个拖放文件的功能,在 Windows XP.Windows Serv ... 
- 【LeetCode】169. 多数元素
			169. 多数元素 知识点:数组:排序:消消乐:分治: 题目描述 给定一个大小为 n 的数组,找到其中的多数元素.多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素. 你可以假设数组是非空的, ... 
- 3D性能优化 | 说一说glTF文件压缩
			引言 最近做T级互动,需要使用到3D模型.相信大家和我一样,在开始着手的时候,一定会有这么些问题: 1.如何选择3D模型的导出格式 2.如何对模型文件进行优化 3.在大流量的项目中兼容性怎么样 让我们 ... 
- .Net Core with 微服务 - 分布式事务 - 2PC、3PC
			最近比较忙,好久没更新了.这次我们来聊一聊分布式事务. 在微服务体系下,我们的应用被分割成多个服务,每个服务都配置一个数据库.如果我们的服务划分的不够完美,那么为了完成业务会出现非常多的跨库事务.即使 ... 
- dubbo学习实践(3)之Dubbo整合Consul及Dubbo配置方式
			前言:上一篇中,已经写到了使用zookeeper为注册中心的配置,下面写下配置Consul为注册中心 1. Consul注册中心验证 修改provider和consumer的服务配置文件 Provid ... 
- rabbitMQ通过@RabbitListener和配置文件XML创建exchange及队列绑定关系
			1.@RabbitListener 2.配置文件 <rabbit:fanout-exchange name="fanoutExchange" xmlns="http ... 
