MyBatis是一个优秀的基于Java的持久层框架。其内部封装了JDBC,使开发者只需要关注SQL语句本身,不用花费精力去处理如注册驱动,创建Connection,配置Statement等繁琐过程。
我们通过测试案例来看下MyBatis的基本API的使用及相关简介;
public void insertTest(Student student) {
try {
// 1.加载主配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
// 2.创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
// 4.相关数据库操作
sqlSession.insert("insert", student);
sqlSession.commit();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
}
我们需要通过SqlSession对象来操作数据库,而SqlSession对象的创建,需要其工厂对象SqlSessionFactory对象;SqlSessionFactory对象需要通过其构建器对象SqlSessionFactoryBuilder的build()方法,在加载了主配置文件的输入流对象后创建的;
SqlSessionFactoryBuilder类
SqlSessionFactory的创建,需要使用SqlSessionFactoryBuilder对象的build()方法,由于SqlSessionFactory对象在创建完工厂后,就完成了其使命,即可被销毁。所以我们一般=将SqlSessionFactoryBuilder对象创建为一个方法内的局部对象,方法结束后,对象销毁;
SqlSessionFactory接口
SqlSessionFactory接口对象是一个重量级对象,系统开销大,是线程安全的,所以一个应用只需要一个该对象即可。创建SqlSession需要使用SqlSessionFactory接口的openSession()方法;
openSession(true):创建一个有自动提交功能的SqlSession
openSession(false):创建一个非自动提交功能的Sql’Session,需要手动提交;
默认为创建一个非自动提交功能的SqlSession.
SqlSession接口
SqlSession接口对象用于执行持久化操作,一个SqlSession对应这一次数据库会话,一次会话以SqlSession对象的创建开始,以SqlSession对象的关闭结束。
SqlSession接口的对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其close()方法,将其关闭。再次需要会话,再次创建。而关闭时会判断当前SqlSession是否被提交,若没有被提交,则会执行回滚后关闭;若已被提交,则直接将SqlSession关闭,所以,SqlSession无需手工回滚;
在上面的测试案例我们发现有几个问题,看MyBatis是如何帮我们解决的;
1.我们读取配置的文件的流没有关闭?
我们查看SqlSessionFactoryBuilder对象的build()方法,查看其底层源码:
我们可以看到在输入流被使用后,SqSessionFactoryBuilder对象的build()方法会自动将输入流关闭,不需要我们手动关闭;
2.我们调用SqlSession.commit()方法就会提交事务?
我们查看SqlSession对象的创建,需要使用SqlSessionFactory接口对象的openSession()方法;SqlSessionFactory接口的实现类为DefaultSqlSessionFactory;
从这里我们也可以知道openSession()方法默认是创建了一个非自动提交的SqlSession;
我们看到创建SqlSession时就是加载主配置文件,然后创建一个执行器对象,然后会初始化一个数据库数据被修改的标志变量dirty;
然后SqlSession对象调用对应的增删改查方法,我们在测试案例中调用insert方法,我们看其底层实现:
大家可以查看下SqlSession的insert(),delete(),update()方法,其底层均是调用的update()方法;我们也可以看到底层调用update()方法中会将变量dirty修改为true; 并且根据对应的statement获取映射文件中指定id的Sql语句,然后将Sql交给执行器executor执行;
我们接下来在看我们SqlSession的commit()方法;
当executor执行完成后,又会将dirty变量更新为false;
我们从底层代码知道,isCommitOrRollbackRequired(false)方法的返回值为true; 即:autoCommit为false; dirty = true ; force = false;
然后在调用executor的commit()方法;
从底层代码调用可以看到SqlSession的无参commit()方法,最终会将事务进行提交的。
3.SqlSession如何进行回滚的?
我们查看SqlSession的关闭close()方法底层的实现:
executor关闭后,会将数据被修改的标志dirty重置为false,表示前面的更新操作已经结束了,此时的数据处于未被修改的状态;
我们首先看下sqlSession.commit后在进行sqlSession.close():
注意此时isCommitOrRollbackRequired(false)方法返回的是false;
this.autoCommit = false;
this.dirty = false; 由于SqlSession.commit后,会将改变量重置为false;
this.rollback()方法实现:
接下来我们在看下,如果没有执行commit(),直接执行的close()方法会怎么样?
我们直接看下isCommitOrRollbackRequired方法
我们在执行更新操作的时候,底层调用update()方法就会将dirty更新为true;可以看到isCommitOrRollbackRequired方法最终返回的是true; 继续看executor的close方法:
从上面的代码中可以知道,在SqlSession进行关闭时,会将事务回滚后关闭,所以对于,无需通过显示的对SqlSession进行回滚,达到事务回滚的目的