在 MyBatis 中 SqlSession 类是非常强大的一个类。在这里你会发现所有 SQL 语句执行方法、事务提交、事务回滚均来自 SqlSession 类。
注意:每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不能被共享,且是线程不安全的,因此最佳的范围是请求或方法范围。
绝对不能将 SqlSession 实例的引用放在一个类的静态字段甚至是实例字段中。也绝不能将 SqlSession 实例的引用放在任何类型的管理范围中,比如 Serlvet 架构中的 HttpSession。
如果你现在正用任意的 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的范围内。
换句话说,基于收到的 HTTP 请求,你可以打开了一个 SqlSession,然后返回响应,就可以关闭它了。
关闭 Session 很重要,你应该确保使用 finally 块来关闭它。下面的示例就是一个确保 SqlSession 关闭的基本模式:
SqlSession session = sqlSessionFactory.openSession(); try { // do work } finally { session.close(); }
在你的代码中一贯地使用这种模式,将会保证所有数据库资源都正确地关闭(假设你没有通过你自己的连接关闭,这会给 MyBatis 造成一种迹象表明你要自己管理连接资源)。
这些方法被用来执行定义在 SQL 映射的 XML 文件中的 SELECT,INSERT,UPDATE 和 DELETE 语句。它们都会自行解释,每一句都使用语句的 ID 属性和参数对象,参数可以是原生类型(自动装箱或包装类),JavaBean,POJO 或 Map。
// 查询数据,只能返回一条记录,多于一条抛出错误 Object selectOne(String statement, Object parameter) // 查询数据,返回一个列表 List selectList(String statement, Object parameter) // 插入数据到数据库 int insert(String statement, Object parameter) // 更新数据库数据 int update(String statement, Object parameter) // 删除数据库数据 int delete(String statement, Object parameter)
selectOne 和 selectList 的不同仅仅是 selectOne 必须返回一个对象。如果多余一个,或者没有返回(或返回了 null),那么就会抛出异常。如果你不知道需要多少对象,使用 selectList。
如果你想检查一个对象是否存在,那么最好返回统计数(0 或 1)。因为并不是所有语句都需要参数,上面这些方法都有不同版本的重载,它们可以不需要参数对象。如下:
// 无任何参数,获取单条数据 Object selectOne(String statement) // 无任何参数,获取数据列表 List selectList(String statement) // 无任何参数,插入数据 int insert(String statement) // 无任何参数,更新数据 int update(String statement) // 无任何参数,删除数据 int delete(String statement)
最后,还有三个高级版本的查询方法,它们允许你限制返回行数的范围,或者提供自定义结果控制逻辑,这通常用于大量的数据集合。
// 分页查询 List selectList(String statement, Object parameter, RowBounds rowBounds) // 自定义结果处理器 void select(String statement, Object parameter, ResultHandler handler) // 分页和自定义结果处理器 void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler)
RowBounds 参数会告诉 MyBatis 略过指定数量的记录,还有限制返回结果的数量。RowBounds 类用一个构造方法来接收 offset 和 limit,否则是不可改变的。
int offset = 100; int limit = 25; RowBounds rowBounds = new RowBounds(offset, limit);
ResultHandler 参数允许你按你喜欢的方式处理每一行。你可以将它添加到 List 中,创建Map,Set 或抛出每个结果而不是只保留总计。你可以使用 ResultHandler 做很多漂亮的事,那就是 MyBatis 内部创建结果集列表。它的接口很简单。
package org.mybatis.executor.result; public interface ResultHandler { void handleResult(ResultContext context); }
ResultContext 参数提供了访问结果对象自身的方法,大量结果对象被创建,你可以使用返回布尔值的 stop() 方法来停止 MyBatis 加载更多的结果。
控制事务范围有四个方法。当然,如果你已经选择了自动提交或你正在使用外部事务管理器,这就没有任何效果了。然而,如果你正在使用 JDBC 事务管理器,由 Connection 实例来控制,那么这四个方法就会派上用场:
// 提交事务 void commit() void commit(boolean force) // 回滚事务 void rollback() void rollback(boolean force)
默认情况下 MyBatis 不会自动提交事务,除非它侦测到有插入,更新或删除操作改变了数据库。如果你已经改变了数据库数据,而没有使用插入、更新或删除方法,那么你可以传递 true 到 commit 和 rollback 方法来保证它会被提交。很多时候你不用调用 rollback(),因为如果你没有调用 commit 时 MyBatis 会替你完成。然而,如果你需要对 session 更细粒度的事务提交和回滚控制,你可以选择使用回滚来使它成为可能。
void clearCache()
SqlSession 实例有一个本地缓存,在执行 update,commit,rollback 和 close 时被清理。要明确地关闭它,你可以调用 clearCache() 方法。
void close()
你必须保证关闭所打开的任何 session。保证做到这点的最佳方式是下面的工作模式:
SqlSession session = sqlSessionFactory.openSession(); try { // following 3 lines pseudocod for “doing some work” session.insert(…); session.update(…); session.delete(…); session.commit(); } finally { session.close(); }
注意:就像 SqlSessionFactory,你可以通过调用 getConfiguration() 方法获得 SqlSession 使用的 Configuration 实例。
<T> T getMapper(Class<T> type)
上述的各个 insert,update,delete 和 select 方法都很强大,但也有些繁琐,没有类型安全,对于你的 IDE 也没有帮助。在上面的“MyBatis 入门实例”中我们已经看到了一个使用映射器的示例。
因此,一个更通用的方式来执行映射语句是使用映射器类。一个映射器类就是一个简单的接口,其中的方法定义匹配于 SqlSession 方法。下面的示例展示了一些方法签名和它们是如何映射到 SqlSession 的。
public interface AuthorMapper { // (Author) selectOne(“selectAuthor”,5); Author selectAuthor(int id); // (List<Author>) selectList(“selectAuthors”) List<Author> selectAuthors(); // insert(“insertAuthor”, author) void insertAuthor(Author author); // updateAuthor(“updateAuhor”, author) void updateAuthor(Author author); // delete(“deleteAuthor”,5) void deleteAuthor(int id); }
总之,每个映射器方法签名应该匹配相关联的 SqlSession 方法,而没有字符串参数 ID。相反,方法名必须匹配映射语句的 ID。
此外,返回类型必须匹配期望的结果类型。所有常用的类型都是支持的,包括:原生类型,Map,POJO 和 JavaBean。
映射器接口不需要去实现任何接口或扩展任何类。只要方法前面可以被用来唯一标识对应的映射语句就可以了。
映射器接口可以扩展其他接口。当使用 XML 来构建映射器接口时要保证在合适的命名空间中有语句。而且,唯一的限制就是你不能在两个继承关系的接口中有相同的方法签名(这也是不好的想法)。
你可以传递多个参数给一个映射器方法。如果你这样做了,默认情况下它们将会以它们在参数列表中的位置来命名,比如:#{1}, #{2} 等。如果你想改变参数的名称(只在多参数情况下),那么你可以在参数上使用 @Param("paramName") 注解。