从源码的角度来解读spring整合mybatis

1、前言

spring整合mybatis几乎是每个Java开发学习过程中都会接触到的,相信很多人开始学习的时候都和博主一样,跟着视频或者博客把一大堆配置复制粘贴下来,然后运行发现没问题,于是就觉得自己掌握了。

但实际上真要细究spring和mybatis之间的关系时,大多数人都说不出来个所以然。

今天我们就从源码的角度来了解一下,从mybatis到mybatis-spring。

2、mybatis

作为一个半ORM框架,mybatis的流行度已经远超hibernate,毕竟hibernate的HQL学起来真是太伤神了。我们先从一个简单的例子来使用一下mybatis:

mybatis-config.xml

<?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>
    <properties resource="app.properties"/>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/springtrans"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper class="com.zhengfa.mybatis.dao.UserMapper"></mapper>
    </mappers>
</configuration>

main:

public static void main(String[] args) throws IOException {
    InputStream fileInputStream =  Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(fileInputStream);
    SqlSession sqlSession = factory.openSession(true);
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    User user = userMapper.selectByid(1);
    System.out.println(user);
    sqlSession.close();
}

service,mapper什么的这里就不贴了,小白请自行去看入门博客~

2.1 mybatis使用流程

从上面的xml配置文件到主方法使用mapper调用查询方法,我们主要做了以下几件事:

  1. 配置DataSource,也就是我们要连接的数据库
  2. 配置mapper位置,因为mybatis需要将mapper.xml与class联系起来
  3. 读取主配置文件mybatis-config.xml
  4. 通过SqlSessionFactoryBuilder构建SqlSessionFactory
  5. 通过SqlSessionFactory打开一个SqlSession
  6. 通过SqlSession获取Mapper实例

得到mapper实例对象之后,我们就可以进行相关操作了。

2.2 mybatis三个组件

从上述流程我们可以发现,使用mybatis涉及到了三个组件:

SqlSessionFactoryBuilder
用于构建会话工厂,基于config.xml构建会话工厂,构建完成后即可丢弃。

SqlSessionFactory
用于生成会话的工厂,作用于整个应用运行期间,一般不需要构造多个工厂对象

SqlSession
作用于单次会话,如WEB一次请求期间,不能用作于某个对象属性,也不能在多个线程间共享,因为它是线程不安全的。

2.3 SqlSession

这里单独把SqlSession列出来,是因为这个接口实在是太重要了。它为我们提供了大量操作数据库的接口。

而在Mybatis中,SqlSession的实现类是DefaultSqlSession

在实际应用中,我们可以通过openSession方法拿到session之后,像JDBC那样去操作数据库:

在这里插入图片描述

但是这也太笨重了,如果Mybatis就这的话,还不如使用JDBC。

2.4 Mapper接口式编程

Mybatis通过Mapper接口式编程的方式,巧妙的让SqlSession实现类消失了,取而代之的是通过mapper.xml与mapper接口通过反射建立映射生成代理类的方法,用面向对象的方法来编程。

在DefaultSqlSession中,通过getMapper方法,我们可以得到Mapper的代理类实例:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  if (mapperProxyFactory == null) {
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  }
  try {
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}


3、spring整合mybatis

我们都知道spring可以抽象为IOC容器,先不说具体流程,我们来思考一下,刚才mybatis的流程中,有哪些是可以交给spring管理的:

  1. 数据源DataSource
  2. SqlSessionFactory
  3. SqlSessionTemplate
  4. Mapper

我们通过引入org.mybatis.mybatis-spring包和spring之后,重新来配置一次简单的实例:

spring.xml:

<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/springtrans"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperFactoryBean" id="userFactoryBean">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
        <property name="mapperInterface" value="com.zhengfa.mybatis.dao.UserMapper"/>
    </bean>

</beans>

main:

public static void main(String[] args) {
    ClassPathXmlApplicationContext context
            = new ClassPathXmlApplicationContext("spring.xml");
    UserMapper userMapper = context.getBean(UserMapper.class);
    System.out.println(userMapper.selectByid(1));
}

可以看到,spring整合mybatis之后的代码变得清爽了很多。

但是这中间出现了一个我们在之前没见过的东西:SqlSessionTemplate,同时更加令人困惑的是,不仅在配置中没有看到它,就连使用时也没有它,那么它到底是干嘛的呢?

在了解它之前,我们先介绍一下其它的组件。

3.1 SqlSessionFactoryBean

在MyBatis中要构建SqlsessionFactory对象,让它来产生 Sqlsession。在Mybatis-Spring中也不例外。

在一开始的数据源配置肯定是必不可少的,DriverManagerDataSource是spring自带的数据源,当然我们也可以选择配置Druid数据源。

而我们之前的SqlSessionFactory也被SqlSessionFactoryBean所替换,这也很好理解,spring把mybatis的SqlSessionFactory作为一个bean加入到了容器中


3.3 SqlsessionTemplate

SqlsessionTemplate( org.mybatis.spring.SqlsessionTemplate)是一个模板类,它是SqlSession的一个实现类,通过Sqlsession来完成工作。

在Mybatis中,对应的就是DefaultSqlSession。而在MyBatis-Spring中,实现类就是我们的SqlsessionTemplate

它为我们提供了大量方法以及对SqlSession生命周期的操作,我们可以通过添加如下配置来使用它:

<bean class="org.mybatis.spring.SqlSessionTemplate" id="sessionTemplate">
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>


public static void main(String[] args) {
    ClassPathXmlApplicationContext context
            = new ClassPathXmlApplicationContext("spring.xml");
    SqlSessionTemplate sqlSessionTemplate = context.getBean(SqlSessionTemplate.class);
    sqlSessionTemplate.selectList("……");
}

这个使用过程和DefaultSqlSession的使用没有什么差别?直接使用SqlsessionTemplate显得极为笨重,我们一般不会在代码中使用。

但是我们仍然需要操作SqlSession,在Mybatis中通过DefaultSqlSession的getMapper方法完成了对代理类的实例化,但是Spring明显不会每次还要显示调用getMapper方法。于是添加了MapperFactoryBean组件。

3.3 MapperFactoryBean

熟悉Spring的同学就应该能了解FactoryBean和BeanFactory的区别,这里简要提一下:

一般情况下,Spring通过反射机制利用的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂。

如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。

Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。

从宏观来看,MapperFactoryBean接口的逻辑就是为我们完成了从mapper的接口类到mapper的代理类创建,然后加入到Spring容器中

3.4 配置mapperScan

假如我们需要第二个mapper时,我们就需要在xml中添加如下配置:

	<bean class="org.mybatis.spring.mapper.MapperFactoryBean" id="testFactoryBean">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
        <property name="mapperInterface" value="com.zhengfa.mybatis.dao.Test"/>
    </bean>

这从使用者的角度来说,简直就是噩梦。还好在mybatis.xml配置mapper时,就已经有包扫描的配置,我们只需要简单的进行如下配置即可:

<mybatis:scan base-package="com.zhengfa.mybatis.dao"/>

那么接下来,我先要问你几个问题:

  1. 假如指定的包中,存在大量接口类,mybatis会把它们全部扫描进去并创建代理类吗?
  2. 假如我创建了一个TestMapper接口,但是没有写对应的xml,会报错吗?如果会报错,是在什么时候报错呢?

这两个问题大家可以自行实践去查证,答案将在结尾给出。

4、mybatis与spring流程小结

我们再来做一次小结,对比一下mybatis与spring的差异:

mybatismybatis-spring
SqlSessionFactory通过SqlSessionFactoryBuilder读取配置文件构建通过SqlSessionFactoryBean的getObject方法获取
SqlSession实现类DefaultSqlSessionSqlSessionTemplate
Mapper实例DefaultSqlSession.getMapper方法通过MapperFactoryBean的getObject方法返回实例,实际上是调用SqlSessionTemplate.getMapper方法

其它宏观或者微观细节大致上没有差异,比如xml解析流程,比如映射器和核心类Configuration,这些我虽然没有去求证过,但是想来不会有多少出入。

不过有需要注意的点是,在Spring中,Mapper是单例对象,但是Mapper与SqlSession是绑定在一起的,那这样一来,岂不是SqlSession也是单例的?为什么我们在实际使用过程中,不会出现线程安全问题呢?关于这个问题,博主会另写一篇博客介绍~

最后回答一下之前的两个问题:

  1. 会扫描进去并创建代理类,从而产生大量的无用代理对象,因此我们可以通过配置MapperSacnnerConfigurer来优化
  2. 启动过程并不会报错,但是使用时会报错,有兴趣的读者可以去了解一下XML解析以及代理方法调用流程
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值