Mybatis源码分析(一)

一、SqlSessionFactoryBuilder的作用及工作模式

作用:主要是加载XMLConfig配置文件,生成SqlSessionFactroy对象;通过建造者模式,构建非常复杂的SqlsessionFactory对象
主要代码说明:

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      //通过IO流读取xml配置文件
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      //通过XMLConfigBuilder对象的方法 parse();生产 Configuration配置对象
      //并调用重载build方法,构建DefaultSqlSessionFavtory对象  
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

//获取SqlSessionFactory对象
 public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

二、Configuration配置说明

Configuration配置主要是对各种配置属性进行保存,比如数据源、事务、缓存工厂、xml解析类型、日志类型;Mybatis都给出了默认的配置选项;他默认提供了一系列的注册机;

//事务类型
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
//数据源类型
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class); typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
 //缓存类型
 typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class); typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
//数据库供应商类型。识别连接那种数据库
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
//xml解析类型
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);\
//日志类型
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
 typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
 typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
 typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
 typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
 typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
//动态代理类型
 typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
 typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

xml配置文件解析

public Configuration parse() {
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  //指定根节点。调用解析方法  
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

//解析方法
private void parseConfiguration(XNode root) {
  try {
    //参数配置节点解析,  
    propertiesElement(root.evalNode("properties")); //issue #117 read properties first
    //别名配置节点解析
    typeAliasesElement(root.evalNode("typeAliases"));
    //插件节点解析  
    pluginElement(root.evalNode("plugins"));
    //对象工厂节点解析  
    objectFactoryElement(root.evalNode("objectFactory"));
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    //系统配置节点解析  
    settingsElement(root.evalNode("settings"));
    //environment节点解析  
    environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
    //databaseId解析  
    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);
  }
}

所有节点解析后,都会设置道Configuration中去;

三、SqlSessionFactory的作用

SqlSessionFactory的作用是为了创建SqlSession;存在于整个mybatis应用生命周期;默认构建的是DefaultSqlSession;但Mybatis并没有对其进行单例的控制,
主要代码说明:

/*
*   参数说明:execType:执行器类型
*           level:注册的事务类型
*           autoCommit:是否自动提交,默认为false
*/
private SqlSession openSessionFromDataSource(ExecutorType execType,             TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      //获取Mybatis的配置环境,对于xml配置文件中的environment标签,
      final Environment environment = configuration.getEnvironment();
      //获取事务工厂,如果单独使用则使用的是jdbc的默认事务工厂,如果与spring进行整合的话,使用的是sring的事务
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //获取sql执行器  
      final Executor executor = configuration.newExecutor(tx, execType);
      //构建Sqlsession  
      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();
    }
  }

四、SqlSession的作用

作为与数据库交互的对象,生命周期存在于每次与数据库交互期间;最常使用的是DefaulteSqlSession;默认提供了crud一系列基础接口方法,并且提供了获取Mapper与Conncation的接口方法;
主要方法: <T> T getMapper(Class<T> type);
该方法用于获取Mapper接口的代理类,执行最终的sql;
该SqlSession只要用于获取mapper的代理类。以及构建事务。为sql的执行做准备

五、Mapper接口代理类的产生+Mapper运行流程

Mybatis的Mapper接口,通过注册的形式,注册到Mybatis的配置对象中Conuration,使用 Configuration 中的MapperRegistry进行保存。

 protected MapperRegistry mapperRegistry = new MapperRegistry(this);

MapperRegistry:
MapperRegistry是所有Mapper接口注册;内部使用Map保持,以接口的绝对地址作为key,以MapperProxyFactory作为value

private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new 
 HashMap<Class<?>, MapperProxyFactory<?>>();

Mapper代理类的产生:

1、调用用Sqlsession.getMapper()

public <T> T getMapper(Class<T> type) {
    //Mybatis配置类Configuration获取Mapper
    return configuration.<T>getMapper(type, this);
  }

2、Configuration中的MapperRegistry;通过Mapper的class作为key获取MapperProxyFactory工厂的产生

 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    //1、通过Mapper接口的class获取到MapperProxyFactory,接口代理工厂  
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null)
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    try {
      //2、接口代理工程生成Mapper代理对象  
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

MapperProxyFactory

//接口代理工厂生成代理对象
 protected T newInstance(MapperProxy<T> mapperProxy) {
    //4、通过接口代理生成代理对象
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    //3、new出MaperProxy代理类
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

MapperProxy

该类实现的InvocationHandler接口,可以产生代理对象

//mapper接口方法都会进入这里
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //判断是否是抽象方法,如果不是则直接代理运行返回,如果是则不进入if
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    //缓存该方法,一级缓存结果,获取该方法查询的缓存
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //执行sql
    return mapperMethod.execute(sqlSession, args);MapperMethod
  }

MapperMetod

该类为最终执行sql语句以及一些结果集的封装
主要sql执行代码块

public Object execute(SqlSession sqlSession, Object[] args) { 
    Object result;
    //判断该方法sql的执行类型
    if (SqlCommandType.INSERT == command.getType()) {
        Object param = method.convertArgsToSqlCommandParam(args);   
        result = rowCountResult(sqlSession.insert(command.getName(), param)); 
    } else if (SqlCommandType.UPDATE == command.getType()) { 
        Object param = method.convertArgsToSqlCommandParam(args); 
        result = rowCountResult(sqlSession.update(command.getName(), param));
    } else if (SqlCommandType.DELETE == command.getType()) {  
        Object param = method.convertArgsToSqlCommandParam(args);   
        result = rowCountResult(sqlSession.delete(command.getName(), param));  
    } else if (SqlCommandType.SELECT == command.getType()) { 
        if (method.returnsVoid() && method.hasResultHandler()) { 
            executeWithResultHandler(sqlSession, args);    
            result = null;    
        } else if (method.returnsMany()) {   
            result = executeForMany(sqlSession, args);  
        } else if (method.returnsMap()) {   
            result = executeForMap(sqlSession, args);  
        } else {   
            Object param = method.convertArgsToSqlCommandParam(args);  
            result = sqlSession.selectOne(command.getName(), param); 
        }  
    } else {    
        throw new BindingException("Unknown execution method for: " + command.getName());  }  
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {    
        throw new BindingException("Mapper method '" + command.getName()  + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");  }  return result;}

六、Mybatis的事务控制

Mybatis的Transaction接口定义了事务方法,提交、回滚、关闭、获取连接的方法

public interface Transaction {

  /**
   * Retrieve inner database connection
   * @return DataBase connection
   * @throws SQLException
   */
  Connection getConnection() throws SQLException;

  /**
   * Commit inner database connection.
   * @throws SQLException
   */
  void commit() throws SQLException;

  /**
   * Rollback inner database connection.
   * @throws SQLException
   */
  void rollback() throws SQLException;

  /**
   * Close inner database connection.
   * @throws SQLException
   */
  void close() throws SQLException;

}

1、SqlSession调用commit
2、Executor调用commit
3、Transaction调用commit
4、Contection调用commit

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容