• 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();
    

    在这里插入图片描述
    默认的执行器是SIMPLE在这里插入图片描述

    处理器 说明
    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代理实现的代码。结束~~~

  • 相关阅读:
    工厂模式
    dubbo
    WebSocket WebService
    消息中间
    原型模式
    ApiPost Apifox
    Future 的使用与源码解析
    JUC 线程池的使用与源码解析
    ReentrantLock 源码解析
    CountDownLatch 的使用与源码解析
  • 原文地址:https://www.cnblogs.com/dengpengbo/p/10308134.html
Copyright © 2020-2023  润新知