MyBatis解析

看到这篇文章,你一定和我刚开始接触MyBatis一样很好奇它到底是如何实现了对数据库的CRUD。接下来我们一点一点的分析

废话不多说,直接开始:

先放一个工程以便分析

对应表的实体类

public class User {
    private String name;
    private Integer age;
    private String gender;
    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

Dao层接口

public interface UserDao {
    List<User> findAll ();
}

 MyBatis的配置文件

<?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="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///test?serverTimezone=CTT"/>
                <property name="username" value="root"/>
                <property name="password" value="1229"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="UserDao.xml"/>
    </mappers>
</configuration>

接口的映射

<?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="UserDao">
    <select id="findAll" resultType="User">
        select * from user
    </select>
</mapper>

测试方法

    @Test
    public void testFindAll2 () throws IOException {
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        InputStream is = Resources.getResourceAsStream("mybatis_config.xml");
        SqlSessionFactory factory = builder.build(is);
        SqlSession sqlSession = factory.openSession();
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }
    }

在测试方法中,最重要的就是sqlSession对象,是它创建了UserDao接口的实现类对象。

先看看它是怎么来的

需要sqlSession对象,就先需要SquSessionFactory的对象factory;需要factory,就先需要SqlSessionFactoryBuilder的对象builder。

其中,builder创建factory用到了建造者模式,factory创建sqlSession用到了工厂模式,这里不细说。

builder的build()方法接收一个InputStream流,把MyBatis的配置文件传递进去,创建了factory

Ctrl+左键进入这个方法,看看源码

不管怎么样,最终都会调用接收一个Configuration类型参数的build()方法

其中Configuration类型参数是由XMLConfigBuilder类型的parse()方法得到的,Ctrl+左键看看XMLConfigBuilder的构造方法干了什么

最终调用了

private XMLConfigBuilder(XPathParser parser, String environment, Properties props)

这个构造方法中调用父类的构造方法创建的一个Configuration类对象

目前这个Configuration对象还什么都没有

其中XPathParser对象parser接收了之前的配置文件

那么parser.parse()又做了什么

它最终返回了configuration,这个configuration就是刚刚在构造方法中创建的Configuration对象。

在返回前,调用了

private void parseConfiguration(XNode root)

 看到了吗,这里面全都是标签语句,也就是说这个parseConfiguration(XNode root)方法把刚刚那个新创建的configuration填充了,用到的就是我们之前写好的MyBatis配置文件。

再回到最开始的SqlSessionFactoryBuilder类的那个最终调用的build()方法

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

它返回了一个DefaultSqlSessionFactory对象,传进了带有我们写好的配置文件信息的config,这样我们就得到了SqlSessionFactory对象,接下来就需要利用这个SqlSessionFactory对象的openSession()方法来

 很明显,这个类是SqlSessionFactory接口的实现类

这个类中有很多很多openSession()方法的重载 ,不过最终都指向了

这个方法又返回了一个DefaultSqlSession类对象,显然这是一个SqlSession接口的实现类。

到目前为止,我们才刚刚得到SqlSession对象。

好,可以跳回去了,到我们的测试类

SqlSession对象调用了getMapper()方法,创建了UserDao接口的实现类对象

看看getMapper()做了什么

一层一层的进入方法,最终在MapperRegistry类中的getMapper()方法返回了一个mapperProxyFactory.newInstance(sqlSession)

这个东西就是动态代理了。

 好,动态代理简单的讲就是把原本的方法fun1()进行功能扩展,比如fun1()原本只能打印“hello”,那么就为他添加功能不但能打印“hello”,还能再将这个“hello”存放进文件中。具体实现的步骤就是:

利用Proxy类的newProxyInstance()方法,提供三个参数,第一个是需要进行扩展的方法所在类的类加载器,第二个是这个类实现的接口数组,第三个是一个InvocationHandler接口的实现类。

实现InvocationHandler接口需要实现invoke()方法,它包含三个参数,Object proxy,Method method,Object[] args,其中proxy是代理对象,method是原类的方法,args是原方法的参数数组。

利用反射机制,可以方便的使用method对象使用原方法。

比如:method.invoke (this, args),就可以实现fun1()中的打印“haha”功能,然后就可以在invoke()方法中写保存“haha”的逻辑了。

所以,这个mapperProxy就变得重要了,这是一个MapperProxy对象,并且它实现了InvocationHandler接口,所以它就是用于扩展我们定义的Dao层接口功能的类。

MyBatis把扩展功能都放进了mapperMethod.execute()中

终于!要到终点了,这里就是代理模式扩展的功能代码。

 

总结一下

我们先写好MyBatis的配置文件,其中包含了数据库连接的相关信息,以及Dao层接口的映射文件地址,此处记为mybatis_config.xml。

我们把mybatis_config.xml传递给SqlSessionFactoryBuilder类的build()方法,它解析了这个xml配置文件,提取出了关键信息,创建了SqlSessionFactory对象,然后SqlSessionFactory对象又通过openSqlSession()方法创建了SqlSession对象,打开源码,你会发现这个对象类似于传统JDBC中的PrepareStatement对象,有一些增删改查方法。

我们利用SqlSession对象来为Dao层接口创建一个代理对象,扩充了接口中方法的功能,实现了CRUD。

我个人认为,这些东西我们不必完全了解,因为这毕竟是一个开源框架,如果真的要深入了解,我觉得框架所蕴含的思想更为重要,那就是一劳永逸,把复用做到极致,尽可能的让同功能的代码只出现一次。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值