专栏首页波波烤鸭mybatis教程之原理剖析

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方法

回到此处

进入update方法

注意进入的是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代理实现的代码。结束~~~

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • mybatis教程1(基本使用)

      MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结...

    用户4919348
  • Redis哨兵模式详解

      在主从模式的Redis系统中,从数据库在整个系统中起到了数据冗余备份和读写分离的作用,但是当数据库遇到异常中断服务后,我们只能通过手动的方式选择一个从...

    用户4919348
  • SpringBoot【SpringMVC+mybatis完成CRUD案例】

      在main/src/resources目录下创建application.properties文件

    用户4919348
  • Java数据结构和算法总结-数组、二分查找

    前言:在平时开发中数组几乎是最基本也是最常用的数据类型,相比链表、二叉树等又简单很多,所以在学习数据和算法时用数组来作为一个起点再合适不过了。本篇博文的所有代...

    codingblock
  • 新经资讯项目业务逻辑梳理

    __init__.py 项目应用初始化文件--应用程序实例、数据库实例、注册蓝图、日志等

    skylark
  • 分享大厂分布式唯一ID设计方案,为何搞的这么复杂?

    很多人看了分布式唯一 ID 相关的文章,觉得都设计的非常复杂,大厂的分布式唯一ID生成方案为什么要设计的这么复杂?看完本篇文章,希望能够给你解惑!

    业余草
  • left/right join中on和where的区别

    开发同学提了个问题,如下两种left join中on和where条件的写法是否等价?

    bisal
  • 一起出发 为爱徒色-品牌设计总结

    腾讯ISUX
  • Vue响应式原理

    Vue是数据驱动视图实现双向绑定的一种前端框架,采用的是非入侵性的响应式系统,不需要采用新的语法(扩展语法或者新的数据结构)实现对象(model)和视图(vie...

    伯爵
  • Easy单例模式

    在学习单例模式前,不妨问自己几个问题:单例模式是怎么来的,单例模式怎么去用? 单例模式是怎么来的? 这就从设计模式起源开始,他是在实际实践中遇到类似情况可以通用...

    用户1148881

扫码关注云+社区

领取腾讯云代金券