Mybatis 映射器接口代理对象的方式 运行过程debug分析

Posted jxearlier

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis 映射器接口代理对象的方式 运行过程debug分析相关的知识,希望对你有一定的参考价值。

查询一张表的所有数据。

环境:

使用工具IntelliJ IDEA 2018.2版本。

创建Maven工程不用骨架

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
 3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <modelVersion>4.0.0</modelVersion>
 6 
 7     <groupId>com.jxjdemo</groupId>
 8     <artifactId>day34_mybatis1_curd_dao</artifactId>
 9     <version>1.0-SNAPSHOT</version>
10 
11     <properties><!--锁定编译版本与字符集-->
12         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
13         <maven.compiler.source>1.8</maven.compiler.source>
14         <maven.compiler.target>1.8</maven.compiler.target>
15     </properties>
16 
17     <dependencies>
18         <dependency><!--导入mysql依赖-->
19             <groupId>mysql</groupId>
20             <artifactId>mysql-connector-java</artifactId>
21             <version>5.1.47</version>
22         </dependency>
23 
24         <dependency> <!--导入mybatis依赖-->
25             <groupId>org.mybatis</groupId>
26             <artifactId>mybatis</artifactId>
27             <version>3.5.2</version>
28         </dependency>
29 
30         <dependency> <!--导入日志依赖-->
31             <groupId>log4j</groupId>
32             <artifactId>log4j</artifactId>
33             <version>1.2.17</version>
34         </dependency>
35 
36         <dependency>
37             <groupId>junit</groupId>
38             <artifactId>junit</artifactId>
39             <version>4.12</version>
40         </dependency>
41     </dependencies>
42 </project>
  1. 表User
 1 package com.jxjdemo.domain;
 2 import java.util.Date;
 3 
 4 public class User 
 5     private Integer id;
 6     private String username;
 7     private Date birthday; //框架会帮我们自动转
 8     private String sex;
 9     private String address;
10 
11     @Override
12     public String toString() 
13         return "User" +
14                 "id=" + id +
15                 ", username=‘" + username + ‘\\‘‘ +
16                 ", birthday=" + birthday +
17                 ", sex=‘" + sex + ‘\\‘‘ +
18                 ", address=‘" + address + ‘\\‘‘ +
19                 ‘‘;
20     
21 //省略Get与Set方法

        2.映射器XML

1 <?xml version="1.0" encoding="utf-8"?><!--引入约束-->
2 <!DOCTYPE mapper
3         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5         <mapper namespace="com.jxjdemo.dao.UserDao"><!--mapper:映射器配置/namespace:映射器的全限定类名-->
6 
7         <select id="queryAll" resultType="com.jxjdemo.domain.User"><!--查询所有-->
8                 select * from user
9         </select>

 

      3.映射器配置文件

<?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.jxjdemo.dao.UserDao"><!--mapper:映射器配置/namespace:映射器的全限定类名-->

        <select id="queryAll" resultType="com.jxjdemo.domain.User">
                select * from user
        </select>
        </mapper>

      4.实现类

 1 package com.jxjdemo.dao.impl;
 2 import com.jxjdemo.dao.UserDao;
 3 import com.jxjdemo.domain.User;
 4 import org.apache.ibatis.session.SqlSession;
 5 import org.apache.ibatis.session.SqlSessionFactory;
 6 import java.util.List;
 7 
 8 public class UserDaoImpl implements UserDao //执行SQL语句用实现类做,不用代理对象。
 9     //从工厂里面获取sqlsession对象
10     private SqlSessionFactory factory;//以后由sping整合sping创建,现在去测试类里面创建
11 
12     public UserDaoImpl(SqlSessionFactory factory) 
13         this.factory = factory;
14     
15 
16     @Override
17     public List<User> queryAll() 
18         SqlSession session = factory.openSession();
19         List<User> list = session.selectList("com.jxjdemo.dao.UserDao.queryAll");//返回的是Object但实际得到的是(UserDao)
20         session.close();//session关闭流,释放资源
21         return list;
22     

5.测试类

 1 package com.jxjtest.test;
 2 
 3 import com.jxjdemo.dao.UserDao;
 4 import com.jxjdemo.dao.impl.UserDaoImpl;
 5 import com.jxjdemo.domain.User;
 6 import org.apache.ibatis.io.Resources;
 7 import org.apache.ibatis.session.SqlSessionFactory;
 8 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 9 import org.junit.After;
10 import org.junit.Before;
11 import org.junit.Test;
12 
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.util.Date;
16 import java.util.List;
17 
18 public class MybatisCurdTest 
19     private SqlSessionFactory factory; 
20     private InputStream is;      
21     private UserDao userDao;  
22 
23     @Test
24     public void testQueryAll() //查询全部
25         //读取配置文件,获取sqlsession对象工厂,获取映射器提取
26         List<User> userList = userDao.queryAll();
27         for (User user : userList)
28             System.out.println(user);
29         
30     
31 @Before
32     public void init() throws IOException  //重复执行的代码,单独提取出来
33         is = Resources.getResourceAsStream("sqlMapConfig.xml");//提取成员变量后
34 
35         // SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is); //获取sqlsession对象,通过工厂构建一个,提取成员变量
36         factory = new SqlSessionFactoryBuilder().build(is);
37         userDao = new UserDaoImpl(factory);//提取成员变量
38     
39     @After
40     public void destroy() throws IOException   //关流
41         is.close();
42     
43 

 6.数据库核心配置文件XML

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE configuration
 3         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 4         "http://mybatis.org/dtd/mybatis-3-config.dtd">
 5 <!--mybatis的核心配置文件,主要配置数据库连接信息-->
 6 <configuration><!--根标签-->
 7     <!--enxironments 可以配置多个数据库环境-->
 8     <environments default="mysql"><!--default 默认使用的数据库-->
 9         <environment id="mysql"><!--environment每一个数据库连接(配置)信息-->
10             <transactionManager type="JDBC" /><!--事物管理方式-->
11             <dataSource type="POOLED"><!--数据源。不使用UN连接池POOLED,POOLED使用连接池,JNDI查找数据源配置文件-->
12                 <property name="driver" value="com.mysql.jdbc.Driver" />
13                 <property name="url" value="jdbc:mysql://localhost:端口号/库名"/>
14                 <property name="username" value="账号"/>
15                 <property name="password" value="密码"/>
16             </dataSource>
17         </environment>
18     </environments>
19 
20     <mappers>
21         <mapper resource="com/jxjdemo/dao/UserDao.xml" />
22     </mappers>
23 </configuration>

  7.省略log4j日志配置文件,目录结构。

技术图片

8.在测试类打断点,开始deBug跟踪,从这里开始。

相册里面有步骤截图,可以配合一起看。

1 List<User> userList = userDao.queryAll();

9.到实现类,获取session

1 SqlSession session = factory.openSession();

10.跟踪这行代码做了什么事情,进入这个方法。

1 List<User> list = session.selectList("com.jxjdemo.dao.UserDao.queryAll");

11.到了DefaultSqlSession.java类里面,在selectList调了selectList,方法的重载。

1 @Override
2   public <E> List<E> selectList(String statement) 
3     return this.selectList(statement, null);
4   
5 
6   @Override
7   public <E> List<E> selectList(String statement, Object parameter) 
8     return this.selectList(statement, parameter, RowBounds.DEFAULT);
9   

12.到了ms,获取数据库,配置文件信息,与SQL类型。关键在于executor.query执行查询。

 1 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) 
 2     try 
 3       MappedStatement ms = configuration.getMappedStatement(statement);
 4       return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
 5      catch (Exception e) 
 6       throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
 7      finally 
 8       ErrorContext.instance().reset();
 9     
10   

13.先对wrspCollection(parameter)包装一下。回到12.

1 if (object instanceof Collection) 这里省略

14.进入CachingExecutor.java.这个类自己不执行。

1  @Override
2   public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException 
3     BoundSql boundSql = ms.getBoundSql(parameterObject);

15.这里第一步,从ms.getBoundSql获取到了绑定的SQL语句。

CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);//缓存相关忽略

16.继续调query方法重载。

return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

17.到了query方法。

1 @Override
2   public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
3       throws SQLException 
4     Cache cache = ms.getCache();

18.找缓存。

1 if (cache != null) 

19.缓存里面没有,CachingExecutor.java.这个类自己不执行,就调了delegate委托者。

 return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

20.CachingExecutor.java.这个类是从父类继承的,所以到了BaseExecutor.java.

@Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException 
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());

21.往下走。

if (closed) //1.
if (queryStack == 0 && ms.isFlushCacheRequired()) //2.
queryStack++;//3.
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;//4.
if (list != null) //5.

22.到了queryFromDatabase开始到库里面查询。

list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

23.BaseExecutor.java.执行了doQuery方法

localCache.putObject(key, EXECUTION_PLACEHOLDER);//1
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);//2.

24.进入doQuery查看,到了SimpleExecutor.java

1 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException 
2     Statement stmt = null;//1
3 Configuration configuration = ms.getConfiguration();//2.configuration所有的配置信息
4 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);//3.
5 stmt = prepareStatement(handler, ms.getStatementLog());//4.准备得到一个
6 return handler.query(stmt, resultHandler);//5.handler.query开始真正执行了

25.进入query到了RoutingStatementHandler.java

return delegate.query(statement, resultHandler);//调了delegate

26.调了delegate到了PreparedStatementHandler.java

1 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException 
2     PreparedStatement ps = (PreparedStatement) statement;PreparedStatement预编译对象
3     ps.execute();//2.获取预编译对象,执行任意语句
4     return resultSetHandler.handleResultSets(ps);//
5   

到了这里就是最终的结果,剩余处理结果集,封装的事情了。

结果:Mybatis封装的再深底层还是JDBC。

 

 

 

 

以上是关于Mybatis 映射器接口代理对象的方式 运行过程debug分析的主要内容,如果未能解决你的问题,请参考以下文章

Spring整合mybatis框架--调用映射器接口的方式

SQL映射器Mapper接口(MyBatis)

MyBatis映射器--多参数传递方式

MyBatis sql映射器 Mapper

MyBatis数据库连接的基本使用-补充Mapper映射器

深入浅出MyBatis:MyBatis解析和运行原理