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

    以前一直都是在公司的写好的框架模式中直接使用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
                                                          |
                                                          |
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)接口执行流程
                          
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为什么我们要使用通用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()异常。
Mybatis执行流程主要包括以下几个步骤: 1. 配置文件解析:Mybatis会读取配置文件,解析其中的配置信息,包括数据库连接信息、mapper文件路径、全局配置等。 2. SqlSessionFactory创建:SqlSessionFactory是Mybatis的核心,它负责创建SqlSession对象。在Mybatis中,SqlSessionFactory是线程安全的,因此可以被多个线程共享。 3. SqlSession创建:SqlSession是Mybatis的核心接口,它提供了执行SQL语句、获取Mapper接口等功能。SqlSession的创建需要通过SqlSessionFactory,可以通过SqlSessionFactory.openSession()方法获取SqlSession对象。 4. Mapper接口绑定:Mapper接口Mybatis的另一个核心概念,通过Mapper接口可以执行SQL语句。在Mybatis中,Mapper接口需要与对应的Mapper文件进行绑定,这样才能正确执行SQL语句。 5. SQL语句执行:通过SqlSession对象调用Mapper接口中的方法Mybatis会根据Mapper接口中的方法名和参数信息,动态生成SQL语句,并执行该语句。 6. 结果映射:Mybatis会根据Mapper接口方法的返回值型,将查询结果映射成Java对象,并返回给调用者。 7. 事务提交/回滚:如果在执行SQL语句的过程中发生异常,Mybatis会将事务回滚。如果没有异常,则会将事务提交。 以上就是Mybatis执行流程,总体来说,Mybatis执行流程比较简单,但是其中涉及到的细节比较多,需要开发者仔细理解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值