您的位置:首页 > 其它

Mybatis执行dao接口方法的流程梳理及源码分析

2017-06-10 07:31 761 查看
以前一直都是在公司的写好的框架模式中直接使用Mybatis,而且也甚是简单,不需要什么思考,只注重sql语句就好了。但是用着用着就对他的实现流程方式感到奇怪了,

明明看到的只是在Dao层写了一个接口,在配置文件中写好自己的sql,就可以给人感觉,接口被自动实例化,然后在service层调用接口的实例,完成他从数据库取数据的过程。

在这种好奇的驱使下开启了对Mybatis的浅显阅读。其实之前一段时间看了些框架的代码,一直懒得总结,现在写一点内容供自己以后参考。

下面以一个最简单的实例开启对Mybatis的理解,也是以最白话的形式供自己以后忘记的一干二净的时候参考。


这是一个本文阅读代码引子的一个project概览,就以此工程为入口,相信有兴趣研究Mybatis的人,对Mybatis都已经有了一些使用经验,下面黏贴一些代码。

mybatis_config.xml

Mybatis启动时候读取的配置文件,最终将所有的配置文件会封装在一个叫Configuration的类里,供以后任何时候使用

<?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>
<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://localhost:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="ball.xml"/>
</mappers>
</configuration>


ball.xml

接口中的方法数据库存取数据时候所调用的sql语句,每个接口方法都封装在一个MappedStatement中

<?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">
<!-- namespace必须与接口对应,id必须是自己的方法名-->
<mapper namespace="IPlayDao">
<select id="selectBall" parameterType="string" resultType="Ball">
select id, name from ball where id = #{id}
</select>
</mapper>


IPlayDao.java

public interface IPlayDao {
public Ball selectBall(String id);
}


Ball.java

省略getter,setter方法

public class Ball {
private String id;
private String name;
}


Demo.java

主函数类,mybatis的启动也是从这里开始分析

import java.io.InputStream;

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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Demo {
private static Logger LOG = LoggerFactory.getLogger(Demo.class);
public static void main(String[] args) {
SqlSession sqlSession = null;
try {
sqlSession = MyBatisUtil.getSqlSessionFactory().openSession();
IPlayDao play = sqlSession.getMapper(IPlayDao.class);
Ball ball = play.selectBall("1");
LOG.info("查询结果: {}====>{}", ball.getId(), ball.getName());
} finally {
sqlSession.close();
}
}
static class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory = null;

public static SqlSessionFactory getSqlSessionFactory() {
InputStream inputStream = null;
if (sqlSessionFactory == null) {
try {
String resource = "mybatis_config.xml";
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
} catch (Exception ex) {
System.err.println(ex.getMessage());
ex.printStackTrace();
}
}
return sqlSessionFactory;
}
}
}

为了方便将获取sqlSessionFactory的过程写为了一个静态内部类,把所有关键的代码都累积在这一个类中了。
大体上分析一下这个流程:

(1)首先获取解析配置文件mybatis_config

(2) Mybatis用配置文件最终构建出一个sqlSessionFactory对象

(3)从sqlSessionFactory中打开了mybatis的执行流程大门,取得sqlSession对象

(4)调用getMapper方法,将接口传入框架内部,实现被代理过程

(5)调用接口方法, 实际上在Mybatsis内部走了一圈代理流程,最终取得结果

下面开启到Mybatis内部游离,MyBatis的整个流程包含两个过程,一个是配置文件读取封装为Configuration的过程,一个是执行一个接口方法实现整个代理的过程。

其实所有框架都是这样一个过程,首先根据配置文件将整个框架初始化,然后再实现整个流程的相互配合完成接口执行流程工作。

看起来这样一直写下去文章太长了,先把总结的整个流程写下来,下面分两篇分别介绍Configuration,和代理执行过程,主要是四大对象Executor,StatementHandler,

ParmeterHandler,ResultHandler,靠sqlSession将整个配合流程完美组装在了一起。

1.getMapper()的过程中对dao接口实现了动态代理

2.接口调用其内部的方法的时候,转到了MapperProxy类的invoke方法中
3.MapperProxy类中将方法包装成MapperMethod,然后调用其MapperMethod的execute方法(其中将sqlsession作为参数传过去,为命令模式)
4.sqlsession中包含有executor执行器,sqlsession调用的方法对应内部使用executor来调用
5.具体方法中会初始化StatementHandler
6.StatementHandler中又会初始化ParameterHandler和ResultHandler
7.StatementHandler初始化完成以后,作为参数传入prepareStatement方法中,创建真正的JDBC的Statement对象
               JDBC  |-----------------1.DriverManage  ---------------------------->Connection
                            |                                                    |createStatement() ---------Statement
                            |  ----------------2.Connection    |
                            |                                                    |prepareStatement(sql)----Prea
                            |                             | 
                            |                             |                                  
                            |                             | Statement
                            |                             | 
                            |------------------3.  |
                                                          | 
                                                          |   
                                                          |PrepareStatement
                                    
4000
                     |
                                                          |
8.prepareStatement中会用statementHandler创建Statement/PrepareStatementHandler准备参数,获取结果
9.  prepare()获取Statement==》因为Statement和prepareStatement传入sql语句的时间不同,所以内部具体使用instantiateStatement创建
10.paramerize()设置参数(Statement该函数为空,主要为prepareStatement准备)
11.StatementHandler调用具体的方法(上述mappermethod具体 执行的增删改查)
12.Statement取得sql,然后执行Statement的execute方法,然后将Statement传入到ResultHandler的query()(或者其他方法),   ResultHandler该方法内部再调用Statement         的getResutSet结果集封装。
13.PrepareStatement在9中已经传入sql,10中设置了参数,所以只需要执行execute方法 ,类似封装结构。
详细源码分析:(1)解析配置文件封装configuration
 解析配置文件封装为Configuration
                                  (2)接口执行流程
接口执行流程

                          
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: