您的位置:首页 > 编程语言 > Java开发

Spring事务源码分析专题(二)Mybatis的使用及跟Spring整合原理分析

2020-08-10 23:19 1131 查看

前言

专题要点如下:

本文要解决的是第二点,Mybatis的使用、原理及跟Spring整合原理分析。

Mybatis的简单使用

搭建项目

  1. pom文件添加如下依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
  1. 创建mybaits配置文件,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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="password" value="123"/>
<property name="username" value="root"/>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/userMapper.xml"/>
</mappers>
</configuration>
  1. 创建mapper.xml文件如下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="org.apache.ibatis.dmz.mapper.UserMapper">
<select id="selectOne" resultType="org.apache.ibatis.dmz.entity.User">
select * from user where id = #{id}
</select>
</mapper>
  1. 实体类如下
public class User {

private  int id;

private String name;

private int age;

// 省略getter/setter方法

@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
  1. 测试代码如下
    public class Main {
    public static void main(String[] args) throws Exception {
    String resource = "mybatis-config.xml";
    InputStream resourceAsStream = Resources.getResourceAsStream(resource);
    // 1.解析XML配置
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    // 2.基于解析好的XML配置创建一个SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = builder.build(resourceAsStream);
    // 3.通过SqlSessionFactory,创建一个SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 4.测试直接调用mapper.xml中的方法
    Object o = sqlSession.selectOne("org.apache.ibatis.dmz.mapper.UserMapper.selectOne",2);
    if(o instanceof User){
    System.out.println("直接执行mapper文件中的sql查询结果:"+o);
    }
    // 5.获取一个代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    // 6.调用代理对象的方法
    System.out.println("代理对象查询结果:"+mapper.selectOne(1));
    }
    }

// 程序输出如下,分别对应了我本地数据库中的两条记录
// 直接执行mapper文件中的sql查询结果:User{id=2, name='dmz', age=18}
// 代理对象查询结果:User{id=1, name='dmz', age=18}

> **原理分析**

因为本专栏不是对mybatis的源码分析专题(笔者对于三大框架都会做一个源码分析专题),所以对这块的原理分析不会牵涉到过多源码级别的内容。

从上面的例子中我们可以看到,对于Mybatis的使用主要有两种形式

1. 直接通过sqlsession调用相关的增删改查的API,例如在我们上面的例子中就直接调用了sqlsession的selectOne方法完成了查询。使用这种方法我们需要传入namespace+statamentId以便于Mybatis定位到要执行的SQL,另外还需要传入查询的参数
1. 第二种形式,则是先通过sqlsession创建一个代理对象,然后调用代理对象的方法完成查询

本文要探究的原理主要是第二种形式的使用,换而言之,就是Mybatis是如何生成这个代理对象的。在思考Mybatis是如何做的之前,我们不妨想一想,如果是我们自己要实现这个功能,那么你会怎么去做呢?

如果是我的话,我会这么做:

![](https://s4.51cto.com/images/blog/202008/10/5d09bd2be5dcfffe5fb9ca6d0f34a152.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)

当然我这种做法省略了很多细节,比如如何将方法参数绑定到SQL,如何封装结果集,是否对同样的Sql进行缓存等等。正常Mybatis在执行Sql时起码需要经过下面几个流程

![](https://s4.51cto.com/images/blog/202008/10/6992f5b6c7bd628daad4ad18624dc491.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)
9

其中,Executor负责维护缓存以及事务的管理,它会将对数据库的相关操作委托给StatementHandler完成,StatementHandler会先通过ParameterHandler完成对Sql语句的参数的绑定,然后调用JDBC相关的API去执行Sql得到结果集,最后通过ResultHandler完成对结果集的封装。

本文只是对这个流程有个大致的了解即可,详细的流程介绍我们在Mybatis的源码分析专栏中再聊~

# Mybaits中的事务管理

Mybatis中的事务管理主要有两种方式

1. 使用JDBC的事务管理机制:即利用JDBC中的java.sql.Connection对象完成对事务的提交(commit())、回滚(rollback())、关闭(close())等

1. 使用MANAGED的事务管理机制:这种机制MyBatis自身不会去实现事务管理,而是让程序的容器如(tomcat,jboss)来实现对事务的管理

在文章开头的例子中,我在mybatis-config.xml配置了

<transactionManager type="JDBC"/>

这意味着我们选用了JDBC的事务管理机制,那么我们在哪里可以开启事务呢?实际上Mybatis默认是关闭自动提交的,也就是说事务默认就是开启的。而是否开启事务我们可以在创建SqlSession时进行控制。SqlSessionFactory提供了以下几个用于创建SqlSession的方法

SqlSession openSession()
SqlSession openSession(boolean autoCommit)
SqlSession openSession(Connection connection)
SqlSession openSession(TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType)
SqlSession openSession(ExecutorType execType, boolean autoCommit)
SqlSession openSession(ExecutorType execType, Connection connection)

我们在觉得使用哪个方法来创建SqlSession主要是根据以下几点

1. 是否要关闭自动提交,意味着开启事务
1. 使用外部传入的连接对象还是从配置信息中获取到的连接对象
1. 使用哪种执行方式,一共有三种执行方式
* ExecutorType.SIMPLE:每次执行SQL时都创建一个新的PreparedStatement
* ExecutorType.REUSE:复用PreparedStatement对象
* ExecutorType.BATCH:进行批处理
在前面的例子中,我们使用的是空参的方法来创建SqlSession对象的,这种情况下Mybatis会创建一个开启了事务的、从配置的连接池中获取连接的、事务隔离级别跟数据库保持一致的、执行方式为ExecutorType.SIMPLE的SqlSession对象。

我们基于上面的例子来体会一下Mybatis中的事务管理,代码如下:

public class Main {
public static void main(String[] args) throws Exception {
String resource = "mybatis-config.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(resource);
// 1.解析XML配置
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 2.基于解析好的XML配置创建一个SqlSessionFactory
SqlSessionFactory sqlSessionFactory = builder.build(resourceAsStream);
// 3.开启一个SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4.获取一个代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user =new User();
user.setId(3);
user.setName("dmz111");
user.setAge(27);
// 插入一条数据
mapper.insert(user);
// 抛出一个异常
throw new RuntimeException("发生异常!");
}
}

运行上面的代码,我们会发现数据库中并不会新增一条数据,但是如果我们在创建SqlSession时使用下面这种方式

SqlSession sqlSession = sqlSessionFactory.openSession(true);
即使发生了异常,数据仍然会插入到数据库中

# Spring整合Mybatis的原理

首先明白一点,虽然我在之前介绍了Mybatis的事务管理,但是当Mybatis跟Spring进行整合时,事务的管理完全由Spring进行控制!所以对于整合原理的分析不会涉及到事务的管理

我们先来看一个Spring整合Mybatis的案例,我这里以JavaConfig的形式进行整合,核心配置如下:
[p]@Configuration[url=https://blog.51cto.com/14890701/mailto:br/>@ComponentScan(]br/>@ComponentScan("com.dmz.mybatis.spring")@MapperScan(]br/>@MapperScan("com.dmz.mybatis.spring.mapper")@Target(ElementType.TYPE)@Target(ElementType.TYPE)@Import(MapperScannerRegistrar.class)@Import(MapperScannerRegistrar.class)@Override@Override@Override@Override@Override@Override
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: