自己实现Dao接口的方式
-
首先是写一个 UserDao 的实现类:UserDaoImpl
这一个步骤区别于一般情况,平时我们一般不会自己写Dao的实现类,而是通过Mybatis通过动态代理的方式帮我们实现一个Dao的代理类,然后我们直接使用;
public class UserDaoImpl implements UserDao {
// 创建SqlSession的工厂对象
private SqlSessionFactory factory;
// 通过构造的方式将factory传入本实现类
public UserDaoImpl (SqlSessionFactory factory) {
this.factory = factory;
}
/**
* 查询所有
*/
@Override
public List<User> findAll() {
// 获取操控数据库的 SqlSession 对象
SqlSession session = factory.openSession();
// 调用selectList()方法来查询,dao接口全限定类名.findAll(方法名)
List<User> users = session.selectList("com.xfzhao.dao.UserDao.findAll");
// 关闭资源
session.close();
// 返回结果
return users;
}
}
- 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">
<!-- mybatis的主配置文件 -->
<configuration>
<typeAliases>
<package name="com.xfzhao.domain"/>
</typeAliases>
<!-- 配置环境 -->
<environments default="mysql">
<!-- 配置mysql的环境-->
<environment id="mysql">
<!-- 配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
<mappers>
<mapper resource="com/xfzhao/dao/Userdao.xml"></mapper>
</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="com.xfzhao.dao.UserDao">
<!--查询所有用户-->
<select id="findAll" resultType="com.xfzhao.domain.User">
select * from user;
</select>
</mapper>
- 测试方法:
public class mybatis {
private InputStream in;
private SqlSessionFactory factory;
private UserDao userDao;
@Before
public void init() throws IOException {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
factory = builder.build(in);
}
@Test
public void testFindAll() {
UserDao userDao = new UserDaoImpl(factory);
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
}
流程解析-findAll
-
首先我们调用的是 SqlSession 的 selectList() 方法:
List<User> users = session.selectList("com.xfzhao.dao.UserDao.findAll");
-
因为 SqlSession 是接口,通过断点调试发现,真正的实现类的是
DefaultSqlSession
,其中有三个selectList()
方法,最终都是走的最后一个三个参数的方法; -
其中很关键的一步就是
this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
这里的 executor 是 CachingExecutor
// DefaultSqlSession 类 public <E> List<E> selectList(String statement) { // 调用下面一个方法 return this.selectList(statement, (Object)null); } public <E> List<E> selectList(String statement, Object parameter) { // 调用下面一个方法 return this.selectList(statement, parameter, RowBounds.DEFAULT); } // 真正执行的是这个方法 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { List var5; try { MappedStatement ms = this.configuration.getMappedStatement(statement); // 走的是这里,这里的 executor 是 CachingExecutor var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception var9) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9); } finally { ErrorContext.instance().reset(); } return var5; }
-
-
下面找到 CachingExecutor :
// CachingExecutor类 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { // 省略代码... // 走的是这里,this.delegate 是 SimpleExecutor 类 return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
-
然后我们去 SimpleExecutor 类中:
我们发现这个类中没有 query() 方法,只有 doQuery() 方法。于是我们就去找它的父类:BaseExecutor
public class SimpleExecutor extends BaseExecutor {...}
在 BaseExecutor 类中存在 query() 方法:而且存在两个query() 方法,我们直接看最后被调用的那一个
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { //....省略其他 // 主要走的就是这一步: 执行 queryFromDatabase() 方法 list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); }
继续看 queryFromDatabase() 方法:
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { // .... // 走的是这一步,执行的是抽象的 doQuery()方法 // 这里我们就知道了实际上绕了一圈还是走的是 simpleExecutor 的 doQuery() 方法 list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql); // .... }
最后我们再回去看 simpleExecutor 的 doQuery() 方法
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; List var9; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = this.prepareStatement(handler, ms.getStatementLog());\ // 最后走的这一步:这里的 handler 是 RoutingStatementHandler var9 = handler.query(stmt, resultHandler); } finally { this.closeStatement(stmt); } return var9; }
-
现在来到了 RoutingStatementHandler 类中:查看它的 query() 方法:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { // 走的这里:这里的delegate是 PrepareStatementHandler return this.delegate.query(statement, resultHandler); }
-
然后继续进入类: PrepareStatementHandler 中,查看 query() 方法:这里就是JDBC 的常规操作
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { // 到这里就很熟悉了,使用的是 prepareStatement对象 PreparedStatement ps = (PreparedStatement)statement; // 执行的是它的 executor() 方法 ps.execute(); // 下面的步骤就是封装结果集 return this.resultSetHandler.handleResultSets(ps); }
执行流程图
自己实现Dao接口的方式
-
首先是写一个 UserDao 的实现类:UserDaoImpl
这一个步骤区别于一般情况,平时我们一般不会自己写Dao的实现类,而是通过Mybatis通过动态代理的方式帮我们实现一个Dao的代理类,然后我们直接使用;
public class UserDaoImpl implements UserDao {
// 创建SqlSession的工厂对象
private SqlSessionFactory factory;
// 通过构造的方式将factory传入本实现类
public UserDaoImpl (SqlSessionFactory factory) {
this.factory = factory;
}
/**
* 查询所有
*/
@Override
public List<User> findAll() {
// 获取操控数据库的 SqlSession 对象
SqlSession session = factory.openSession();
// 调用selectList()方法来查询,dao接口全限定类名.findAll(方法名)
List<User> users = session.selectList("com.xfzhao.dao.UserDao.findAll");
// 关闭资源
session.close();
// 返回结果
return users;
}
}
- 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">
<!-- mybatis的主配置文件 -->
<configuration>
<typeAliases>
<package name="com.xfzhao.domain"/>
</typeAliases>
<!-- 配置环境 -->
<environments default="mysql">
<!-- 配置mysql的环境-->
<environment id="mysql">
<!-- 配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
<mappers>
<mapper resource="com/xfzhao/dao/Userdao.xml"></mapper>
</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="com.xfzhao.dao.UserDao">
<!--查询所有用户-->
<select id="findAll" resultType="com.xfzhao.domain.User">
select * from user;
</select>
</mapper>
- 测试方法:
public class mybatis {
private InputStream in;
private SqlSessionFactory factory;
private UserDao userDao;
@Before
public void init() throws IOException {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
factory = builder.build(in);
}
@Test
public void testFindAll() {
UserDao userDao = new UserDaoImpl(factory);
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
}
流程解析-findAll
-
首先我们调用的是 SqlSession 的 selectList() 方法:
List<User> users = session.selectList("com.xfzhao.dao.UserDao.findAll");
-
因为 SqlSession 是接口,通过断点调试发现,真正的实现类的是
DefaultSqlSession
,其中有三个selectList()
方法,最终都是走的最后一个三个参数的方法; -
其中很关键的一步就是
this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
这里的 executor 是 CachingExecutor
// DefaultSqlSession 类 public <E> List<E> selectList(String statement) { // 调用下面一个方法 return this.selectList(statement, (Object)null); } public <E> List<E> selectList(String statement, Object parameter) { // 调用下面一个方法 return this.selectList(statement, parameter, RowBounds.DEFAULT); } // 真正执行的是这个方法 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { List var5; try { MappedStatement ms = this.configuration.getMappedStatement(statement); // 走的是这里,这里的 executor 是 CachingExecutor var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception var9) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9); } finally { ErrorContext.instance().reset(); } return var5; }
-
-
下面找到 CachingExecutor :
// CachingExecutor类 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { // 省略代码... // 走的是这里,this.delegate 是 SimpleExecutor 类 return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
-
然后我们去 SimpleExecutor 类中:
我们发现这个类中没有 query() 方法,只有 doQuery() 方法。于是我们就去找它的父类:BaseExecutor
public class SimpleExecutor extends BaseExecutor {...}
在 BaseExecutor 类中存在 query() 方法:而且存在两个query() 方法,我们直接看最后被调用的那一个
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { //....省略其他 // 主要走的就是这一步: 执行 queryFromDatabase() 方法 list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); }
继续看 queryFromDatabase() 方法:
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { // .... // 走的是这一步,执行的是抽象的 doQuery()方法 // 这里我们就知道了实际上绕了一圈还是走的是 simpleExecutor 的 doQuery() 方法 list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql); // .... }
最后我们再回去看 simpleExecutor 的 doQuery() 方法
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; List var9; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = this.prepareStatement(handler, ms.getStatementLog());\ // 最后走的这一步:这里的 handler 是 RoutingStatementHandler var9 = handler.query(stmt, resultHandler); } finally { this.closeStatement(stmt); } return var9; }
-
现在来到了 RoutingStatementHandler 类中:查看它的 query() 方法:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { // 走的这里:这里的delegate是 PrepareStatementHandler return this.delegate.query(statement, resultHandler); }
-
然后继续进入类: PrepareStatementHandler 中,查看 query() 方法:这里就是JDBC 的常规操作
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { // 到这里就很熟悉了,使用的是 prepareStatement对象 PreparedStatement ps = (PreparedStatement)statement; // 执行的是它的 executor() 方法 ps.execute(); // 下面的步骤就是封装结果集 return this.resultSetHandler.handleResultSets(ps); }