mybatis教程之原理剖析
MyBatis是目前非常流行的ORM框架,功能很强大,然而其实现却比较简单、优雅。本文通过代理的方式来看下其实现
方式一:传统API方式
@Test
public void add() throws IOException {
	// 1.通过Resources对象加载配置文件
	InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
	// 2.获取SqlSessionFactory对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream );
	// 3.通过SqlSessionFactory对象获取SQLSession对象
	SqlSession session = factory.openSession();
	User user = new User();
	user.setName("dpb");
	user.setAge(22);
	// dpb.addUser  是映射文件中 namespace的内容加 id的内容,定位要执行的SQL
	int count = session.insert("dpb.addUser", user);
	System.out.println("影响的行数:"+count);
	// 需要显示的提交
	session.commit();
	session.close();
}
1.怎么加载配置文件的
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
进入getResourceAsStream方法




小结:
Resources.getResourceAsStream("mybatis-config.xml");这行代码其实很简单,就是通过类加载器加载我们的配置文件,获取到一个InputSream。
扩展知识
getResourceAsStream方法的使用:
1.Class.getResourceAsStream(String path) : path 不以’/'开头时默认是从此类所在的包下取资源,以’/'开头则是从ClassPath根下获取。其只是通过path构造一个绝对路径,最终还是由ClassLoader获取资源
2.Class.getClassLoader.getResourceAsStream(String path) :默认则是从ClassPath根下获取,path不能以’/'开头,最终是由ClassLoader获取资源。
2.怎么获取SqlSessionFactory对象的
// 2.获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder()
										.build(inputStream );
源码分析






小结:
通过SqlSessionFactoryBuilder的builder方法获取SqlSessionFactory对象,实际是获取的是DefaultSqlSessionFactory对象,且同时解析了配置文件,并将信息封装到了Configuration对象中。加载配置文件的方式在项目中肯定只需要加载一次。所以在整个项目生命周期中SqlSessionFactory的实例只需要一个,所以此处可以将SqlSessionFactory设计为单例模式。
3.获取SqlSession对象
SqlSession session = factory.openSession();



| 处理器 | 说明 | 
|---|---|
| SimpleExecutor | 就是普通的执行器 | 
| ReuseExecutor | 执行器会重用预处理语句(prepared statements) | 
| BatchExecutor | 批量执行器 | 

同时创建了Transaction对象,该对象有数据库连接对象Connection

创建执行器的过程

小结:
通过openSession()方法获取SqlSession对象,我们获取到了一个DefaultSqlSession实例,不会自动提交事务。实例化了一个执行器,如果我们没有专门指定执行器的类型,那么默认的执行器是SimpleExecutor。且获取了Transaction对象。
4.insert方法执行的过程
int i = session.insert("aaa.addUser", user);
<?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="aaa">
	<insert id="addUser" parameterType="com.sxt.bean.User">
		insert into t_user(name,age)values(#{name},#{age})
	</insert>
</mapper>
代码跟踪:
 添加数据进入DefaultSqlSession方法后调用的还是update方法
添加数据进入DefaultSqlSession方法后调用的还是update方法










回到此处
 进入update方法 ==
进入update方法 ==
 注意进入的是PreparedStatementHandler==
注意进入的是PreparedStatementHandler==
怎么会进入PreparedStatementHandler的?不是SimpleExecutor处理器,应该进入SimpleStatementHandler吗?
 参数在哪动态绑定的呢?
参数在哪动态绑定的呢?





 按照相同的方式可以跟踪下查询的方法,代码就在此不贴出来了。
按照相同的方式可以跟踪下查询的方法,代码就在此不贴出来了。
| 对象 | 作用 | 
|---|---|
| SqlSessionFactory | 顶层API,提供SQLSession对象,获取的同时加载配置文件 | 
| configuration | 封装的有全局配置文件和各个映射文件的相关信息 | 
| SqlSession | 顶层API,和数据库交互完成增删改查操作 | 
| MappedStatement | 封装了一条insert|update|delete|select节点信息。 | 
| Executor | 执行器,调度核心,SimpleExecutor,ReuseExecutor,BatchExecutor. | 
| StatementHandler | 封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合 | 
| BoundSql | 封装的有动态SQL及对应的参数信息。 | 
| TypeHandler | 类型处理器,java类型和数据库字段类型的转换 | 
| ResultSetHandler | 负责将jdbc的ResultSet的结果和java中List的数据相互转换 | 
方式二:基于Mapper接口方式
@Test
public void add() throws IOException {
	// 1.通过Resources对象加载配置文件
	InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
	// 2.获取SqlSessionFactory对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream );
	// 3.通过SqlSessionFactory对象获取SQLSession对象
	SqlSession session = factory.openSession();
	User user = new User();
	user.setName("dpb");
	user.setAge(22);
	//通过Java动态代理自动提供了UserMapper的实现类
	UserMapper mapper = session.getMapper(UserMapper.class);
	int count = mapper.addUser(user);
	System.out.println("影响的行数:"+count);
	session.commit();
}
该方式本质上是通过jdk动态代理实现的。
在看源码之前我们自己来简单的实现下看看。
通过jdk动态代理简单实现
bean对象
	private int id;
	private String name;
	private int age;
接口文件
public interface UserMapper {
	public int addUser(User user);
	public int updateById(User user);
	public int deleteById(int id);
	public User queryById(int id);
}
接口实现类
public class UserDao implements UserMapper {
	@Override
	public int addUser(User user) {
		return DBUtils.getInstall().openSession().insert("com.sxt.dao.UserMapper.addUser", user);
	}
	@Override
	public int updateById(User user) {
		// TODO Auto-generated method stub
		return DBUtils.getInstall().openSession().update("com.sxt.dao.UserMapper.updateById", user);
	}
	@Override
	public int deleteById(int id) {
		// TODO Auto-generated method stub
		return DBUtils.getInstall().openSession().delete("com.sxt.dao.UserMapper.deleteById", id);
	}
	@Override
	public User queryById(int id) {
		// TODO Auto-generated method stub
		return DBUtils.getInstall().openSession().selectOne("com.sxt.dao.UserMapper.queryById", id);
	}
}
映射文件
注意:namespace和接口全路径名称相同,id和接口中的方法名相同
<?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="com.sxt.dao.UserMapper">
	<insert id="addUser" parameterType="com.sxt.bean.User">
		insert into t_user(name,age)values(#{name},#{age})
	</insert>
	<delete id="deleteById" parameterType="java.lang.Integer">
		delete from t_user where id=#{id}
	</delete>
	<update id="updateById" parameterType="com.sxt.bean.User">
		update t_user
		set name=#{name},age=#{age}
		where id=#{id}
	</update>
	<select id="queryById" parameterType="java.lang.Integer"
		resultType="com.sxt.bean.User">
		select * from t_user where id=#{id}
	</select>
</mapper>
目录结构

到此我们发现接口实现UserDao,其实就是个模板,没有特定的内容,这时我们可以将其删掉通过jdk代理的方式实现
测试文件
/**
 * 代理方式
 */
@Test
public void test(){
	UserMapper mapper = (UserMapper) Proxy.newProxyInstance(UserMapper.class.getClassLoader()
			, new Class[]{UserMapper.class},new InvocationHandler() {
				@Override
				public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
					System.out.println(UserMapper.class.getName()+"."+method.getName());
					Object id = null;
					for (Object object : args) {
						System.out.println(object);
						id = object;
					}
					// 实现逻辑
					return DBUtils.getInstall().openSession().selectOne(UserMapper.class.getName()+"."+method.getName(), id);
				}
			} );
	System.out.println(mapper.queryById(5));
}
 测试成功
测试成功
源码跟踪



 源码中看到jdk代理实现的代码。结束~~~
源码中看到jdk代理实现的代码。结束~~~
mybatis教程之原理剖析的更多相关文章
- iPhone/Mac Objective-C内存管理教程和原理剖析
		http://www.cocoachina.com/bbs/read.php?tid-15963.html 版权声明 此文版权归作者Vince Yuan (vince.yuan#gmail.com)所 ... 
- Objective-C内存管理教程和原理剖析(四)
		初学Objective-C的朋友都有一个困惑,总觉得对Objective-C的内存管理机制琢磨不透,程 序经常内存泄漏或莫名其妙的崩溃.我在这里总结了自己对Objective-C内存管理机制的研究成果 ... 
- Objective-C内存管理教程和原理剖析(三)
		初学Objective-C的朋友都有一个困惑,总觉得对Objective-C的内存管理机制琢磨不透,程 序经常内存泄漏或莫名其妙的崩溃.我在这里总结了自己对Objective-C内存管理机制的研究成果 ... 
- 【MyBatis】MyBatis Tomcat JNDI原理及源码分析
		一. Tomcat JNDI JNDI(java nameing and drectory interface),是一组在Java应用中访问命名和服务的API,所谓命名服务,即将对象和名称联系起来,使 ... 
- java的加载与执行原理剖析
		到目前为止,我们接触过的重点术语,总结一下: Java体系的技术被划分为三大块: JavaSE:标准版 JavaEE:企业版 JavaME:微型版 安装JDK之后: JDK:java开发工具箱 JRE ... 
- ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)
		ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Constructor(构造函数) 2.Configure ... 
- ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行
		ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行 核心框架 ASP.NET Core APP 创建与运行 总结 之前两篇文章简析.NET Core 以及与 .NET Framew ... 
- 【Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析】
		原文:[Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析] [注意:]团队里总是有人反映卸载Xamarin,清理不完全.之前写过如何完全卸载清理剩余的文件.今天写了Windows下的批命令 ... 
- 【Xamarin 跨平台机制原理剖析】
		原文:[Xamarin 跨平台机制原理剖析] [看了请推荐,推荐满100后,将发补丁地址] Xamarin项目从喊口号到现在,好几个年头了,在内地没有火起来,原因无非有三,1.授权费贵 2.贵 3.原 ... 
随机推荐
- ubuntu中运行java程序
			查找jdk rivsidn@rivsidn:~/demo/java$ sudo apt-cache search jdk default-jdk - Standard Java or Java com ... 
- jdk8 永久代变更
			java8 去掉了永久代permgen(又称非堆,其实也是堆的一部分),类的方法代码,常亮,方法名,静态变量等存放在永久代中 改为使用元空间 Metaspace , Metaspace 不在是堆的一部 ... 
- nodeJS实现一个在线填表应用
			1.构建一个web服务器 以前玩php和jsp时用过Apache.汤姆猫服务器,nodejs则有不同,他是需要自己createServer. //server.jsvar http = require ... 
- S 实现精确加减乘除
			//加法函数 function accAdd(arg1, arg2) { var r1, r2, m; try { r1 = arg1.toString().split(".")[ ... 
- Word中的段落
			Word文档中的块级内容的最基本单位是段落,段落用<p>元素进行存储.段落定义在新行中开始,段落可以包含三方面的信息:可选的段落属性.内嵌的内容(通常为文本)和用于比较两个文档的内容的一组 ... 
- 笔记:Activity的启动过程
			Activity的创建特点 作为四大组件之一的Activity,它不像普通java对像那样,可以new出来,然后去使用.而是调用 startActivity()这样的方式启动.那么Android系统是 ... 
- jzoj5929. 【NOIP2018模拟10.26】情书
			动态规划: #include<bits/stdc++.h> using namespace std; int n,iv[30]; #define mo 998244353 typedef ... 
- python tricks
			1. cities = ['Marseille', 'Amsterdam', 'New York', 'Londom'] # the good way for i, city in enumerate ... 
- jenkins 集成钉钉机器人通知
			公司使用钉钉做为公司内部的通讯工具,所以想通过Jenkins发布完成以后通过钉钉来通知大家,研究发现钉钉提供机器人,所以我把机器人集成进来通知相关人员. 1.创建通知人群组,添加机器人(钉钉默认自带了 ... 
- code128 C语言实现
			https://blog.csdn.net/walk_ing/article/details/52712641 参考链接 1,具有A.B.C三种不同的编码类型,可提供标准ASCII中128个字元(字元 ... 
