Mybatis源码分析一(SqlsessionFactory及源码整体结构)

搞java的想提高自己的姿势水平,想拿高工资,对常用开源框架的深入了解是必不可少的,想深入了解源码分析更是必不可少的,今天我开始对mybatis的源码进行分析,并做点记录以备查验。开源框架研究,文档的获取建议去读官方的文档和例子,这样获得的知识成体系,成体系的知识被你掌握了,你就可以说你精通它了。好了,开始吧。

上面说道要看官方的文档,那么就得找到官方网站什么的对吧?这里给几个网站都是不错的:

Myabtis官网:http://www.mybatis.org/

github地址:https://github.com/mybatis/mybatis-3

这里我使用mybatis-3.1.1版本做分析,更新的版本自己去github下吧,好了这是下载到的项目:

起一个项目来边写代码,边分析!

1.新建一个工程,web或者java工程都可以,将以下的jar包拷到lib目录并buildPath:

2.在src目录下创建mybatis配置文件Configuration.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>
    <typeAliases>
        <typeAlias alias="AdminPermissionGroup" type="com.raykip.study.mybatis.model.AdminPermissionGroup"/>
    </typeAliases> 

    <environments default="development">
        <environment id="development">
        <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8" />
            <property name="username" value="root"/>
            <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="com/raykip/study/mybatis/model/AdminPermissionGroup.xml"/>
    </mappers>
</configuration>

3.准备一下测试数据吧:

/*
Navicat MySQL Data Transfer

Source Server         : raykipp
Source Server Version : 50627
Source Host           : 127.0.0.1:3306
Source Database       : test

Target Server Type    : MYSQL
Target Server Version : 50627
File Encoding         : 65001

Date: 2016-05-15 11:21:37
*/SET FOREIGN_KEY_CHECKS=0;-- ------------------------------ Table structure for `t_admin_permission_group`-- ----------------------------DROPTABLEIFEXISTS`t_admin_permission_group`;CREATETABLE`t_admin_permission_group` (
  `groupId`bigint(20) NOTNULL AUTO_INCREMENT COMMENT'权限组表Id',
  `groupName`varchar(20) DEFAULTNULLCOMMENT'权限名称',
  `status`int(10) DEFAULTNULLCOMMENT'状态,1表示有效,0表示无效',
  `orderId`int(10) DEFAULT'0'COMMENT'排序号',
  `creater`varchar(20) DEFAULTNULLCOMMENT'创建者',
  `createTime` datetime DEFAULTNULLCOMMENT'创建时间',
  `modifyer`varchar(20) DEFAULTNULLCOMMENT'修改者',
  `modifyTime` datetime DEFAULTNULLCOMMENT'修改时间',
  PRIMARY KEY (`groupId`)
) ENGINE=InnoDB AUTO_INCREMENT=13DEFAULTCHARSET=utf8;-- ------------------------------ Records of t_admin_permission_group-- ----------------------------INSERTINTO`t_admin_permission_group`VALUES ('1', '用户管理', '1', '1', 'wxh', '2015-06-21 09:41:35', 'wxh', '2015-06-21 09:41:43');

4.看到上面的xml里面配有AdminPermissionGroup实体类和它的映射文件,代码如下:

AdminPermissionGroup.java

package com.raykip.study.mybatis.model;

import java.sql.Timestamp;

publicclassAdminPermissionGroup {

	/**权限组表Id*/private Long groupId;
	/**权限组名称*/private String groupName;
	/**状态,1表示有效,0表示无效*/privateint status;
	/**排序号*/privateint orderId;
	/**创建者*/private String creater;
	/**创建时间*/private Timestamp createTime;
	/**修改者*/private String modifyer;
	/**修改时间*/private Timestamp modifyTime;		

	public Long getGroupId() {
		return groupId;
	}

	publicvoidsetGroupId(Long groupId) {
		this.groupId = groupId;
	}

	public String getGroupName() {
		return groupName;
	}

	publicvoidsetGroupName(String groupName) {
		this.groupName = groupName;
	}

	publicintgetStatus() {
		return status;
	}

	publicvoidsetStatus(int status) {
		this.status = status;
	}

	publicintgetOrderId() {
		return orderId;
	}

	publicvoidsetOrderId(int orderId) {
		this.orderId = orderId;
	}

	public String getCreater() {
		return creater;
	}

	publicvoidsetCreater(String creater) {
		this.creater = creater;
	}

	public Timestamp getCreateTime() {
		return createTime;
	}

	publicvoidsetCreateTime(Timestamp createTime) {
		this.createTime = createTime;
	}

	public String getModifyer() {
		return modifyer;
	}

	publicvoidsetModifyer(String modifyer) {
		this.modifyer = modifyer;
	}

	public Timestamp getModifyTime() {
		return modifyTime;
	}

	publicvoidsetModifyTime(Timestamp modifyTime) {
		this.modifyTime = modifyTime;
	}

}
<pre name="code" class="html" style="font-size: 14px; line-height: 21px; widows: 1;">AdminPermissionGroup.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="AdminPermissionGroup" >
  <resultMap id="BaseResultMap" type="com.raykip.study.mybatis.model.AdminPermissionGroup" >
    <id column="groupId" property="groupId" jdbcType="BIGINT" />
    <result column="groupName" property="groupName" jdbcType="VARCHAR" />
    <result column="status" property="status" jdbcType="INTEGER" />
    <result column="orderId" property="orderId" jdbcType="INTEGER" />
    <result column="creater" property="creater" jdbcType="VARCHAR" />
    <result column="createTime" property="createTime" jdbcType="TIMESTAMP" />
    <result column="modifyer" property="modifyer" jdbcType="VARCHAR" />
    <result column="modifyTime" property="modifyTime" jdbcType="TIMESTAMP" />
  </resultMap>
  <sql id="Base_Column_List" >
    groupId,groupName,status,orderId,creater,createTime,modifyer,modifyTime
  </sql>

  <!-- #是特殊字符只能通过定义变量间接输出  -->

  <!-- 按id查找-->
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select <include refid="Base_Column_List" /> from t_admin_permission_group
    where groupId = #{groupId,jdbcType=BIGINT}
  </select>
  <!-- 按对象查找-->
  <select id="selectByObject" resultMap="BaseResultMap" parameterType="com.raykip.study.mybatis.model.AdminPermissionGroup" >
    select
    <include refid="Base_Column_List" />
    from t_admin_permission_group
    where 1=1
        <if test="groupId != null" >
         and groupId = #{groupId,jdbcType=BIGINT}
         </if>
        <if test="groupName != null" >
         and groupName = #{groupName,jdbcType=VARCHAR}
         </if>
        <if test="status != null" >
         and status = #{status,jdbcType=INTEGER}
         </if>
        <if test="orderId != null and orderId != 0" >
         and orderId = #{orderId,jdbcType=INTEGER}
         </if>
        <if test="creater != null" >
         and creater = #{creater,jdbcType=VARCHAR}
         </if>
        <if test="createTime != null" >
         and createTime = #{createTime,jdbcType=TIMESTAMP}
         </if>
        <if test="modifyer != null" >
         and modifyer = #{modifyer,jdbcType=VARCHAR}
         </if>
        <if test="modifyTime != null" >
         and modifyTime = #{modifyTime,jdbcType=TIMESTAMP}
         </if>
         order by groupId
  </select>
  <!-- 统计总数 -->
  <select id="countAll" resultType="java.lang.Integer">
      select count(1) as count from t_admin_permission_group
  </select>
 <!-- 统计总数(部分) -->
  <select id="countSelective" resultType="java.lang.Integer" parameterType="com.raykip.study.mybatis.model.AdminPermissionGroup">
      select count(1) as count from t_admin_permission_group where 1=1
        <if test="groupId != null" >
         and groupId = #{groupId,jdbcType=BIGINT}
         </if>
        <if test="groupName != null" >
         and groupName = #{groupName,jdbcType=VARCHAR}
         </if>
         <!--
        <if test="status != null" >
         and status = #{status,jdbcType=INTEGER}
         </if>
         -->
        <if test="orderId != null and orderId != 0" >
         and orderId = #{orderId,jdbcType=INTEGER}
         </if>
        <if test="creater != null" >
         and creater = #{creater,jdbcType=VARCHAR}
         </if>
        <if test="createTime != null" >
         and createTime = #{createTime,jdbcType=TIMESTAMP}
         </if>
        <if test="modifyer != null" >
         and modifyer = #{modifyer,jdbcType=VARCHAR}
         </if>
        <if test="modifyTime != null" >
         and modifyTime = #{modifyTime,jdbcType=TIMESTAMP}
         </if>
  </select>
  <!-- 分页&条件查询-->
  <select id="selectByObjectLimit" resultMap="BaseResultMap" parameterType="com.raykip.study.mybatis.model.AdminPermissionGroup" >
    select
    <include refid="Base_Column_List" />
    from t_admin_permission_group
    where 1=1
        <if test="groupId != null" >
         and groupId = #{groupId,jdbcType=BIGINT}
         </if>
        <if test="groupName != null" >
         and groupName = #{groupName,jdbcType=VARCHAR}
         </if>
         <!--
        <if test="status != null" >
         and status = #{status,jdbcType=INTEGER}
         </if>
         -->
        <if test="orderId != null and orderId != 0" >
         and orderId = #{orderId,jdbcType=INTEGER}
         </if>
        <if test="creater != null" >
         and creater = #{creater,jdbcType=VARCHAR}
         </if>
        <if test="createTime != null" >
         and createTime = #{createTime,jdbcType=TIMESTAMP}
         </if>
        <if test="modifyer != null" >
         and modifyer = #{modifyer,jdbcType=VARCHAR}
         </if>
        <if test="modifyTime != null" >
         and modifyTime = #{modifyTime,jdbcType=TIMESTAMP}
         </if>
  </select>
  <!-- 多条件查询-->
  <select id="selectListByObject" resultMap="BaseResultMap" parameterType="com.raykip.study.mybatis.model.AdminPermissionGroup" >
    select
    <include refid="Base_Column_List" />
    from t_admin_permission_group
    where 1=1
        <if test="groupId != null" >
         and groupId = #{groupId,jdbcType=BIGINT}
         </if>
        <if test="groupName != null" >
         and groupName = #{groupName,jdbcType=VARCHAR}
         </if>
        <if test="status != null" >
         and status = #{status,jdbcType=INTEGER}
         </if>
        <if test="orderId != null and orderId != 0" >
         and orderId = #{orderId,jdbcType=INTEGER}
         </if>
        <if test="creater != null" >
         and creater = #{creater,jdbcType=VARCHAR}
         </if>
        <if test="createTime != null" >
         and createTime = #{createTime,jdbcType=TIMESTAMP}
         </if>
        <if test="modifyer != null" >
         and modifyer = #{modifyer,jdbcType=VARCHAR}
         </if>
        <if test="modifyTime != null" >
         and modifyTime = #{modifyTime,jdbcType=TIMESTAMP}
         </if>
  </select>
  <!-- 插入数据-->
  <insert id="insert" parameterType="com.raykip.study.mybatis.model.AdminPermissionGroup" >
    insert into t_admin_permission_group (
      groupId,groupName,status,orderId,creater,createTime,modifyer,modifyTime
      )
    values (
          #{groupId,jdbcType=BIGINT},
          #{groupName,jdbcType=VARCHAR},
          #{status,jdbcType=INTEGER},
          #{orderId,jdbcType=INTEGER},
          #{creater,jdbcType=VARCHAR},
          #{createTime,jdbcType=TIMESTAMP},
          #{modifyer,jdbcType=VARCHAR},
          #{modifyTime,jdbcType=TIMESTAMP}
      )
  </insert>
  <!-- 插入数据(部分字段)-->
  <insert id="insertSelective" parameterType="com.raykip.study.mybatis.model.AdminPermissionGroup" >
    insert into t_admin_permission_group
    <trim prefix="(" suffix=")" suffixOverrides="," >
        <if test="groupId != null" >
          groupId,
         </if>
        <if test="groupName != null" >
          groupName,
         </if>
        <if test="status != null" >
          status,
         </if>
        <if test="orderId != null and orderId != 0" >
          orderId,
         </if>
        <if test="creater != null" >
          creater,
         </if>
        <if test="createTime != null" >
          createTime,
         </if>
        <if test="modifyer != null" >
          modifyer,
         </if>
        <if test="modifyTime != null" >
          modifyTime,
         </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
        <if test="groupId != null" >
          #{groupId,jdbcType=BIGINT},
         </if>
        <if test="groupName != null" >
          #{groupName,jdbcType=VARCHAR},
         </if>
        <if test="status != null" >
          #{status,jdbcType=INTEGER},
         </if>
        <if test="orderId != null" >
          #{orderId,jdbcType=INTEGER},
         </if>
        <if test="creater != null" >
          #{creater,jdbcType=VARCHAR},
         </if>
        <if test="createTime != null" >
          #{createTime,jdbcType=TIMESTAMP},
         </if>
        <if test="modifyer != null" >
          #{modifyer,jdbcType=VARCHAR},
         </if>
        <if test="modifyTime != null" >
          #{modifyTime,jdbcType=TIMESTAMP},
         </if>
    </trim>
  </insert>
  <!-- 批量插入数据-->
  <insert id="batchInsert" parameterType="java.util.List">
      insert into t_admin_permission_group
      (groupId,groupName,status,orderId,creater,createTime,modifyer,modifyTime) values
      <foreach collection="list" item="item" index="index" separator=",">
      (
          #{groupId,jdbcType=BIGINT},
          #{groupName,jdbcType=VARCHAR},
          #{status,jdbcType=INTEGER},
          #{orderId,jdbcType=INTEGER},
          #{creater,jdbcType=VARCHAR},
          #{createTime,jdbcType=TIMESTAMP},
          #{modifyer,jdbcType=VARCHAR},
          #{modifyTime,jdbcType=TIMESTAMP}
    )
      </foreach>
  </insert>
  <!-- 删除数据-->
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
    delete from t_admin_permission_group
    where groupId = #{groupId,jdbcType=BIGINT}
  </delete>
  <!-- 更新数据(部分字段)-->
  <update id="updateByPrimaryKeySelective" parameterType="com.raykip.study.mybatis.model.AdminPermissionGroup" >
    update t_admin_permission_group
    <set >
      <if test="groupName != null" >
        groupName = #{groupName,jdbcType=VARCHAR},
      </if>
      <if test="status != null" >
        status = #{status,jdbcType=INTEGER},
      </if>
      <if test="orderId != null and orderId != 0" >
        orderId = #{orderId,jdbcType=INTEGER},
      </if>
      <if test="creater != null" >
        creater = #{creater,jdbcType=VARCHAR},
      </if>
      <if test="createTime != null" >
        createTime = #{createTime,jdbcType=TIMESTAMP},
      </if>
      <if test="modifyer != null" >
        modifyer = #{modifyer,jdbcType=VARCHAR},
      </if>
      <if test="modifyTime != null" >
          modifyTime = #{modifyTime,jdbcType=TIMESTAMP}
      </if>
    </set>
    where groupId = #{groupId,jdbcType=BIGINT}
  </update>
  <!-- 更新数据-->
  <update id="updateByPrimaryKey" parameterType="com.raykip.study.mybatis.model.AdminPermissionGroup" >
    update t_admin_permission_group set
          groupName = #{groupName,jdbcType=VARCHAR},
          status = #{status,jdbcType=INTEGER},
          orderId = #{orderId,jdbcType=INTEGER},
          creater = #{creater,jdbcType=VARCHAR},
          createTime = #{createTime,jdbcType=TIMESTAMP},
          modifyer = #{modifyer,jdbcType=VARCHAR},
          modifyTime = #{modifyTime,jdbcType=TIMESTAMP}
    where groupId = #{groupId,jdbcType=BIGINT}
  </update>
  <!-- 批量更新数据 -->
  <update id="batchUpdate" parameterType="map">
      update t_admin_permission_group
      <set >
        groupId = #{groupId,jdbcType=BIGINT},
        groupName = #{groupName,jdbcType=VARCHAR},
        status = #{status,jdbcType=INTEGER},
        orderId = #{orderId,jdbcType=INTEGER},
        creater = #{creater,jdbcType=VARCHAR},
        createTime = #{createTime,jdbcType=TIMESTAMP},
        modifyer = #{modifyer,jdbcType=VARCHAR},
        modifyTime = #{modifyTime,jdbcType=TIMESTAMP}
    </set>
       where groupId in
      <foreach collection="list" item="item" index="index" open="(" separator="," close=")" >
            #{item.groupId}
     </foreach>
  </update>
</mapper>

5.写一个测试类测试一下吧!

package com.raykip.study.mybatis.test;

import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.alibaba.fastjson.JSON;
import com.raykip.study.mybatis.model.AdminPermissionGroup;

public class SqlsessionTest{
	privatestaticSqlSessionFactory sqlSessionFactory;
    privatestaticReader reader; 

    static{
        try{
            reader    = Resources.getResourceAsReader("Configuration.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    publicstaticSqlSessionFactory getSession(){
        return sqlSessionFactory;
    }

    publicstatic void main(String[] args) {
        SqlSession session = sqlSessionFactory.openSession();
        try {
        	AdminPermissionGroup group = (AdminPermissionGroup) session.selectOne("AdminPermissionGroup.selectByPrimaryKey", 1);
        	System.out.println(JSON.toJSONString(group));
        } finally {
        	session.close();
        }
    }

}

6.运行结果如下:

log4j:WARN No appenders could be found for logger (org.apache.ibatis.logging.LogFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
{"createTime":1434850895000,"creater":"wxh","groupId":1,"groupName":"用户管理","modifyTime":1434850903000,"modifyer":"wxh","orderId":1,"status":1}

这些是结果,接下来结合SqlsessionTest类来分析源码!

1.在本类中先定义了一个SqlSessionFactory接口,完整接口名是org.apache.ibatis.session.SqlSessionFactory,源码如下:

/*
 *    Copyright 2009-2012 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.apache.ibatis.session;

import java.sql.Connection;

publicinterfaceSqlSessionFactory{

  SqlSession openSession();

  SqlSession openSession(boolean autoCommit);
  SqlSession openSession(Connection connection);
  SqlSession openSession(TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType);
  SqlSession openSession(ExecutorType execType, boolean autoCommit);
  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType, Connection connection);

  Configuration getConfiguration();

}
我们看到这个接口主要定义了openSeeion、getConfiguration这2个接口,如果你用过hibernate应该能猜到一个打开seesion,一个是用来获取配置信息的,接下来看一下它们的实现类:

http://tool.oschina.net/apidocs/apidoc?api=mybatis-3.1.1这个文档有这2个类的说明,也就是说你可以使用这2个类来获取sqlSessionFactory,比如:
sqlSessionFactory = SqlSessionManager.newInstance(reader);
或者
DataSource dataSource = new PooledDataSource("com.mysql.jdbc.Driver",
                    "jdbc:mysql://127.0.0.1:3306/circcenter?useUnicode=true&characterEncoding=UTF-8", "root", "root");
    Environment environment = new Environment("test", new JdbcTransactionFactory(), dataSource);
    Configurationconfiguration = newConfiguration(environment);
    configuration.addMapper(AdminPermissionGroup.class);
    //SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
            sqlSessionFactory = new DefaultSqlSessionFactory(configuration);
通过看代码,发现SqlSessionFactoryBuilder类起到关键作用,OK我们看一下这个类:

首先用Eclipse工具查看SqlSessionFactoryBuilder类的Outline视图:

其实这个三个方法才是最重要的:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties)
public SqlSessionFactory build(Reader reader, String environment, Properties properties)
public SqlSessionFactory build(Configuration config)

我们来看一下build方法的源码:


  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.

      }

    }

  }

通过上面这几行代码,就能看出基于XML文件的这种构造方式,通过从XML中读取信息的工作之后,也是构造出Configuration对象之后再继续进行SqlSessionFactory的构建工作的,只是多了些XML的解析工作,所以我们只需单刀直入,直按分析编程构造方式的代码就可以了,或者是直接分析 build(parser.parse())这句代码(参数产生过程先跳过)

编程构造方式的build方法源码如下(基于xml的构造方式的build(parser.parse())最终也是调了这个代码):

public SqlSessionFactory build(Configuration config) {
                returnnew DefaultSqlSessionFactory(config);
    }

其实这么看来SqlSessionFactory在mybatis的默认实现类为org.apache.ibatis.session.defaults.DefaultSqlSessionFactory , 其构造过程主要是注入了Configuration的实例对象,Configuration的实例对象即可通过解析xml配置文件产生,也可能通过代码直接构造。以上代码使用了一个设计模式:建设者模式(Builder),SqlSessionFactoryBuilder扮演具体的建造者,Configuration类则负责建造的细节工作,SqlSession则是建造出来的产品。

以下是类图和建造者模式的基本形态图,读者自行对照阅读。

构造者模式是一种对象的创建模式。它可以将一个复杂对象的内部构成特征与对象的构建过程完全分开。

在本类中使用static静态块初始化了sqlsessionFactory,在实际项目中会写一个单例类获取sqlsessionFactory或者获取Sqlsession,原因和Hibernate一样,这个类加载比较消耗资源。例如:

package com.raykip.study.mybatis.util;

import java.io.IOException; 
import java.io.Reader;

import org.apache.ibatis.io.Resources; 
import org.apache.ibatis.session.SqlSessionFactory; 
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

publicclassSqlSessionFactoryUtil{ privatestatic SqlSessionFactory sqlSessionFactory;

publicstatic SqlSessionFactory getSqlSessionFactory()throws IOException{
	 if(sqlSessionFactory==null){
		 Reader reader    = Resources.getResourceAsReader("Configuration.xml");
         returnnew SqlSessionFactoryBuilder().build(reader);
     }else{
         return sqlSessionFactory;
     }
}
}
好了,我们再来看下Sqlsession这个接口,我相信大家对这个类不会陌生,它是一个会话,与数据库的会话,通过它可以执行sql返回结果集、提交/回滚事务等操作。还是看一下outline图:

相信看到这些方法,可以猜到这些方法是干嘛的,包含的select、insert、update、delete方法都是基本的数据库操作,我们可以利用这些方法封装我们自己的BaseDao,并进行扩展。实现类有这2个:

我们先看一下测试类中使用的public <T> T selectOne(String statement, Object parameter) 方法,这个方法的作用显然是返回一个结果集,源码如下:

public <T> T selectOne(String statement, Object parameter){
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.<T>selectList(statement, parameter);
    if (list.size() == 1) {
      returnlist.get(0);
    } elseif (list.size() > 1) {
      thrownew TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }

我们看到其实它调用的是this.<T>selectList(statement, parameter)方法,好吧继续:

public <E> List<E> selectList(String statement, Object parameter) {
    return this.<E>selectList(statement, parameter, RowBounds.DEFAULT);
  }

  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      List<E> result = executor.<E>query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
      returnresult;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

这里已经比较复杂了,必须通过debug来领会代码的含义:

通过设置断点,selectList方法中首先取到了MappedStatement,MappedStatement类在Mybatis框架中用于表示XML文件中一个sql语句节点,即一个<select
/>、<update />或者<insert />标签。Mybatis框架在初始化阶段会对XML配置文件进行读取,将其中的sql语句节点对象化为一个个MappedStatement对象。

也就是说mybatis通过<mapper>标签的namespace和<select>标签的id属性找到指定的sql。现在这里取到的statement是AdminPermissionGroup.selectByPrimaryKey,就是我们调用selectOne传进来的参数,parameter=1也是我们传进来的参数,我们继续看它是怎么找到sql的:

发现如果validateIncompleteStatements为false,跳到了这里:

原来是Configuration类的mappedStatements属性,原来当加载AdminPermissionGroup.xml文件时,xml的每个节点都被加载到了mappedStatements这个map中。

{countAll=[email protected], AdminPermissionGroup.selectByPrimaryKey=[email protected]be2c3, AdminPermissionGroup.selectByObject=[email protected], batchInsert=[email protected], updateByPrimaryKeySelective=[email protected], batchUpdate=[email protected], insert=[email protected], selectListByObject=[email protected], AdminPermissionGroup.selectByObjectLimit=[email protected], AdminPermissionGroup.deleteByPrimaryKey=[email protected], selectByPrimaryKey=[email protected], selectByObjectLimit=[email protected], AdminPermissionGroup.insertSelective=[email protected], AdminPermissionGroup.batchInsert=[email protected], updateByPrimaryKey=[email protected], insertSelective=[email protected], AdminPermissionGroup.selectListByObject=[email protected], AdminPermissionGroup.insert=[email protected], countSelective=[email protected], AdminPermissionGroup.updateByPrimaryKey=[email protected], AdminPermissionGroup.updateByPrimaryKeySelective=[email protected], AdminPermissionGroup.batchUpdate=[email protected], deleteByPrimaryKey=[email protected], AdminPermissionGroup.countSelective=[email protected], AdminPermissionGroup.countAll=[email protected], selectByObject=[email protected]}

但是这里的validateIncompleteStatements的意思是校验Statement是否正确的含义,源码如下:

最终还是会从mappedStatements这个map中取到对应的select节点,而我们的sql就存在MappedStatement对象的sqlSource属性中,通过getBoundSql方法获取的:

从这段代码中我们可以看到,mybatis默认是会缓存结果集的:

由于有缓存这个方法就直接看最后一段:

接下来就快到了JDBC的代码了:

我们看到最终还是调用了JDBC的代码,可见mybatis对了JDBC做了封装,也就是说当mybatis不能满足我们的需要的时候,我们可以扩展的方式取到PreparedStatement使用JDBC的代码来完成我们的需求。那么接下来就是对JDBC返回的结果集进行封装、包装成resultMap设定的实体类了:

public List<Object> handleResultSets(Statement stmt) throws SQLException {
    finalList<Object> multipleResults = new ArrayList<Object>();
    finalList<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    int resultSetCount = 0;
    ResultSet rs = stmt.getResultSet();

    while (rs == null) {
      // move forward to get the first resultset in case the driver// doesn‘t return the resultset as the first result (HSQLDB 2.1)if (stmt.getMoreResults()) {
        rs = stmt.getResultSet();
      } else {
        if (stmt.getUpdateCount() == -1) {
          // no more results.  Must be no resultsetbreak;
        }
      }
    }

    validateResultMapsCount(rs, resultMapCount);
    while (rs != null && resultMapCount > resultSetCount) {
      final ResultMap resultMap = resultMaps.get(resultSetCount);
      ResultColumnCache resultColumnCache = new ResultColumnCache(rs.getMetaData(), configuration);
      handleResultSet(rs, resultMap, multipleResults, resultColumnCache);
      rs = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
    return collapseSingleResultList(multipleResults);
  }
resultHandler这里可以对结果集进行自定义的操作,也可以自己实现一个resultHandler覆盖这个里的resultHandler:

这里按实体类的属性类型获取值:

好了,今天先分析到这里,谢谢大家!

时间: 2024-10-30 05:39:18

Mybatis源码分析一(SqlsessionFactory及源码整体结构)的相关文章

[转] jQuery源码分析-如何做jQuery源码分析

jQuery源码分析系列(持续更新) jQuery的源码有些晦涩难懂,本文分享一些我看源码的方法,每一个模块我基本按照这样的顺序去学习. 当我读到难度的书或者源码时,会和<如何阅读一本书>结合起来进行学习.推荐读读这本书,你可以从这里和这里下载. 第一部分:检视阅读 1. 收集参考资料:官方文档.书籍.百度/谷歌,专题/博客等,快速的浏览,对涉及的知识点.范围.深度.是否有参考意义等有大致的了解和判断,知道这些文章的作者想要解释或解决什么问题. 第二部分:分析阅读 2. 细读官方文档,官方有非

Android 7.0 Gallery图库源码分析1 - 初识Gallery源码

分析一个项目的源代码时,第一件事就是查看清单文件,找到程序入口,我们从Gallery2源码的清单文件中可以看到GalleryActivity是此应用的启动Activity. 1 <activity android:name="com.android.gallery3d.app.GalleryActivity" android:label="@string/app_name" 2 android:configChanges="keyboardHidde

Java源码分析系列之HttpServletRequest源码分析

从源码当中 我们可以 得知,HttpServletRequest其实 实际上 并 不是一个类,它只是一个标准,一个 接口而已,它的 父类是ServletRequest. 认证方式 public interface HttpServletRequest extends ServletRequest 从阅读源码 当中 ,我们 可以 获得 如下认证信息: /** * String identifier for Basic authentication. Value "BASIC" */ pu

Dubbo源码分析(一):Dubbo源码的结构概述

1.dubbo源码的结构 Dubbo源文件主要包含以上这么多包,其中:  dubbo-common 公共逻辑模块,包括Util类和通用模型. dubbo-remoting 远程通讯模块,相当于Dubbo协议的实现,如果RPC用RMI协议 则不需要使用此包. dubbo-rpc 远程调用模块,抽象各种协议,以及动态代理,只包含一对一的调用, 不关心集群的管理.  dubbo-cluster 集群模块,将多个服务提供方伪装为一个提供方,包括:负载均衡, 容 错,路由等,集群的地址列表可以是静态配置的

JDK1.8源码分析02之阅读源码顺序

序言:阅读JDK源码应该从何开始,有计划,有步骤的深入学习呢? 下面就分享一篇比较好的学习源码顺序的文章,给了我们再阅读源码时,一个指导性的标志,而不会迷失方向. 很多java开发的小伙伴都会阅读jdk源码,然而确不知道应该从哪读起.有些零零散散的学习,知识与知识之间没有相互联系起来,不成知识体系.以下为小编整理的通常所需阅读的源码范围. 标题为包名,后面序号为优先级1-4,优先级递减 1.java.lang 1) Object 1 2) String 1 3) AbstractStringBu

wifidog源码分析Lighttpd1.4.20源码分析之fdevent系统(1)---fdevents结构体和fdevent系统对外接口

前面讲了lighttpd的插件系统,这一篇将看一看lighttpd中的fdevent系统.fdevent系统主要是处理各种IO事件,在web服务器中,主要就是向socket写数据和从socket读数据.通常,web服务器是IO密集型程序,这就要求在数据的读写上,web服务器必须能够具有很好的性能,不会因为某个socket的阻塞而致使其他socket也被阻塞,否则会大大降低服务器的性能.因此,大部分的web服务器都采用非阻塞IO进行数据的读写.lighttpd通过fdevent系统,采用类似OO中

wifidog源码分析Lighttpd1.4.20源码分析之插件系统(3)---PLUGIN_TO_SLOT宏

前面讲了lighttpd插件系统的加载和初始化,这一篇中,将介绍一下plugin.c中的宏PLUGIN_TO_SLOT.在将PLUGIN_TO_SLOT宏之前,我们先来看看lighttpd中插件系统的对外接口.这个接口所对的“外”指的是lighttpd服务器.前面已经提到,在运行的过程中,lighttpd不知道所加载的插件都是干什么用的,只知道这些插件所实现的接口,也就是在plugin结构体中那些函数指针有哪些对于某个插件是NULL,哪些是具体的函数地址.既然lighttpd只知道这些,那么它又

Dubbo源码分析(八):Javassist字节码技术生成代理

Java动态编程的作用:      通过配置生成代码,减少重复编码和维护成本 我们常用到的动态特性主要是反射,在运行时查找对象属性.方法,修改作用域,通过方法名称调用方法等.在线的应用不会频繁使用反射,因为反射的性能开销较大.其实还有一种和反射一样强大的特性,但是开销却很低,它就是Javassit. Javassit其实就是一个二方包,提供了运行时操作Java字节码的方法.大家都知道,Java代码编译完会生成.class文件,就是一堆字节码.JVM(准确说是JIT)会解释执行这些字节码(转换为机

wifidog源码分析Lighttpd1.4.20源码分析之fdevent系统(3) -----使用

接着上文介绍的函数fdevent_linux_sysepoll_event_add 讲解,首先看函数的第三个参数events,他是一个整型,其没以为对应一种IO事件.上面fdevent_event_add()函数的额第三个参数是FDEVENT_IN,这是一个宏 /* * 用于标记文件描述符的状态 */ #define FDEVENT_IN BV(0) //文件描述符是否可写 #define FDEVENT_PRI BV(1) //不阻塞的可读高优先级的数据 poll #define FDEVEN

Mybatis源码分析之Cache二级缓存原理 (五)

一:Cache类的介绍 讲解缓存之前我们需要先了解一下Cache接口以及实现MyBatis定义了一个org.apache.ibatis.cache.Cache接口作为其Cache提供者的SPI(ServiceProvider Interface) ,所有的MyBatis内部的Cache缓存,都应该实现这一接口 Cache的实现类中,Cache有不同的功能,每个功能独立,互不影响,则对于不同的Cache功能,这里使用了装饰者模式实现. 看下cache的实现类,如下图: 1.FIFOCache:先进