博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mybatis源码分析之SqlSessionFactory(一)
阅读量:6343 次
发布时间:2019-06-22

本文共 14324 字,大约阅读时间需要 47 分钟。

简介

MyBatis的前身叫iBatis,本是apache的一个开源项目, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis。MyBatis是支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis使用简单的XML或注解用于配置和原始映射,将接口和Java的POJOs(Plan Old Java Objects,普通的Java对象)映射成数据库中的记录。

实验代码

public static void main(String[] args) throws Exception {        SqlSessionFactory sessionFactory = null;        String resource = "configuration.xml";        sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader(resource));        SqlSession sqlSession = sessionFactory.openSession();        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);        System.out.println(userMapper.findUserById(1));    }

    

configuration.xml 

resource.properties

jdbc.driverClass=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8jdbc.userName=rootjdbc.password=root

SqlSessionFactoryBuilder

更加上面demo的main方法我们先来看下 SqlSessionFactoryBuilder,他的主要目的是创建一个SqlSessionFactory

看下面源码:

/** * Builds {@link SqlSession} instances. * * @author Clinton Begin */public class SqlSessionFactoryBuilder {  public SqlSessionFactory build(Reader reader) {  //Reader读取mybatis配置文件 "configuration.xml";    return build(reader, null, null);  }  public SqlSessionFactory build(Reader reader, String environment) {    return build(reader, environment, null);  }  public SqlSessionFactory build(Reader reader, Properties properties) {    return build(reader, null, properties);  }  //通过XMLConfigBuilder解析mybatis配置,然后创建SqlSessionFactory对象  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {    try {      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);      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.      }    }  }  public SqlSessionFactory build(InputStream inputStream) {    return build(inputStream, null, null);  }  public SqlSessionFactory build(InputStream inputStream, String environment) {    return build(inputStream, environment, null);  }  public SqlSessionFactory build(InputStream inputStream, Properties properties) {    return build(inputStream, null, properties);  }  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {    try {      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);      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);  }}

通过源码,我们可以看到SqlSessionFactoryBuilder有不同的参数的build,通过XMLConfigBuilder 去解析我们传入的mybatis的配置文件,转化成Configuration,通过SqlSessionFactory build(Configuration config) 

最终创建DefaultSqlSessionFactory,下面就接着看看 XMLConfigBuilder 部分源码

  // mybatis 配置文件解析 public XMLConfigBuilder(Reader reader, String environment, Properties props) {    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);  }    private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {    super(new Configuration());    ErrorContext.instance().resource("SQL Mapper Configuration");    this.configuration.setVariables(props);    this.parsed = false;    this.environment = environment;    this.parser = parser;  }//读取XMLConfigBuilder返回一个Configuration对象 public Configuration parse() {    if (parsed) {      throw new BuilderException("Each XMLConfigBuilder can only be used once.");    }    parsed = true;    parseConfiguration(parser.evalNode("/configuration"));//xml的根节点    return configuration;  }  //configuration下面能配置的节点为以下11个节点 reflectionFactory是后面版本新增的  //不同子节点进行Element处理  private void parseConfiguration(XNode root) {    try {      Properties settings = settingsAsPropertiess(root.evalNode("settings"));      //issue #117 read properties first      propertiesElement(root.evalNode("properties"));      loadCustomVfs(settings);      typeAliasesElement(root.evalNode("typeAliases"));      pluginElement(root.evalNode("plugins"));      objectFactoryElement(root.evalNode("objectFactory"));      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));      reflectionFactoryElement(root.evalNode("reflectionFactory"));      settingsElement(settings);      // read it after objectFactory and objectWrapperFactory issue #631      environmentsElement(root.evalNode("environments"));      databaseIdProviderElement(root.evalNode("databaseIdProvider"));      typeHandlerElement(root.evalNode("typeHandlers"));      mapperElement(root.evalNode("mappers"));    } catch (Exception e) {      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);    }  }

通过以上源码,我们就能看出,在mybatis的配置文件中:

1. configuration节点为根节点。

2. 在configuration节点之下,我们可以配置11个子节点, 分别为:properties、typeAliases、plugins、objectFactory、objectWrapperFactory、settings、environments、databaseIdProvider、typeHandlers、mappers、reflectionFactory(后面版本新增的)

如下图:

1040703-20170111160501166-423604401.png

下面我们看下子节点的源码

1:properties(相关配置读取)

 private void propertiesElement(XNode context) throws Exception {    if (context != null) {      Properties defaults = context.getChildrenAsProperties();      String resource = context.getStringAttribute("resource");      String url = context.getStringAttribute("url");      if (resource != null && url != null) { //如果resource和url都为空就抛出异常        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");      }      if (resource != null) {        defaults.putAll(Resources.getResourceAsProperties(resource));      } else if (url != null) {        defaults.putAll(Resources.getUrlAsProperties(url));      }      Properties vars = configuration.getVariables();      if (vars != null) {        defaults.putAll(vars);      }//把Properties defaults 设置到 parser.setVariables      parser.setVariables(defaults);//把Properties defaults 设置到 configuration.setVariables      configuration.setVariables(defaults);    }  }

  

2:settings 全局性的配置

private Properties settingsAsPropertiess(XNode context) {    if (context == null) {      return new Properties();    }    Properties props = context.getChildrenAsProperties();    // Check that all settings are known to the configuration class    MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);    for (Object key : props.keySet()) {      if (!metaConfig.hasSetter(String.valueOf(key))) {        throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");      }    }    return props;  }    //settings元素设置 元素比较多不懂的可以看下官方文档  private void settingsElement(Properties props) throws Exception {     configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));     configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));     configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));     configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));     configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));     configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true));     configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));     configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));     configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));     configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));     configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));     configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));     configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));     configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));     configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));     configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));     configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));     configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));     configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));     configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));     configuration.setLogPrefix(props.getProperty("logPrefix"));     configuration.setLogImpl(resolveClass(props.getProperty("logImpl"))); //这个是我们的配置文件里面设置的 日志     configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));   }

   

有关全局设置说明

1040703-20170111160502635-1732678165.png

3:typeAliases  为一些类定义别名

  private void typeAliasesElement(XNode parent) {    if (parent != null) {      for (XNode child : parent.getChildren()) {        if ("package".equals(child.getName())) {          String typeAliasPackage = child.getStringAttribute("name");          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);        } else {          String alias = child.getStringAttribute("alias");          String type = child.getStringAttribute("type");          try {            Class clazz = Resources.classForName(type);            if (alias == null) {              typeAliasRegistry.registerAlias(clazz);            } else {              typeAliasRegistry.registerAlias(alias, clazz);            }          } catch (ClassNotFoundException e) {            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);          }        }      }    }  }    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); //最终是放到map里面   }

   

4:environments Mybatis的环境

//数据源  事务管理器 相关配置  private void environmentsElement(XNode context) throws Exception {    if (context != null) {      if (environment == null) {        environment = context.getStringAttribute("default"); //默认值是default      }      for (XNode child : context.getChildren()) {        String id = child.getStringAttribute("id");        if (isSpecifiedEnvironment(id)) {   // MyBatis 有两种事务管理类型(即type=”[JDBC|MANAGED]”) Configuration.java的构造函数          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));//事物          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); //数据源          DataSource dataSource = dsFactory.getDataSource();          Environment.Builder environmentBuilder = new Environment.Builder(id)              .transactionFactory(txFactory)              .dataSource(dataSource);          configuration.setEnvironment(environmentBuilder.build());  //放到configuration里面        }      }    }  }

  

 5: mappers 映射文件或映射类

 既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。 但是, 首先我们需要告诉 MyBatis  到哪里去找到这些语句。 Java 在这方面没有提供一个很好 的方法, 所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的  资源引用,或者字符表示,或 url 引用的完全限定名(包括 file:///URLs) 

 

 /

/读取配置文件将接口放到configuration.addMappers(mapperPackage)  //四种方式 resource、class name url private void mapperElement(XNode parent) throws Exception {   if (parent != null) {     for (XNode child : parent.getChildren()) {       if ("package".equals(child.getName())) {         String mapperPackage = child.getStringAttribute("name");         configuration.addMappers(mapperPackage);       } else {         String resource = child.getStringAttribute("resource");         String url = child.getStringAttribute("url");         String mapperClass = child.getStringAttribute("class");         if (resource != null && url == null && mapperClass == null) {//demo是通过xml加载的           ErrorContext.instance().resource(resource);           InputStream inputStream = Resources.getResourceAsStream(resource);           XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());           mapperParser.parse();         } else if (resource == null && url != null && mapperClass == null) {           ErrorContext.instance().resource(url);           InputStream inputStream = Resources.getUrlAsStream(url);           XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());           mapperParser.parse();         } else if (resource == null && url == null && mapperClass != null) {           Class mapperInterface = Resources.classForName(mapperClass);           configuration.addMapper(mapperInterface);         } else {           throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");         }       }     }   } }

 以上是几个常用的配置文件元素源码分析,其它的不在这里过多介绍,因为有些子元素本人也没有用到过。

来源: 

转载地址:http://hpkla.baihongyu.com/

你可能感兴趣的文章
CNCF权威调研揭示K8s用户所面临的最大挑战
查看>>
中介模式
查看>>
JS中将变量转为字符串
查看>>
servlet笔记
查看>>
JVM(五)垃圾回收器的前世今生
查看>>
CentOS 7 下安装 Nginx
查看>>
Spring Boot 自动配置之@EnableAutoConfiguration
查看>>
去掉项目名访问
查看>>
ElasticSearch与spring boot的集成使用
查看>>
041-bash下: () {} [] [[]] (())的解释
查看>>
为了学习go我从0开始用beego写了一个简单个人博客(2)登陆管理
查看>>
关于form与表单元素的相关知识总结
查看>>
dede itemindex用法
查看>>
linux相关
查看>>
js函数的间接调用call()、apply()和bind()
查看>>
清除浮动造成的影响
查看>>
20.4 shell脚本中的变量
查看>>
Ios精品源码,tableview下载视频直播源播放器图片位置3D立体旋转相册屏风动画...
查看>>
web项目中的乱码问题原理分析
查看>>
利用CRM中间件Middleware从ERP下载Customer Material的常见错误
查看>>