zoukankan      html  css  js  c++  java
  • MyBatis-Spring 执行SQL语句的流程

    1. 从SqlSessionDaoSupport开始

    通常我们使用MyBatis会让自己的DAO继承SqlSessionDaoSupport,那么SqlSessionDaoSupport是如何运作的呢,下面是SqlSessionDaoSupport的源代码

    复制代码
    /*
     *    Copyright 2010 The myBatis Team
     *
     *    Licensed under the Apache License, Version 2.0 (the "License");
     *    you may not use this file except in compliance with the License.
     *    You may obtain a copy of the License at
     *
     *       http://www.apache.org/licenses/LICENSE-2.0
     *
     *    Unless required by applicable law or agreed to in writing, software
     *    distributed under the License is distributed on an "AS IS" BASIS,
     *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *    See the License for the specific language governing permissions and
     *    limitations under the License.
     */
    package org.mybatis.spring.support;
    
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.dao.support.DaoSupport;
    import org.springframework.util.Assert;
    
    /**
     * Convenient super class for MyBatis SqlSession data access objects.
     * It gives you access to the template which can then be used to execute SQL methods.
     * <p>
     * This class needs a SqlSessionTemplate or a SqlSessionFactory.
     * If both are set the SqlSessionFactory will be ignored.
     *
     * @see #setSqlSessionFactory
     * @see #setSqlSessionTemplate
     * @see SqlSessionTemplate
     * @version $Id: SqlSessionDaoSupport.java 3266 2010-11-22 06:56:51Z simone.tripodi $
     */
    public abstract class SqlSessionDaoSupport extends DaoSupport {
    
        // 这个SqlSession就是我们平时用来执行SQL和事务的一次会话
        private SqlSession sqlSession;
    
        private boolean externalSqlSession;
    
        // 可以看到以下两个Autowired的set方法,实际上他们的功能都是设置sqlSession的实例
        // 区别在于一个是通过传入sqlSessionFactory然后包装成SqlSessionTemplate
        // 另一个直接传入SqlSessionTemplate赋值给sqlSession
        @Autowired(required = false)
        public final void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
            if (!this.externalSqlSession) {
                this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
            }
        }
    
        @Autowired(required = false)
        public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
            this.sqlSession = sqlSessionTemplate;
            this.externalSqlSession = true;
        }
    
        /**
         * Users should use this method to get a SqlSession to call its statement methods
         * This is SqlSession is managed by spring. Users should not commit/rollback/close it
         * because it will be automatically done.
         *
         * @return Spring managed thread safe SqlSession 
         */
        public final SqlSession getSqlSession() {
            return this.sqlSession;
        }
    
        /**
         * {@inheritDoc}
         */
        protected void checkDaoConfig() {
            Assert.notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
        }
    
    }
    复制代码

    2.SqlSessionDaoSupport中的SqlSession产生

    先提一提既然我们是用dao去继承这个SqlSessionDaoSupport,观察我们的dao的配置

    复制代码
        <bean id="userDao" class="dao.UserDao">
            <!--<property name="sqlSessionFactory" ref="sqlSessionFactory" />-->
            <property name="sqlSessionTemplate" ref="sqlSession" />
        </bean>
    
        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" >
            <!-- 第一个参数是 sqlSessionFactory -->
            <constructor-arg index="0" ref="sqlSessionFactory"/>
            <!-- 第二个参数是 ExecutorType -->
            <constructor-arg index="1" ref="BATCH"/>
        </bean>
    
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!-- 指定数据源 -->
            <property name="dataSource" ref="dataSource" />
            <!-- 指定MyBatis配置文件 -->
            <property name="configLocation" value="classpath:mybatis-config.xml" />
            <!-- 导入Mapper -->
            <property name="mapperLocations" value="classpath*:mappers/*.xml" />
        </bean>
    
        <!-- datasource -->
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatistest?characterEncoding=utf8" />
            <property name="username" value="root" />
            <property name="password" value="root" />
        </bean>
    复制代码

    观察我们的dao的配置可以发现我们可以配置sqlSessionFactory和sqlSessionTemplate,实际上我们配置 sqlSessionTemplate的话就是能多配置一个ExecutorType参数(这个参数在MyBatis的Settings参数里也可以找 到,叫做defaultExecutorType,参数配置详细介绍见 http://mybatis.github.io/mybatis-3/configuration.html),这个参数的选项有三个:

    SIMPLE: 普通SQL执行器,不会使用预解析和批量处理

    REUSE: 重用prepared statement

    BATCH: 不但重用prepared statement,而且能执行批量处理,如

    public void insertUsers(User[] users) {
       for (User user : users) {
         sqlSession.insert("org.mybatis.spring.sample.mapper.UserMapper.insertUser", user);
       }
     }

    3. 分析sqlSessionTemplate的构造

    查看sqlSessionTemplate的源代码,关键部分如下

    复制代码
        /**
         * Constructs a Spring managed {@link SqlSession} with the given
         * {@link SqlSessionFactory} and {@link ExecutorType}.
         * A custom {@link SQLExceptionTranslator} can be provided as an 
         * argument so any {@link PersistenceException} thrown by MyBatis
         * can be custom translated to a {@link RuntimeException}
         * The {@link SQLExceptionTranslator} can also be null and thus no
         * exception translation will be done and MyBatis exceptions will be 
         * thrown
         *
         * @param sqlSessionFactory
         * @param executorType
         * @param exceptionTranslator
         */
        public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
                PersistenceExceptionTranslator exceptionTranslator) {
    
            Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
            Assert.notNull(executorType, "Property 'executorType' is required");
    
            this.sqlSessionFactory = sqlSessionFactory;
            this.executorType = executorType;
            this.exceptionTranslator = exceptionTranslator;
            this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
                    SqlSessionFactory.class.getClassLoader(),
                    new Class[] { SqlSession.class }, 
                    new SqlSessionInterceptor());
        }
    复制代码

    它的功能如下

    1) 设置sqlSessionFactory,查看上面的xml的配置,就知道sqlSessionFactory这个东西其实就是用来指定 datasource,读取mybatis配置(environment,settings种种),还有读取mybatis的各个sql语句的 mapper的东西

    2) 设置executor type,上面已经介绍过了

    3) 设置exception translator,这个东西是用来将jdbc的异常转换成spring的sql异常的东西(因为jdbc的异常很单一,无法详细的表达出错时错误到底 是什么,所以spring自己写了一套更好理解的异常,这是题外话),这个东西保持默认就可以了,所以我们在配置sqlSessionTemplate的 bean的时候并没有配置这个参数,如果没有配置,则构造方法会使用一个默认的(这是一个重载方法,另外还有一个构造方法是不需要设置这个参数的,那个构 造方法调用了这个构造方法,其中execeptionTranslator就是传了一个默认的进来)

    4) 对SqlSessionInteceptor()设置了一个代理,从这个动态代理的构造函数参数我们就能看出来这个东西是一个SqlSession

    其中,这个SqlSessionInterceptor是SqlSessionTemplate一个内部类,他返回了一个SqlSession关键代码如下

                final SqlSession sqlSession = SqlSessionUtils.getSqlSession(
                        SqlSessionTemplate.this.sqlSessionFactory,
                        SqlSessionTemplate.this.executorType);

    很简单,其实就是用sqlSessionFactory和executorType生成了一个sqlSession

    接下来进入一段七弯八拐的调用,过程如下

    SqlSessionUtils.getSqlSession() -> SessionFactory.openSession(executorType, conn) -> DefaultSessionFactory.openSession(ExecutorType execType) ->Configuration.newExecutor(tx, execType, autoCommit) -> return new DefaultSqlSession(configuration, executor)

    其中的关键部分是newExecutor(),这玩意就是生成SQL语句执行器的地方,生成的代码如下:
    复制代码
      public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        // 这里就是根据ExecutorType创建Executor的地方
        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);
        }
        // 这句就是判断setting里的cacheEnabled参数的地方
        // 所以设置了cacheEnabled参数后就会被包装成缓存Executor
        if (cacheEnabled) {
          executor = new CachingExecutor(executor, autoCommit);
        }
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
      }
    复制代码
    主要做的事情就是 1) 根据executorType生成合适的executor 2) 更具cacheEnabled参数包装executor 



    至此, SqlSessionTemplate中的sqlSessionProxy的executor终于生成出来,以后我们使用dao中的session来执行sql相关的操作用的就都是这个SqlSessionTemplate中的sqlSessionProxy

    最后,画个图总结一下


    也就是说,我们其实使用的是SqlSessionTemplate在做各种数据库操作,这个东西读取了我们的datasource和mybatisconfig,用它的Executor去执行我们Mapper里的sql语句来获取查询结果
  • 相关阅读:
    Java中一对多映射关系(转)
    java映射一对一关系 (转)
    如何创建JUnit
    Java数组转置
    get与post方法(吴老师整理)
    后台获得数据
    JDK1.8的安装与卸载
    使用JSP输出九九乘法表
    foreach
    匿名内部类
  • 原文地址:https://www.cnblogs.com/wenlj/p/4608320.html
Copyright © 2011-2022 走看看