美文网首页
mybatis源码解析之mapper

mybatis源码解析之mapper

作者: 源来是你啊 | 来源:发表于2019-03-11 16:42 被阅读0次

1.Mybatis

1.1 mybatis使用

配置文件mybatis.cfg.xml

<?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>
    <!-- 数据库配置 -->
    <properties resource="datasource.properties"/>

    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="UNPOOLED">

                <property name="url" value="${jdbc.url}"/>
                <property name="driver" value="${jdbc.driver}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>

            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--<mapper resource="com/lh/train/mapper/StudentMapper.xml"></mapper>-->
        <package name="com.lh.train.mapper" />
    </mappers>
</configuration>

mybatisUtil


public class MyBatisUtil {

    private static SqlSessionFactory sqlSessionFactory;
    static {
        InputStream in = null;
        try {
            in = Resources.getResourceAsStream("mybatis.cfg.xml");

            sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //获取session句柄
    public static SqlSession getSession(){
        return sqlSessionFactory.openSession();
    }
}

操作数据

SqlSession session = MyBatisUtil.getSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);

Student s = new Student();
s.setFirstName("诸葛");
s.setLastName("孔明");
s.setBirthday(new Date());

mapper.insert(s);
session.commit();
session.close();

2.浅析源码

InputStream in = Resources.getResourceAsStream("mybatis.cfg.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);

首先mybatis加载了配置文件,然后作为参数传递给SqlSessionFactoryBuilder,以此创建sqlSessionFactory ;
在build中做了如下工作:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //xml解析器
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //解析xml后并创建一个DefaultSqlSessionFactory
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
    
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

那么看看xml是如何解析的(parser.parse())

public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    //更新配置的flag
    parsed = true;
    //解析后返回配置
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      //以下共有10个配置 分别对应mabatis的10个配置属性
      //properties
      propertiesElement(root.evalNode("properties"));
      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"));
      //数据提供者
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //类型映射
      typeHandlerElement(root.evalNode("typeHandlers"));
      //mapper
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

解析完成后,configration类的属性如下,即为mabatis.cfg.xml的配置属性实例

protected Environment environment;

  protected boolean safeRowBoundsEnabled;
  protected boolean safeResultHandlerEnabled = true;
  protected boolean mapUnderscoreToCamelCase;
  protected boolean aggressiveLazyLoading;
  protected boolean multipleResultSetsEnabled = true;
  protected boolean useGeneratedKeys;
  protected boolean useColumnLabel = true;
  protected boolean cacheEnabled = true;
  protected boolean callSettersOnNulls;
  protected boolean useActualParamName = true;
  protected boolean returnInstanceForEmptyRow;

  protected String logPrefix;
  protected Class <? extends Log> logImpl;
  protected Class <? extends VFS> vfsImpl;
  protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
  protected Integer defaultStatementTimeout;
  protected Integer defaultFetchSize;
  protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
  protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;

  protected Properties variables = new Properties();
  protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
  protected ObjectFactory objectFactory = new DefaultObjectFactory();
  protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

  protected boolean lazyLoadingEnabled = false;
  protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL

  protected String databaseId;
  /**
   * Configuration factory class.
   * Used to create Configuration for loading deserialized unread properties.
   *
   * @see <a href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue 300 (google code)</a>
   */
  protected Class<?> configurationFactory;

  protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
  protected final InterceptorChain interceptorChain = new InterceptorChain();
  protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
  protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
  protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
  protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
  protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");

  protected final Set<String> loadedResources = new HashSet<String>();
  protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");

  protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
  protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
  protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
  protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();

  /*
   * A map holds cache-ref relationship. The key is the namespace that
   * references a cache bound to another namespace and the value is the
   * namespace which the actual cache is bound to.
   */
  protected final Map<String, String> cacheRefMap = new HashMap<String, String>();

接下来接续

sqlSessionFactory.openSession();
@Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

//创建SQLSession实例

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      //获取创建事务的工厂
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //创建事务
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //通过配置创建Exector执行器
      final Executor executor = configuration.newExecutor(tx, execType);
      //创建sqlSession
      //注意 以下先执行finally语句 然后再返回
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

接下来看看 configuration.newExecutor(tx, execType);

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    //根据以上的executortype创建不同的Executor执行器 否则则创建默认的simpleExecutor
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    //如果cacheEnable则开启二级缓存
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    //拦截器 这里使用了经典的设计模式--职责链模式
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

以上的Executor其实是对jdbc对数据库的增删改查的一个封装类

回到之前返回了一个默认的DefaultSqlSession,那么来看一看它的实现

public class DefaultSqlSession implements SqlSession {
  //mabtis配置
  private final Configuration configuration;
  //数据库执行器
  private final Executor executor;
  //是否自动提交
  private final boolean autoCommit;
  //是否为脏数据
  private boolean dirty;
  //游标集
  private List<Cursor<?>> cursorList;

  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

  public DefaultSqlSession(Configuration configuration, Executor executor) {
    this(configuration, executor, false);
  }
  //获取mapper
  @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
  }
}

接下来看如下执行语句,才是真正操作数据的开始

SqlSession session = MyBatisUtil.getSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
Student s = new Student();

s.setFirstName("诸葛");
s.setLastName("孔明");
s.setBirthday(new Date());
mapper.insert(s);

session.commit();
session.close();

那么首先看一看session.getMapper(StudentMapper.class)干了啥?

即为上面所说的DefaultSqlSession.getMapper()

显然,它将这个锅甩给了configuration.<T>getMapper(type, this)去被,
然后。。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}

configration又甩给了mapperRegistry!!!

怎么这个mapperRegistry这么眼熟,没错,他正是configration的一个属性,就是刚开始xml解析的最后一个解析属性。
接着他们又将这个锅送给了 mapperProxyFactory.newInstance(sqlSession);

  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    //这个knowMapper集合中保存了所有注册的xxxMapper.xml以及它们各自的属性
    //获取当前的Mappper有没有被注册过
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

这里有必要解释一下下面这句话

 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);

这意思是获取当前mapper接口的代理工厂实例;而这个代理工厂实例保存在以mapper为key,
代理工厂为值得knowsMappers(HashMap)的集合里面;

那么这些代理工厂实例是什么时候创建的呢?

public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        //显然在他们被解析成属性时已经创建了
        //并被加入了knowmappers集合中
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

言归正传,回到mapperProxyFactory.newInstance(sqlSession)上来:

public class MapperProxyFactory<T> {
  //对应的mapper接口
  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);
  }
}

为了条理更加清晰,最后再看看关键的MapperProxy类的实现:

//使用动态代理模式需要继承InvocationHandler接口
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())) {
        //执行method方法  方法对应的类this,方法对应的参数args  
        //至此 再不发生异常的情况下  getMapper方法到此结束~
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    //最后 保存mapperMethod到集合中
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
  //保存集合到缓存
  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }
}

至此getMapper的使命就完成了,那么接下来就是

mapper.insert(s);
session.commit();
session.close();

提交事务

  @Override
  public void commit(boolean required) throws SQLException {
    if (closed) {
      throw new ExecutorException("Cannot commit, transaction is already closed");
    }
    clearLocalCache();
    flushStatements();
    if (required) {
      transaction.commit();
    }
  }

相关文章

网友评论

      本文标题:mybatis源码解析之mapper

      本文链接:https://www.haomeiwen.com/subject/rvzvpqtx.html