上期文章
走进Mybatis–01.简单模拟实现Mybatis
上一篇文章中,简单的使用了Mybatis来进行了一个数据查询的实现,发现我们只需要声明Dao接口,创建Mapper.xml文件,并不需要我们自己来实现Dao接口的逻辑,就可以实现数据的查询操作。
1. 自定义Dao接口的实现类
当然也可以手动编写Dao接口的实现类来进行数据的CRUD操作。
首先创建Maven项目,在pom文件中加入mybatis和数据库连接的依赖。
首先回顾之前的方式来进行数据查询
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
创建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="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://xxxx:3306/demo?useUnicode=true"/>
<property name="username" value="xxxx"/>
<property name="password" value="xxxx"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/DemoMapper.xml"/> <!--1.加载MapperXML-->
</mappers>
</configuration>
编写 DemoMapper java接口
package com.iBatis.test.dao;
import com.iBatis.test.entity.Demo;
import java.util.List;
public interface DemoMapper {
List<Demo> queryDemo();
}
编写DemoMapper.xml 文件
<?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">
<!--指定dao -->
<mapper namespace="com.iBatis.test.dao.DemoMapper">
<!--查询-->
<select id="queryDemo"
useCache="true"
resultType="com.iBatis.test.entity.Demo">
SELECT
id,
name,
age,
salary,
birthDay
FROM
demo
</select>
</mapper>
编写Main方法进行测试
package com.iBatis.test;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.iBatis.test.dao.DemoMapper;
import java.io.InputStream;
public class MyBatisTest {
public static void main(String[] args) throws Exception {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoMapper mapper = sqlSession.getMapper(DemoMapper.class);
System.out.println("mapper QueryList结果="+ mapper.queryDemo());
}
}
正常查询出来结果,此处sqlSession.getMapper(DemoMapper.class); 拿出来的DemoMapper 是使用了Mybatis进行代理的实现类,这个等会再说,此时我们发现并没有创建DemoMapper的实现类,也可以进行查询操作,我们来创建DemoMapper的实现类来进行查询
定义DemoMapperImpl实现类,来实现DemoMapper接口。在上一篇文章中来看,实际上是通过sqlSession来进行的查询,我们的实现类就可以这样写
package com.iBatis.test.dao.impl;
import com.iBatis.test.dao.DemoMapper;
import com.iBatis.test.entity.Demo;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class DemoMapperImpl implements DemoMapper {
private SqlSession sqlSession;
public DemoMapperImpl(SqlSession sqlSession){
this.sqlSession = sqlSession;
}
@Override
public List<Demo> queryDemo() {
System.out.println("执行DemoMapperImpl queryDemo查询方法。");
List<Demo> result = sqlSession.selectList("com.iBatis.test.dao.DemoMapper.queryDemo");
return result;
}
}
修改测试类的Main方法来调用DemoMapperImpl进行查询
package com.iBatis.test;
import com.iBatis.test.dao.impl.DemoMapperImpl;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.iBatis.test.dao.DemoMapper;
import java.io.InputStream;
public class MyBatisTest {
public static void main(String[] args) throws Exception {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
// DemoMapper mapper = sqlSession.getMapper(DemoMapper.class);
// System.out.println("mapper QueryList结果="+ mapper.queryDemo());
DemoMapper mapper = new DemoMapperImpl(sqlSession);
System.out.println("mapperImpl queryList结果="+ mapper.queryDemo());
}
}
同样可以查询出来结果
2. Dao接口实现类的执行逻辑分析
实现类中,通过sqlSession.selectList("")方法进行了数据查询。。
看代码发现org.apache.ibatis.session.SqlSession有两个实现类,断点执行发现走的是DefaultSqlSession
进入DefaultSqlSession 找到selectList方法
MappedStatement ms = this.configuration.getMappedStatement(statement);
这个就类似上一篇文章中,通过类全名+方法名从Configuration获取到要执行的sql语句返回结果类型,入参类型等信息的数据
this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
这里面执行了executor的query方法,接下来看看executor是什么东西
发现org.apache.ibatis.executor.Executor是一个接口,它有这么多实现类
打断点发现,此时的executor是叫CachingExecutor
进入CachingExecutor的query方法
发现又走到了
delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
这个delegate是什么呢?
发现这个delegate 是一个SimpleExecutor 实现类的Executor
然后进入SimpleExecutor类找query方法,发现并没有query方法
发现它(SimpleExecutor)是继承自BaseExecutor,我们看BaseExecutor,
执行断点,走到了,看名字意思是,从数据库查询,好像快找到最后了
this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
在queryFromDatabase方法中,发现它调用了this.doQuery()方法,这个方法在BaseBaseExecutor是一个抽象方法,在SimpleExecutor中进行了实现
protected abstract List doQuery(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, BoundSql var5) throws SQLException;
于是我们就清楚了,CachingExecutor中调用了delegate.query()查询先执行了SimpleExecutor父类的query()方法,在这个方法里面调用了 被子类(SimpleExecutor)实现的doQuery方法
终于我们看到了我们熟悉的PreparedStatement对象,
此处执行了StatementHandler.query(), 继续看StatementHandler的类结构
上面的handler就是RoutingStatementHandler的实例,进入RoutingStatementHandler看代码
发现从RoutingStatementHandler里面又使用PreparedStatementHandler的query方法
在PreparedStatementHandler的query方法中终于发现了我们最开始写jdbc时,使用PreparedStatement来执行sql语句的代码了,突然就觉得亲切了好多
在接着看
this.resultSetHandler.handleResultSets(ps);
resultSetHandler类是一个接口,它只有一个默认实现类DefaultResultSetHandler
在这个方法里面对查询出来的数据做解析
3. SqlSession查询执行逻辑总结
-
首先执行
defaultSqlSession.selectList(""); -
最终调用的为三个参数的selectList方法
defaultSqlSession.selectList("",params,RowBounds); -
在selectList方法里面先获取了MappedStatement对象,然后调用了executor.query方法
executor.query(); //实际是 cachingExecutor.query(); -
cachingExecutor的query方法中调用了delegate.query()方法
delegate.query(); // delegate为SimpleExecutor实例,实际调用simpleExecutor.query();该query方法在抽象父类BaseExecutor实现 -
baseExecutor.query方法中调用了自己的baseExecutor.queryFromDatabase()方法
-
queryFromDatabase() 调用了抽象方法doQuery();该方法在SimpleExecutor实现
-
在SimpleExecutor.doQquer()方法执行了handler.query()方法;
handler.query();// 实际是RoutingStatementHandler.query(); -
RoutingStatementHandler.query()又调用了PreparedStatementHandler的query()方法
-
在PreparedStatementHandler的query()执行了preparedStatement.execute();
-
最后执行resultSetHandler.handleResultSets(ps);在DefaultResultSetHandler对结果集进行封装