mybatis源码分析(一)

demo案例

public class MyBatisTest {

    public SqlSessionFactory getSqlSessionFactory() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        return new SqlSessionFactoryBuilder().build(inputStream);
    }

    /**
     * 1、获取sqlSessionFactory对象:
     *         解析文件的每一个信息保存在Configuration中,返回包含Configuration的DefaultSqlSession;
     *         注意:【MappedStatement】:代表一个增删改查的详细信息
     * 
     * 2、获取sqlSession对象
     *         返回一个DefaultSQlSession对象,包含Executor和Configuration;
     *         这一步会创建Executor对象;
     * 
     * 3、获取接口的代理对象(MapperProxy)
     *         getMapper,使用MapperProxyFactory创建一个MapperProxy的代理对象
     *         代理对象里面包含了,DefaultSqlSession(Executor)
     * 4、执行增删改查方法
     * 
     * 总结:
     *     1、根据配置文件(全局,sql映射)初始化出Configuration对象
     *     2、创建一个DefaultSqlSession对象,
     *         他里面包含Configuration以及
     *         Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor)
     *  3、DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy;
     *  4、MapperProxy里面有(DefaultSqlSession);
     *  5、执行增删改查方法:
     *          1)、调用DefaultSqlSession的增删改查(Executor);
     *          2)、会创建一个StatementHandler对象。
     *              (同时也会创建出ParameterHandler和ResultSetHandler)
     *          3)、调用StatementHandler预编译参数以及设置参数值;
     *              使用ParameterHandler来给sql设置参数
     *          4)、调用StatementHandler的增删改查方法;
     *          5)、ResultSetHandler封装结果
     *  注意:
     *      四大对象每个创建的时候都有一个interceptorChain.pluginAll(parameterHandler);
     * 
     * @throws IOException
     */
    @Test
    public void test01() throws IOException {
        // 1、获取sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        // 2、获取sqlSession对象
        SqlSession openSession = sqlSessionFactory.openSession();
        try {
            // 3、获取接口的实现类对象
            //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
            Employee employee = mapper.getEmpById(1);
            System.out.println(mapper);
            System.out.println(employee);
        } finally {
            openSession.close();
        }

    }

}

源码分析

一.
1.根据全局配置文件创建sqlsessionfactory (Configuration(数据源,全局配置信息),SqlSessio对象)
2.创建sqlsession对象
3.获取接口的实现类对象//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法

二.mybatis属性<configuration>
    <!--1、mybatis可以使用properties来引入外部properties配置文件的内容;resource:引入类路径下的资源 url:引入网络路径或者磁盘路径下的资源-->
    <properties resource="dbconfig.properties"></properties>    
    <settings>
        <!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
        <setting name="jdbcTypeForNull" value="NULL"/>
        
        <!--显示的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题  -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>    
    <!-- 3、typeAliases:别名处理器:可以为我们的java类型起别名 别名不区分大小写-->
    <typeAliases>
        <!-- 1、typeAlias:为某个java类型起别名  type:指定要起别名的类型全类名;默认别名就是类名小写;employee  alias:指定新的别名-->
        <!-- <typeAlias type="com.atguigu.mybatis.bean.Employee" alias="emp"/> -->
        
        <!-- 2、package:为某个包下的所有类批量起别名 
                name:指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名(类名小写),)
        -->
        <package name="com.atguigu.mybatis.bean"/>
        
        <!-- 3、批量起别名的情况下,使用@Alias注解为某个类型指定新的别名 -->
    </typeAliases>         
    <environments default="dev_mysql">
        <environment id="dev_mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}" />
                <property name="url" value="${jdbc.url}" />
                <property name="username" value="${jdbc.username}" />
                <property name="password" value="${jdbc.password}" />
            </dataSource>
        </environment>
    
        <environment id="dev_oracle">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${orcl.driver}" />
                <property name="url" value="${orcl.url}" />
                <property name="username" value="${orcl.username}" />
                <property name="password" value="${orcl.password}" />
            </dataSource>
        </environment>
    </environments>
    
    
    <!-- 5、databaseIdProvider:支持多数据库厂商的;
         type="DB_VENDOR":VendorDatabaseIdProvider
             作用就是得到数据库厂商的标识(驱动getDatabaseProductName()),mybatis就能根据数据库厂商标识来执行不同的sql;
             MySQL,Oracle,SQL Server,xxxx
      -->
    <databaseIdProvider type="DB_VENDOR">
        <!-- 为不同的数据库厂商起别名 -->
        <property name="MySQL" value="mysql"/>
        <property name="Oracle" value="oracle"/>
        <property name="SQL Server" value="sqlserver"/>
    </databaseIdProvider>
    
    
    <!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
    <!-- 6、mappers:将sql映射注册到全局配置中 -->
    <mappers>
        <!-- 
            mapper:注册一个sql映射 
                注册配置文件
                resource:引用类路径下的sql映射文件
                    mybatis/mapper/EmployeeMapper.xml
                url:引用网路路径或者磁盘路径下的sql映射文件
                    file:///var/mappers/AuthorMapper.xml
                    
                注册接口
                class:引用(注册)接口,
                    1、有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
                    2、没有sql映射文件,所有的sql都是利用注解写在接口上;
                    推荐:
                        比较重要的,复杂的Dao接口我们来写sql映射文件
                        不重要,简单的Dao接口为了开发快速可以使用注解;
        -->
        <!-- <mapper resource="mybatis/mapper/EmployeeMapper.xml"/> -->
        <!-- <mapper class="com.atguigu.mybatis.dao.EmployeeMapperAnnotation"/> -->
        
        <!-- 批量注册: -->
        <package name="com.atguigu.mybatis.dao"/>
    </mappers>
</configuration>

三.mapper文件常用标签
Xml 映射文件中,除了常见的 select|insert|updae|delete 标签之外,还有哪些标签? 
答:还有很多其他的标签,<resultMap>、<parameterMap>、<sql>、<include>、 <selectKey>,<collection>  <association> <resulttype>
加上动态 sql 的 9 个标签, trim|where|set|foreach|if|choose|when|otherwise|bind 等,其中<sql>为 sql 片段标签,
通 过<include>标签引入 sql 片段,
<selectKey>为不支持自增的主键生成策略标签,select 字标签。

resultType resultMap区别
resultType   可以接收基本类型,包装类型的结果集映射。但是当是包装类型的时候就有要求了,必须包装类型中的属性值跟查询结果的字段对应的上,否则的话对应不上的属性是接收不到查询结果的
resultMap  需要首先在xml中定义一个  <resultMap></resultMap>节点

association 和collection 区别
association是用于一对一和多对一,而collection是用于一对多的关系

<mapper namespace="com.peny.mapper.ActivityMapper">
  <resultMap id="BaseResultMap" type="com.peny.po.Activity">
    <!--
      WARNING - @mbggenerated
      This element is automatically generated by MyBatis Generator, do not modify.
    -->
    <result column="activityId" jdbcType="INTEGER" property="activityid" />
    <result column="activityName" jdbcType="VARCHAR" property="activityname" />
    <result column="activityDes" jdbcType="VARCHAR" property="activitydes" />
    <result column="discount" jdbcType="REAL" property="discount" />
    <result column="fullPrice" jdbcType="INTEGER" property="fullprice" />
    <result column="reducePrice" jdbcType="INTEGER" property="reduceprice" />
    <result column="fullNum" jdbcType="INTEGER" property="fullnum" />
    <result column="reduceNum" jdbcType="INTEGER" property="reducenum" />
  </resultMap>

<select id="countByExample" parameterType="com.peny.po.ActivityExample" resultType="java.lang.Integer">
    <!--
      WARNING - @mbggenerated
      This element is automatically generated by MyBatis Generator, do not modify.
    -->
    select count(*) from activity
    <if test="_parameter != null">
      <include refid="Example_Where_Clause" />
    </if>
  </select>
</mapper>

四。插件类型
ParameterHandler、参数映射,参数类型解析
ResultSetHandler、结果封装
StatementHandler、 sql预编译
Executor :SimpleExecutor、ReuseExecutor、 BatchExecutor 执行器
这 4 种接口的插

五.
 namespace+id 是作为 Map<String, MappedStatement>的 key 使用的

六.参数传递

七.
一级缓存演示&失效情况
 同一次会话期间只要查询过的数据都会保存在当 前SqlSession的一个Map中 ? key:hashCode+查询的SqlId+编写的sql查询语句+参数
? 一级缓存失效的四种情况 
– 1、不同的SqlSession对应不同的一级缓存
– 2、同一个SqlSession但是查询条件不同 
– 3、同一个SqlSession两次查询期间执行了任何一次增 删改操作 
– 4、同一个SqlSession两次查询期间手动清空了缓

二级缓存
? 二级缓存(second level cache),全局作用域缓存 
? 二级缓存默认不开启,需要手动配置 
? MyBatis提供二级缓存的接口以及实现,缓存实现要求 POJO实现Serializable接口 
? 二级缓存在 SqlSession 关闭或提交之后才会生效
? 使用步骤 
– 1、全局配置文件中开启二级缓存 ? <setting name= "cacheEnabled" value="true"/> 
– 2、需要使用二级缓存的映射文件处使用cache配置缓存 ? <cache /> <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
– 3、注意:POJO需要实现Serializable接口
缓存有关设置
? 1、全局setting的cacheEnable: – 配置二级缓存的开关。一级缓存一直是打开的。 
? 2、select标签的useCache属性: – 配置这个select是否使用二级缓存。一级缓存一直是使用的 
? 3、sql标签的flushCache属性: – 增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。 查询默认flushCache=false。
? 4、sqlSession.clearCache(): – 只是用来清除一级缓存。 
? 5、当在某一个作用域 (一级缓存Session/二级缓存 Namespaces) 进行了 C/U/D 操作后,默认该作用域下所 有 select 中的缓存将被clear。

当执行一条查询SQL时,流程为 从二级缓存中进行查询 进入一级缓存中查询 执行 JDBC 查询

八.mybatis执行过程
1.根据全局配置文件创建sqlsessionfactory
  1)XMLConfigBuilder(SAX解析)解析xml,设置Configuration 属性(mybatis的全局变量),每个mapper.xml将每一个标签封装成一个MappedStatment
    每一个MappedStatmen就代表一个增删改查,全局configuration设置了一个mapperRegistry ,里面有个hashmap类型的knownmapper,kownmaper(key=接口名,value=MapperProxyFactory)
    MappedStatement statement = statementBuilder.build();
    configuration.addMappedStatement(statement);
  2)最后返回DefaultSqlSessionfactory
2.创建sqlSession
    1)根据configuration的enviroment信息创建事务
    2)final Executor executor = configuration.newExecutor(tx, execType); 创建执行器,默认是simpleExecutor
    3)开启二级缓存CachingExecutor,委派模式(Delegate) ,可以先去查缓存
    4)executor = (Executor) interceptorChain.pluginAll(executor);
    5)返回DefaultSqlSession(保含configuration和excutor)
3)创建mapperProxy(jdk动态代理)里面有sqlsession
   1)MapperProxyFactory 之前设置的,通过他创建MapperProxy对象(InvocationHandler)
   2)public class MapperProxy<T> implements InvocationHandler, Serializable
   3)(T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
4)查询实现
   1)调用方法,执行的是这个mapperproxy的invoke方法
   
   2)method包装成mapperMethod,执行execute, 会根据SqlCommand类型(增删改查)进行相应处理
   3)查询条件参数有多个会封装成一个mappeer,一个参数直接返回
   4)调用defaultsqlsession  result = sqlSession.selectOne(command.getName(), param);
   5)List<T> list = this.<T>selectList(statement, parameter);
   6)从全局变量configuraton获取mapperstatment,执行execute方法
   7)从mapperstatment获取BoundSql(有sql语句,参数,参数映射关系)
   8)创建CacheKey,executor开始执行查询   
   9)从localCache查询 查不到到数据库查
   10)list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); 
    localCache.putObject(key, list);
   11) StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
   创建statementHandler(preparedstatementHandler)
   12)statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
   13)预编译sql ,
   创建parameterHandler 设置参数(通过typehandler来设置)  
   同时创建preparedstatementHandler sql预处理
   同时会创建resultSetHandler和,创建parameterHandler
   14) resultSetHandler.<E> handleResultSets(ps);
九。插件自定义 拦截器
具体要拦截哪个对象
public @interface Signature {
  Class<?> type(); //拦截对象

  String method(); //拦截对象的方法

  Class<?>[] args();//方法的参数
}
自定义枚举类型
1、实现TypeHandler接口。或者继承BaseTypeHandler ,实现
com.atguigu.mybatis.typehandler.MyEnumEmpStatusTypeHandler.setParameter(PreparedStatement, int, EmpStatus, JdbcType) 
com.atguigu.mybatis.typehandler.MyEnumEmpStatusTypeHandler.getResult(ResultSet, String)
2.自定义statementHandler
/**
 * 完成插件签名:
 *        告诉MyBatis当前插件用来拦截哪个对象的哪个方法
 */
@Intercepts(
        {
            @Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
        })
插件原理
     * 在四大对象创建的时候
     * 1、每个创建出来的对象不是直接返回的,而是
     *         interceptorChain.pluginAll(parameterHandler);
     * 2、获取到所有的Interceptor(拦截器)(插件需要实现的接口);
     *         调用interceptor.plugin(target);返回target包装后的对象
     * 3、插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP(面向切面)
     *         我们的插件可以为四大对象创建出代理对象;
     *         代理对象就可以拦截到四大对象的每一个执行;
     * 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值