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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/ccityzh/article/details/71517490
    以前一直都是在公司的写好的框架模式中直接使用Mybatis,而且也甚是简单,不需要什么思考,只注重sql语句就好了。但是用着用着就对他的实现流程方式感到奇怪了,
明明看到的只是在Dao层写了一个接口,在配置文件中写好自己的sql,就可以给人感觉,接口被自动实例化,然后在service层调用接口的实例,完成他从数据库取数据的过程。
在这种好奇的驱使下开启了对Mybatis的浅显阅读。其实之前一段时间看了些框架的代码,一直懒得总结,现在写一点内容供自己以后参考。
    下面以一个最简单的实例开启对Mybatis的理解,也是以最白话的形式供自己以后忘记的一干二净的时候参考。
 
这是一个本文阅读代码引子的一个project概览,就以此工程为入口,相信有兴趣研究Mybatis的人,对Mybatis都已经有了一些使用经验,下面黏贴一些代码。
mybatis_config.xml
Mybatis启动时候读取的配置文件,最终将所有的配置文件会封装在一个叫Configuration的类里,供以后任何时候使用
 

 
 
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
  3. <configuration>
  4. <environments default="development">
  5. <environment id="development">
  6. <transactionManager type="JDBC" />
  7. <dataSource type="POOLED">
  8. <property name="driver" value="com.mysql.jdbc.Driver" />
  9. <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
  10. <property name="username" value="root" />
  11. <property name="password" value="123456" />
  12. </dataSource>
  13. </environment>
  14. </environments>
  15. <mappers>
  16. <mapper resource="ball.xml"/>
  17. </mappers>
  18. </configuration>


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

 
 
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

 
 
  1. <!-- namespace必须与接口对应,id必须是自己的方法名-->
  2. <mapper namespace="IPlayDao">
  3. <select id="selectBall" parameterType="string" resultType="Ball">
  4. select id, name from ball where id = #{id}
  5. </select>
  6. </mapper>


IPlayDao.java
 

 
 
  1. public interface IPlayDao {
  2. public Ball selectBall(String id);
  3. }


Ball.java
省略getter,setter方法
 

 
 
  1. public class Ball {
  2. private String id;
  3. private String name;
  4. }


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

 
 
  1. import java.io.InputStream;
  2. import org.apache.ibatis.io.Resources;
  3. import org.apache.ibatis.session.SqlSession;
  4. import org.apache.ibatis.session.SqlSessionFactory;
  5. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. public class Demo {
  9. private static Logger LOG = LoggerFactory.getLogger(Demo.class);
  10. public static void main(String[] args) {
  11. SqlSession sqlSession = null;
  12. try {
  13. sqlSession = MyBatisUtil.getSqlSessionFactory().openSession();
  14. IPlayDao play = sqlSession.getMapper(IPlayDao.class);
  15. Ball ball = play.selectBall( "1");
  16. LOG.info( "查询结果: {}====>{}", ball.getId(), ball.getName());
  17. } finally {
  18. sqlSession.close();
  19. }
  20. }
  21. static class MyBatisUtil {
  22. private static SqlSessionFactory sqlSessionFactory = null;
  23. public static SqlSessionFactory getSqlSessionFactory() {
  24. InputStream inputStream = null;
  25. if (sqlSessionFactory == null) {
  26. try {
  27. String resource = "mybatis_config.xml";
  28. inputStream = Resources.getResourceAsStream(resource);
  29. sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  30. return sqlSessionFactory;
  31. } catch (Exception ex) {
  32. System.err.println(ex.getMessage());
  33. ex.printStackTrace();
  34. }
  35. }
  36. return sqlSessionFactory;
  37. }
  38. }
  39. }

为了方便将获取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
                                                          |
                                                          |
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
                                  (2)接口执行流程
                          
为什么我们要使用通用DAO接口呢,因为我们的数据库操作无非是增删改查,CRUD操作,我们不需要为每个实体去编写一个dao接口,对于相似的实体操作可以只编写一个通用接口,然后采用不同的实现! DAO已经成为持久层的标准模式,DAO使结构清晰,面向接口编程为代码提供了规范。而泛型DAO是一个类型安全的,代码精简的设计模式(相对于传统DAO),尤其在DAO组件数量庞大的时候,代码量的减少更加明显。 泛型DAO的核心是定义一个GenericDao接口,声明基本的CRUD操作: 用hibernate作为持久化解决方案的GenericHibernateDao实现类,被定义为抽象类,它提取了CRUD操作,这就是简化代码的关键,以便于更好的重用,这个就不给例子了,增删改都好写,查就需要各种条件了。 然后是各个领域对象的dao接口,这些dao接口都继承GenericDao接口,这样各个领域对象的dao接口就和传统dao接口具有一样的功能了。 下一步是实现类了,个自领域对象去实现各自的接口,还要集成上面的抽象类,这样就实现了代码复用的最大化,实现类只需要写出额外的查询操作就可以了。当然还要获得域对象的Class实例,这就要在构造方法传入Class实例。用spring提供的HibernateTemplate注入到GenericHibernateDao,这样在各个实现类就可以直接调用HibernateTemplate来实现额外的查询操作了。 如果在实现类不想调用某个方法(例如:update()),就可以覆盖它,方法抛出UnsupportedOperationException()异常。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值