zoukankan      html  css  js  c++  java
  • Mybatis初始化过程详解

    spring.xml配置如下:

    <bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <!--配置url地址的时候需要开启useUnicode且将encoding编码定义为UTF-8 否则插入数据的时候会出现???乱码
                而&amp;是因为如果单纯以&符号做拼接的话没法识别,要以&amp;的形式转义一次 -->
            <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf-8"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </bean>
        
        <bean id="sqlSessionFactory" class ="org.mybatis.spring.SqlSessionFactoryBean">
            <!--configLocation用于配置mybatis的配置文件地址
                 dataSource用于配置mybatis对应的数据源信息
                 mapperLocations用于配置xml文件扫描的路径
                 typeAliasesPackage对象的别名配置 -->
            <property name="configLocation" value="classpath:mybatis-config.xml"></property>        
            <property name="dataSource" ref="dataSource"></property>
            <!-- 该参数也可以放在mybatis-config中进行配置 所以该处省略即可
            <property name="mapperLocations" value="classpath:com/demo/dao/*Mapper.xml" ></property>
            <property name="typeAliasesPackage" value="com.demo.bean"></property>-->
        </bean>

    mybatis-config.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>
    
        <!-- 参数设置 -->
        <settings>
            <!-- 通过设置该参数可以将数据库cc_xx带下划线的参数默认映射到java属性中ccXx字段上 默认该属性为false -->
            <setting name="mapUnderscoreToCamelCase" value="true"/>
            <!-- 全局设置延迟加载 默认为false 如果不设置或者设置为false则全都正常加载-->
            <setting name="aggressiveLazyLoading" value="true"></setting>
            <!--全局的二级缓存的开关,默认就是开着的true。由于二级缓存是和命名空间绑定在一起的,
                想要使用这个二级缓存功能只需要开启后在每个xml文件的根节点mapper标签内加入<cache/>即可 -->
            <setting name="cacheEnabled" value="true"/>
        </settings>
    
        <!-- 别名定义 -->
        <typeAliases>
            <!-- typeAlias 将type实体类重命名为country -->
            <!-- 定义别名后在mapper.xml文件中直接使用别名即可(resultType='country')
                不需要再写全称了! 
            <typeAlias alias="country" type="org.web.blog.bean.Country"></typeAlias>-->
            
            <!-- package可以取代typeAlias 
                它是通过扫描路径下所用的bean 然后通过默认规则 将bean的首字母小写作为默认别名
                也可以在bean上通过@Alias("xxx")来手动创建别名 -->
            <package name ="com.demo.bean"></package>
        </typeAliases>
        
        <!-- mysql 配置节点 可以配置多个连接节点 
            具体使用哪个节点的配置可以由default参数来定义,default与子节点的id相关联-->
        <environments default="xs">
            <!-- 正式环境节点配置 -->
            <environment id="xs">
            <!--JDBC MANAGED
                JDBC:这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
                MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文).
                         默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。 -->
            <transactionManager type="JDBC"/>
                <!-- UNPOOLED POOLED JNDI
                    UNPOOLED:Mybatis会为每一个数据库操作创建一个新的连接,并关闭它,该方式
                             适用于只有小规模数量并发用户的简单应用程序上
                    POOLED:Mybatis会创建一个数据库连接池,连接池中的一个连接将会被用作数据库操作
                            一旦数据库操作完成,就会将次连接返回给连接池
                    JNDI:Mybatis从在应用服务器向配置好的JNDI数据源dataSource获取数据库连接
                         生产环境优先考虑 -->
                <dataSource type="UNPOOLED">
                    <property name="driver" value ="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
            <!-- 测试环境节点配置 
            <environment id="xss">
                <dataSource>
                </dataSource>
            </environment> -->
        </environments>
    
         <mappers>
             <!--直接映射到相应的mapper文件
            <mapper resource="com/demo/dao/UserMapper.xml"/>-->
            <!--扫描路径下java 接口文件,然后通过文件路径的全限定名作为路径去找对应的xml文件 xx.xx.java 对应xx.xx.xml
                如果xml文件与java文件的路径不一致 则映射失败,这个时候如果想单纯使用java文件 可以在接口方法上使用注解的方式来实现sql即可-->
            <package name="com.demo.dao"/>
        </mappers>
        
        
    </configuration>

    这2个文件就是Mybatis所有的配置参数的文件了,有了这两个文件Mybatis的工厂就能够初始化。

    下面来说说具体的初始化过程

            //通过Reader将配置文件读入
            Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
            
            //FIXME 数据库初始化问题。5.0版本 drive驱动异常  6.0就不会
            //通过SqlSessionFactoryBuilder创建以Reader读取的内容为基础的SqlSessionFactory工厂类
            //在创建SqlSessionFactory工厂类创建的过程中会去解析.xml配置文件中的配置属性及mappers映射的方法信息
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
            reader.close();
            
            //然后通过工厂类创建一个SqlSession,就可以使用SqlSession执行sql了
            //SqlSession中的方法是所用mybatis-config.xml映射的方法 如果这些方法中有重名的方法 就会出现方法名引用
            //如果多个xml文件中出现重名的sql方法时可以用全限定名来分别方法是属于哪个文件下的xx.xx.selectAll
            SqlSession sqlSession = sqlSessionFactory.openSession();

    可以看到系统通过读取mybatis-config.xml配置文件 然后通过将读取后的参数交给SqlSessionFactory工厂进行节点解析,并创建相应的SqlSession实例。通过创建

    后的SqlSession实例就可以实现sql的实现了。

    可以看到关键的流程就是在SqlSessionFactory中得以实现,接下来看看SqlSessionFactory是如何加载这些配置参数并返回的。

    它分别实现了

    FactoryBean<SqlSessionFactory>,

    InitializingBean,

    ApplicationListener<ApplicationEvent>

    这三个接口。

    实现了InitializingBean 后可以保证bean在初始化的时候,bean中的afterPropertiesSet()方法得以运行

      /**
       * {@inheritDoc}
       */
      @Override
      public void afterPropertiesSet() throws Exception {
        notNull(dataSource, "Property 'dataSource' is required");
        notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
        state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
                  "Property 'configuration' and 'configLocation' can not specified with together");
    
        this.sqlSessionFactory = buildSqlSessionFactory();
      }

    可以从源码中看到引用了一个buildSqlSessionFactory()方法来初始化sqlSessionFactory。

    下面是buildSqlSessionFactory()的源码。

    protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
    
        Configuration configuration;
    
        XMLConfigBuilder xmlConfigBuilder = null;
      
    if (this.configuration != null) { configuration = this.configuration; if (configuration.getVariables() == null) { configuration.setVariables(this.configurationProperties); } else if (this.configurationProperties != null) { configuration.getVariables().putAll(this.configurationProperties); } } else if (this.configLocation != null) { xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
    configuration
    = xmlConfigBuilder.getConfiguration(); } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration"); } configuration = new Configuration(); if (this.configurationProperties != null) { configuration.setVariables(this.configurationProperties); } } if (this.objectFactory != null) { configuration.setObjectFactory(this.objectFactory); } if (this.objectWrapperFactory != null) { configuration.setObjectWrapperFactory(this.objectWrapperFactory); } if (this.vfs != null) { configuration.setVfsImpl(this.vfs); } if (hasLength(this.typeAliasesPackage)) { String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan : typeAliasPackageArray) { configuration.getTypeAliasRegistry().registerAliases(packageToScan, typeAliasesSuperType == null ? Object.class : typeAliasesSuperType); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases"); } } } if (!isEmpty(this.typeAliases)) { for (Class<?> typeAlias : this.typeAliases) { configuration.getTypeAliasRegistry().registerAlias(typeAlias); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered type alias: '" + typeAlias + "'"); } } } if (!isEmpty(this.plugins)) { for (Interceptor plugin : this.plugins) { configuration.addInterceptor(plugin); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered plugin: '" + plugin + "'"); } } } if (hasLength(this.typeHandlersPackage)) { String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan : typeHandlersPackageArray) { configuration.getTypeHandlerRegistry().register(packageToScan); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers"); } } } if (!isEmpty(this.typeHandlers)) { for (TypeHandler<?> typeHandler : this.typeHandlers) { configuration.getTypeHandlerRegistry().register(typeHandler); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered type handler: '" + typeHandler + "'"); } } } if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls try { configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource)); } catch (SQLException e) { throw new NestedIOException("Failed getting a databaseId", e); } } if (this.cache != null) { configuration.addCache(this.cache); } if (xmlConfigBuilder != null) { try { xmlConfigBuilder.parse(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'"); } } catch (Exception ex) { throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex); } finally { ErrorContext.instance().reset(); } } if (this.transactionFactory == null) { this.transactionFactory = new SpringManagedTransactionFactory(); } configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource)); if (!isEmpty(this.mapperLocations)) { for (Resource mapperLocation : this.mapperLocations) { if (mapperLocation == null) { continue; } try { XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, mapperLocation.toString(), configuration.getSqlFragments()); xmlMapperBuilder.parse(); } catch (Exception e) { throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e); } finally { ErrorContext.instance().reset(); } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'"); } } } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found"); } } return this.sqlSessionFactoryBuilder.build(configuration); }

    可以从源码中看到 SqlSessionFactory创建的最终参数是configuration。

    而configuration的参数就是通过xmlConfigBuilder这个类解析xml文件并读取各个节点产生的一个初始化参数对象

    比较有意思的是XMLConfigBuilder 这个类继承自 BaseBuilder,而BaseBuilder中定义了final关键字的configuration对象

    从而当通过XMLConfigBuilder.getConfiguration 获得configuration对象时,这个对象也是final属性,即BaseBuilder定义的那个

    所以SqlSessionFactoryBean中buildSqlSessionFactory()方法内定义的局部变量configuration 被赋值的那刻起,指向的内存地址就已经与XMLConfigBuilder中的configuration 

    是一样的,也就是说这2个对象是相同的。

    这个时候再通过xmlConfigBuilder.parse()方法的调用

      private void parseConfiguration(XNode root) {
        try {
          //issue #117 read properties first
          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"));
          mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
      }

    可以看到是在解析xml文件中的各个标签,然后一次set到configuration 对象中去。

    这个时候buildSqlSessionFactory()方法中

    return this.sqlSessionFactoryBuilder.build(configuration);

    这个configuration参数就已经被初始化完毕。里面包含了mybatis-confg.xml中各个标签的属性等。

  • 相关阅读:
    无线电频谱和波段划分
    数字IC设计工程师推荐用书
    Verilog HDL 经典用书
    Interfacing Two Clock Domains
    值得借鉴的Perl学习总结
    MIMO技术原理、概念、现状简介
    cs0016:未能写入输出文件 "c:"WINDOWS"Microsoft.NET"Framework"v2.0.50727"Temporary ASP.NET Files"root"...."*.dll“拒绝访问”
    Sql Server 2005 数据库备份还原后出现“受限制用户”问题的解决
    两个路由器连接的连接方法
    2个表之间复制数据
  • 原文地址:https://www.cnblogs.com/culushitai/p/9018334.html
Copyright © 2011-2022 走看看