*SqlSessions 由 SqlSessionFactory 实例创建的。SqlSessionFactory 对 象 包 含 创 建 SqlSession 实 例 的 所 有 方 法 。 而 SqlSessionFactory 本 身 是 由
SqlSessionFactoryBuilder 创建。*
那么我们先从 SqlSessionFactoryBuilder 开始
SqlSessionFactoryBuilder:
SqlSessionFactory 的创建,需要使用 SqlSessionFactoryBuilder 对象的 build()方法。由于SqlSessionFactoryBuilder 对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。所以,一般会将该 SqlSessionFactoryBuilder 对象创建为一个方法内的局部对象,方法结束,对象销毁。其被重载的 build()方法较多:
SqlSessionFactoryBuilder 有五个 build()方法,每一种都允许你从不同的资源中创建一个 SqlSession 实例。
第一种方法是最常用的,它使用了一个参照了 XML 文档或特定的 mybatis-config.xml 文件的 Reader 实例。 可选的参数是 environment 和 properties。 environment 决定加载哪种环境,包括数据源和事务管理器。比如:
如果你调用了 一个使用 environment 参数 方 式的 build 方法, 那么 MyBatis 将会使用 configuration 对象来配置这个 environment。 当然, 如果你指定了一个不合法的 environment, 你会得到错误提示。 如果你调用了其中之一没有 environment 参数的 build 方法, 那么就使用 默认的 environment(在上面的示例中就会指定为 default=”development”)。
如果你调用了使用 properties 实例的方法,那么 MyBatis 就会加载那些 properties(属性 配置文件) ,并你在你配置中可使用它们。那些属性可以用${propName}语法形式多次用在 配置文件中。
回想一下,属性可以从 mybatis-config.xml 中被引用,或者直接指定它。因此理解优先 级是很重要的。我们在文档前面已经提及它了,但是这里要再次重申:
如果一个属性存在于这些位置,那么 MyBatis 将会按找下面的顺序来加载它们:
在 properties 元素体中指定的属性首先被读取,
从 properties 元素的类路径 resource 或 url 指定的属性第二个被读取, 可以覆盖已经 指定的重复属性,
作为方法参 数传递 的属性最 后被读 取,可以 覆盖已 经从 properties 元 素体和 resource/url 属性中加载的任意重复属性。
因此,最高优先级的属性是通过方法参数传递的,之后是 resource/url 属性指定的,最 后是在 properties 元素体中指定的属性。
总结一下,前四个方法很大程度上是相同的,但是由于可以覆盖,就允许你可选地指定 environment 和/或 properties。 这里给出一个从 mybatis-config.xml 文件创建 SqlSessionFactory 的示例:
注意这里我们使用了 Resources 工具类,这个类在 org.mybatis.io 包中。Resources 类正如其名,会帮助你从类路径下,文件系统或一个 web URL 加载资源文件。看一下这个类的 源代码或者通过你的 IDE 来查看,就会看到一整套有用的方法。这里给出一个简表:
最后一个 build 方法使用了一个 Configuration 实例。configuration 类包含你可能需要了 解 SqlSessionFactory 实例的所有内容。Configuration 类对于配置的自查很有用,包含查找和 操作 SQL 映射(不推荐使用,因为应用正接收请求) 。configuration 类有所有配置的开关, 这些你已经了解了,只在 Java API 中露出来。这里有一个简单的示例,如何手动配置 configuration 实例,然后将它传递给 build()方法来创建 SqlSessionFactory。
SqlSessionFactory已创建,可以用来创建 SqlSession 实例。
SqlSessionFactory:
SqlSessionFactory 接口对象是一个重量级对象(系统开销大的对象),是线程安全的,所 以一个应用只需要一个该对象即可。创建SqlSession 需要使用 SqlSessionFactory 接口的的 openSession()方法。
SqlSessionFactory 有六个方法可以用来创建 SqlSession 实例。通常来说,如何决定是你 选择下面这些方法时:
- Transaction (事务): 你想为 session 使用事务或者使用自动提交(通常意味着很多 数据库和/或 JDBC驱动没有事务)?
- Execution (执行): 你想 MyBatis 复用预处理语句和/或批量更新语句(包括插入和 删除)?
重载的 openSession()方法签名设置允许你选择这些可选中的任何一个组合。
默认的 openSession()方法没有参数,它会创建有如下特性的 SqlSession:
- 会开启一个事务(也就是不自动提交)
- 连接对象会从由活动环境配置的数据源实例中得到。
- 事务隔离级别将会使用驱动或数据源的默认设置。
- 预处理语句不会被复用,也不会批量处理更新。
这些方法大都可以自我解释的。 开启自动提交, “true” 传递 给可选的 autoCommit 参数。 提供自定义的连接,传递一个 Connection 实例给 connection 参数。注意没有覆盖同时设置 Connection 和 autoCommit 两者的方法,因为 MyBatis 会使用当前 connection 对象提供的设 置。 MyBatis 为事务隔离级别调用使用一个 Java 枚举包装器, 称为 TransactionIsolationLevel, 否则它们按预期的方式来工作,并有 JDBC 支持的 5 级 ( NONE,READ_UNCOMMITTED,READ_COMMITTED,REPEA TABLE_READ,SERIALIZA BLE)
还有一个可能对你来说是新见到的参数,就是 ExecutorType。这个枚举类型定义了 3 个 值:
- ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情。它为每个语句的执行创建一个新的预处理语句。
- ExecutorType.REUSE: 这个执行器类型会复用预处理语句。
- ExecutorType.BATCH:这个执行器会批量执行所有更新语句,如果 SELECT 在它们中间执行还会标定它们是 必须的,来保证一个简单并易于理解的行为。
注意: 在 SqlSessionFactory 中还有一个方法我们没有提及,就是 getConfiguration()。这 个方法会返回一个 Configuration 实例,在运行时你可以使用它来自检 MyBatis 的配置。
SqlSession 接口
SqlSession 接口对象用于执行持久化操作。一个 SqlSession 对应着一次数据库会话,一 次会话以 SqlSession对象的创建开始,以 SqlSession 对象的关闭结束。 SqlSession接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其close()方法,将其关闭。再次需要会话,再次创建。而在关闭时会判断当前的 SqlSession 是否被提交:若没有被提交,则会执行回滚后关闭;若已被提交,则直接将 SqlSession 关闭。 所以,SqlSession 无需手工回滚。
在 SqlSession 类中有超过 20 个方法,所以将它们分开成易于理解的组合。
语句执行方法
这些方法被用来执行定义在 SQL 映射的 XML 文件中的 SELECT,INSERT,UPDA E T 和 DELETE 语句。它们都会自行解释,每一句都使用语句的 ID 属性和参数对象,参数可以 是原生类型(自动装箱或包装类) ,JavaBean,POJO 或 Map。
selectOne 和 selectList 的不同仅仅是 selectOne 必须返回一个对象。 如果多余一个, 或者 没有返回 (或返回了 null) 那么就会抛出异常。 , 如果你不知道需要多少对象, 使用 selectList。
如果你想检查一个对象是否存在,那么最好返回统计数(0 或 1) 。因为并不是所有语句都需 要参数,这些方法都是有不同重载版本的,它们可以不需要参数对象。
最后,还有查询方法的三个高级版本,它们允许你限制返回行数的范围,或者提供自定 义结果控制逻辑,这通常用于大量的数据集合。
RowBounds 参数会告诉 MyBatis 略过指定数量的记录,还有限制返回结果的数量。 RowBounds 类有一个构造方法来接收 offset 和 limit,否则是不可改变的。
不同的驱动会实现这方面的不同级别的效率。对于最佳的表现,使用结果集类型的 SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE(或句话说:不是 FORWARD_ONLY)。
ResultHandler 参数允许你按你喜欢的方式处理每一行。你可以将它添加到 List 中,创 建 Map, 或抛出每个结果而不是只保留总计。 Set 你可以使用 ResultHandler 做很多漂亮的事, 那就是 MyBatis 内部创建结果集列表。
它的接口很简单。
ResultContext 参数给你访问结果对象本身的方法, 大量结果对象被创建, 你可以使用布 尔返回值的 stop()方法来停止 MyBatis 加载更多的结果。
批量立即更新方法(Flush Method)
有一个方法可以刷新(执行)存储在JDBC驱动类中的批量更新语句。当你将ExecutorType.BATCH作为ExecutorType使用时可以采用此方法。
源代码分析
A 、 输入流的关闭
在输入流对象使用完毕后,不用手工关闭,因为输入流在使用完毕后,SqlSessionFactoryBuilder 对象的 builder()方法自动将输入流关闭。
B 、 SqlSession 的创建
SqlSession 的创建,需要使用 SqlSessionFactory 对象的 openSession()方法。
SqlSessionFacotry 接口的实现类是 DefaultSqlSessionFactory.
打开 openSession()的源代码
从以上代码可以看出,无参数的 openSession()方法,将事务的自动提交直接赋值为 false.而所谓的创建 SqlSession。就是加载主配置文件,创建了一个执行器对象(用于执行映射文件中的 SQL 语句),初始化了一个 DB 数据被修改的标志变量 dirty,关闭了事务的自动提交功能。
C 、 增删改的执行
对于 SqlSession 的 insert(), update(),delete(),其底层均是调用执行了 update()方法。
从以上的源代码可知,无论执行增,删还是修改,均将 dirty 变量设置为 true,且在获
取到映射文件中指定的 id 的 SQL 语句后,由执行器 executor 执行。
D 、 SqlSession 的提交 commit()
从代码中可知,isCommitOrRollbackRequired(force)方法的返回值为 true.继承跟踪executor 的 commit()方法。
由以上代码可知,执行 SqlSession 的无参 commit()方法,最终会将事务提交。
E 、 SqlSession 的关闭
由以上代码可知,isCommitOrRollbackRequired(force)方法的返回值为 true.继续跟踪executor 的 close()方法。
再跟踪 Executor 接口的 BaseExecutor 抽象类的 close()方法。
从以上代码可知,在 SqlSession 进行关闭时,会将事务回滚后关闭。所以对于 Mybatis程序,无需通过显示的对 SqlSession 进行回滚,达到事务回滚的目的。