`

mybatis 学习

阅读更多
这篇博客是我学习mybatis的心得。也是对自己的鞭策。
这些我想一步步的解答:)每天记录一下自己的学习心得
如下代码:
String resource = "config/ibatis/configuration.xml";
		Reader reader;
		try {
			reader = Resources.getResourceAsReader(resource);
			sessionFactory = new SqlSessionFactoryBuilder().build(reader);
		} catch (IOException e) {
			e.printStackTrace();
		}


首先 那我就看Resources这个类是如何解析资源的吧。
Resources 这个类 位于 org.apache.ibatis 包下面 包下面的类如下所示:

实际上是调用 ClassLoaderWrapper的getResourceAsReader方法
如下所示:
public static Reader getResourceAsReader(String resource) throws IOException {
    Reader reader;
    if (charset == null) {
      //得到inputStreamReader
      reader = new InputStreamReader(getResourceAsStream(resource));
    } else {
      reader = new InputStreamReader(getResourceAsStream(resource), charset);
    }
    return reader;
  }


实际上调用的主要是:ClassLoaderWrapper的getResourceAsStream方法
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
    for (ClassLoader cl : classLoader) {
    	if (null != cl) {

        // try to find the resource as passed
        InputStream returnValue = cl.getResourceAsStream(resource);

        // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
        if (null == returnValue) returnValue = cl.getResourceAsStream("/" + resource);

        if (null != returnValue) return returnValue;
      }
    }
    return null;
  }

在这里呢,我们发现classLoader 这里传入了多个classLoader 如下:
   return getResourceAsStream(resource, new ClassLoader[]{
        classLoader,
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        ClassLoader.getSystemClassLoader()
    });

这里应该是为了防止找不到相应的配置文件而采取的措施
需要复习一下类的加载机制了。
引用

JVM的类加载是通过ClassLoader以及其子类完成。


打印出传递的5个classLoader 发现结果如下:

发现后面三个classLoader是一样的 也就是System class loader


好了 现在已经得到配置文件 reader对象了 ,如下解析呢 看下一步
关键在于 SqlSessionFactoryBuilder这个类

//============================= 上面是方法重载==================================================
  public SqlSessionFactory build(Reader reader, String environment, Properties props) {
    try {
      //通过XMLConfigBuilder 解析 这个类是解析 configuration.xml的关键
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, props);
      Configuration config = parser.parse();
      return build(config);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
    	// 关闭 reader 所以不需要在程序中关闭 
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore.  Prefer previous error.
      }
    }
  }

这个时候需要看一看XMLConfigBuilder的代码
  public XMLConfigBuilder(Reader reader, String environment, Properties props) {
    super(new Configuration()); //创建一个新的Configuration对象
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    //创建xpath解析器,需要验证 主要是创建xml的document 便于引用
    this.parser = new XPathParser(reader, true, new XMLMapperEntityResolver(), props);
  }

 //======================================= 以上三个构造方法(reader,environment,props) ====================================================
  
  public Configuration parse() {// 解析xml
    if (parsed) {//若没有解析 则抛出异常 
      throw new BuilderException("Each MapperConfigParser can only be used once.");
    }
    parsed = true; //改变是否解析过的状态
    //应该是查找configuration节点 然后开始解析配置
    parseConfiguration(parser.evalNode("/configuration")); //将解析后的configuration后返回
    return configuration;
  }



实际上 关键是 parseConfiguration 来解析这个节点

/**
   * 根据根节点来解析
   * @param root
   * 
   * <configuration>
    <!-- properties文件 -->
    <properties resource="config/properties/db.properties"></properties>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${db.connection.driver_class}"/>
				<property name="url" value="${db.connection.url}"/>
				<property name="username" value="${db.connection.username}"></property>
				<property name="password" value="${db.connection.password}"></property>
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<!-- 活动的sqlxml -->
		<mapper resource="config/ibatis/test.xml" />
	</mappers>
</configuration>
   */
  private void parseConfiguration(XNode root) {
    try {
      typeAliasesElement(root.evalNode("typeAliases")); //注册别名
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      propertiesElement(root.evalNode("properties")); //属性文件
      settingsElement(root.evalNode("settings"));
      environmentsElement(root.evalNode("environments")); //解析environments节点 ,这里是非常关键的
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));//解析mapper文件
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }


那么如何解析typeAliases节点呢?
看如下代码:
/**
   * 解析别名
   * @param parent
   */
  private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        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);
        }
      }
    }
  }


TypeAliasRegistry 类型命名注册的方法 : registerAlias
TypeAliasRegistry类里定义了一个hashMap
如:
private final HashMap<String, Class> TYPE_ALIASES = new HashMap<String, Class>();

这里调用核心的方法:

  /**
   * 注册别名
   * @param alias  别名的名称 需要放入map中
   * @param value  value 相当于类的class
   */
  public void registerAlias(String alias, Class value) {
    assert alias != null;
    String key = alias.toLowerCase();//小写
    if (TYPE_ALIASES.containsKey(key) && !TYPE_ALIASES.get(key).equals(value.getName()) && TYPE_ALIASES.get(alias) != null) {
      if (!value.equals(TYPE_ALIASES.get(alias))) {
        throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(alias).getName() + "'."); //已经存在映射关系,抛出异常
      }
    }
    TYPE_ALIASES.put(key, value);
  }


这里最关键的节点是: enviroment
 /** 解析environments节点
   * <environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${db.connection.driver_class}"/>
				<property name="url" value="${db.connection.url}"/>
				<property name="username" value="${db.connection.username}"></property>
				<property name="password" value="${db.connection.password}"></property>
			</dataSource>
		</environment>
	</environments> 
   ***/
  private void environmentsElement(XNode context) throws Exception { 
    if (context != null) {
      if (environment == null) { //没有传递environment
        environment = context.getStringAttribute("default"); //得到default属性的名称
      }
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id"); //id属性
        if (isSpecifiedEnvironment(id)) { //id是否和默认的eviroment相同 ,若相同 则进行比较
          //当前是JDBC factory  以后还有其他的实现类  如下的方法解析transactionManager的节点
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          //得到数据工厂 得到数据源
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); //解析dataSource节点      
          Environment.Builder environmentBuilder = new Environment.Builder(id, txFactory, dsFactory.getDataSource());
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }


那么数据工厂怎么来处理节点的参数呢?
 /** 解析 dataSource 元素  **/
  private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");//POOLED,UNPOLLED 
      /**类似如下所示:
       * <dataSource type="POOLED">
				<property name="driver" value="${db.connection.driver_class}"/>
				<property name="url" value="${db.connection.url}"/>
				<property name="username" value="${db.connection.username}"></property>
				<property name="password" value="${db.connection.password}"></property>
				若是pooled可以有其他的属性 如最小的链接数
		  </dataSource>
       */
      Properties props = context.getChildrenAsProperties();
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
      factory.setProperties(props); //设置属性
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  }

主要的是方法factory.setProperties(props);
这里 我们可以看到 DataSourceFactory 有一个实现类UnpooledDataSourceFactory
如下代码实现:
 /** 传入的参数是DataSource的配置项 **/
  public void setProperties(Properties properties) {
    Properties driverProperties = new Properties();
    MetaObject metaDataSource = MetaObject.forObject(dataSource); //可以代理的为对象添加属性
    for (Object key : properties.keySet()) {
      String propertyName = (String) key;
      if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {//若属性名称是 以driver开头的
        String value = properties.getProperty(propertyName);
        driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
      } else if (metaDataSource.hasSetter(propertyName)) {
        String value = (String) properties.get(propertyName);//得到dataSource的节点属性集合
        Object convertedValue = convertValue(metaDataSource, propertyName, value);
        metaDataSource.setValue(propertyName, convertedValue); //此方法可以动态的调用set方法 很奇妙 ,明天继续看哈哈 
      } else {
        throw new DataSourceException("Unkown DataSource property: " + propertyName);
      }
    }
    if (driverProperties.size() > 0) {
      metaDataSource.setValue("driverProperties", driverProperties);
    }
  }


这里的关键是一个MetaObject对象的用法。
 Author author = new Author();
		 MetaObject metaDataSource = MetaObject.forObject(author);
		 metaDataSource.setValue("username","111111");

如上代码: MetaObject可以为对象赋值某一个属性 username ,所以 111111

这里需要详细介绍一下:MapWrapper 以及  BeanWrapper两个类
  • 大小: 7.6 KB
  • 大小: 53.4 KB
  • 大小: 21.9 KB
分享到:
评论

相关推荐

    MYBATIS学习资料文件

    "MYBATIS学习资料文件"这一标题明确指出,这是一份与MyBatis相关的学习资源集合,可能包含教程、示例代码、笔记等,旨在帮助用户理解和掌握MyBatis这一流行的数据持久层框架。 【描述解读】 描述中提到“mybatis所...

    MyBatis学习笔记1

    【标题】"MyBatis学习笔记1" 涉及的知识点主要集中在MyBatis这一流行的持久层框架上。MyBatis是一个优秀的Java ORM(对象关系映射)框架,它允许开发者将SQL查询与Java代码分离,使得数据库操作更加灵活且易于维护。...

    mybatis学习

    MyBatis 是一款优秀的...通过深入学习 MyBatis,你可以有效地将业务逻辑与数据访问层分离,提高代码的可维护性和可读性。同时,MyBatis 的灵活性和强大的功能使其成为 Java 开发者在处理 SQL 操作时的首选框架之一。

    mybatis学习笔记

    这篇“mybatis 学习笔记”将带你深入理解 MyBatis 的核心概念和主要功能。 1. **MyBatis 概述**: MyBatis 是一个轻量级的 Java ORM(对象关系映射)框架,它的核心是 SQL 映射文件和 SqlSessionFactory。ORM 技术...

    jee、spring、spring mvc、mybatis 学习(五)

    在本篇【jee、spring、spring mvc、mybatis 学习(五)】中,我们将深入探讨四个关键的Java企业级开发技术:Java EE(Java Enterprise Edition)、Spring框架、Spring MVC以及MyBatis。这些技术是现代Java Web应用...

    Mybatis学习笔记整理,全面详实,通俗易懂.md

    Mybatis学习笔记整理,全面详实,通俗易懂.md

    MyBatis学习实例

    本资源“MyBatis学习实例”旨在帮助你深入理解并掌握MyBatis的核心概念和实践应用。 1. **MyBatis概述** MyBatis是一个基于Java的持久层框架,它简化了数据库访问,通过XML或注解方式配置和映射原生信息,将接口和...

    MyBatis学习笔记.zip

    在这些MyBatis学习笔记中,我们可以期待涵盖以下几个关键知识点: 1. **MyBatis简介**:首先会介绍MyBatis的起源、目标和优点,例如简化数据库操作、提高开发效率、易于维护等。 2. **环境搭建**:包括如何下载...

    【狂神说】mybatis学习总结笔记(全)PDF格式文档 MyBatis.pdf

    MyBatis学习总结笔记 MyBatis是一个优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。MyBatis可以通过简单的XML或注解来配置和映射原始...

    Mybatis学习视频+教程+源码

    本资源包含Mybatis的学习视频、教程以及源码,非常适合初学者和进阶者进行深入研究。 首先,Mybatis的学习视频将覆盖从基础到高级的各个主题,如Mybatis的安装配置、XML配置文件的理解、动态SQL的运用、Mapper接口...

    Mybatis学习的demo工程

    在这个"Mybatis学习的demo工程"中,我们主要探讨以下几个关键知识点: 1. **MyBatis核心组件**:MyBatis的核心组件包括SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession和Mapper接口。...

    MyBatis学习笔记

    ### MyBatis学习笔记 #### 一、MyBatis简介及环境搭建 **MyBatis** 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis...

    MyBatis学习手册及配置文件

    这份“MyBatis学习手册及配置文件”提供了全面的学习资源,帮助用户深入理解并掌握MyBatis的核心概念和实践技巧。 首先,`SqlMapConfig.xml`是MyBatis的全局配置文件,它是MyBatis启动时加载的第一个配置文件,用于...

    mybatis学习的源码

    在"mybatis学习1"中,`com.cpf`包下的源码可能涉及MyBatis的基本使用,包括Mapper接口的实现、动态SQL、结果映射等。Mapper接口允许开发者定义SQL查询,并通过方法签名与SQL结果进行匹配。动态SQL是MyBatis的一大...

    Mybatis学习笔记+学习源码.zip

    在本学习资源中,你将找到一份详尽的Mybatis学习笔记和源码,这将极大地助力你的学习过程,尤其是对自学者而言。这份笔记被认为是非常有价值且深入的,作者在其中倾注了大量的精力,确保了内容的全面性和实用性。 ...

    MyBatis学习.pdf

    MyBatis是一款流行的Java持久层框架,它的设计理念是让程序员能够通过简单的配置和映射,将对象和数据库中的记录进行关联。MyBatis的主要特点是支持定制化SQL、存储过程以及高级映射,同时它减少了传统JDBC操作中...

    Mybatis学习源代码

    本文将基于“Mybatis学习源代码”这一主题,深入探讨Mybatis的基本概念、核心功能以及如何通过狂神说的视频教程进行学习。 Mybatis是一个基于Java的持久层框架,它提供了一个SQL映射框架,可以将SQL语句与Java代码...

    MyBatis学习课件&代码

    MyBatis的学习代码MyBatis的学习代码MyBatis的学习代码MyBatis的学习代码MyBatis的学习代码MyBatis的学习代码MyBatis的学习代码MyBatis的学习代码MyBatis的学习代码MyBatis的学习代码MyBatis的学习代码MyBatis的学习...

Global site tag (gtag.js) - Google Analytics