5.Java API
5.1 典型应用的目录结构
在我们深入 Java API 之前,理解关于目录结构的最佳实践是很重要的。MyBatis 非常灵活, 你可以用你自己的文件来做几乎所有的事情。 但是对于任一框架, 都有一些最佳的方式。
让我们看一下典型应用的目录结构:
/my_application
/bin
/devlib
/lib <-- MyBatis *.jar文件在这里。
/src
/org/myapp/
/action
/data <-- MyBatis配置文件在这里, 包括映射器类, XML配置, XML映射文件。
/mybatis-config.xml
/BlogMapper.java
/BlogMapper.xml
/model
/service
/view
/properties <-- 在你XML中配置的属性 文件在这里。
/test
/org/myapp/
/action
/data
/model
/service
/view
/properties
/web
/WEB-INF
/web.xml
5.2 SqlSessions
使用 MyBatis 的主要 Java 接口就是 SqlSession。
5.2.1 如果获取 一个 SqlSession 实例
SqlSessions 是由 SqlSessionFactory 实例创建的。SqlSessionFactory 对 象 包 含 创 建 SqlSession 实 例 的 所 有 方 法 。 而 SqlSessionFactory 本 身 是 由 SqlSessionFactoryBuilder 创建的,它可以从 XML 配置,注解或手动配置 Java 来创建 SqlSessionFactory。
NOTE:如果使用的是Spring或者Guice框架,SqlSessions由DI框架生成和注入。
5.2.2 语句执行方法
这些方法被用来执行定义在 SQL 映射的 XML 文件中的 SELECT,INSERT,UPDA E T 和 DELETE 语句。它们都会自行解释,每一句都使用语句的 ID 属性和参数对象,参数可以 是原生类型(自动装箱或包装类) ,JavaBean,POJO 或 Map。
T selectOne(String statement, Object parameter)
List selectList(String statement, Object parameter)
Map selectMap(String statement, Object parameter, String mapKey)
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) 。因为并不是所有语句都需 要参数,这些方法都是有不同重载版本的,它们可以不需要参数对象。
T selectOne(String statement)
List selectList(String statement)
Map selectMap(String statement, String mapKey)
int insert(String statement)
int update(String statement)
int delete(String statement)
最后,还有查询方法的三个高级版本,它们允许你限制返回行数的范围,或者提供自定 义结果控制逻辑,这通常用于大量的数据集合。
<E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds)
void select (String statement, Object parameter, ResultHandler<T> handler)
void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler<T> handler)
RowBounds 参数会告诉 MyBatis 略过指定数量的记录,还有限制返回结果的数量。 RowBounds 类有一个构造方法来接收 offset 和 limit,否则是不可改变的。
int offset = 100;
int limit = 25;
RowBounds rowBounds = new RowBounds(offset, limit);
不同的驱动会实现这方面的不同级别的效率。对于最佳的表现,使用结果集类型的 SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE(或句话说:不是 FORWARD_ONLY)。
ResultHandler 参数允许你按你喜欢的方式处理每一行。你可以将它添加到 List 中,创 建 Map, 或抛出每个结果而不是只保留总计。 Set 你可以使用 ResultHandler 做很多漂亮的事, 那就是 MyBatis 内部创建结果集列表。
它的接口很简单。
package org.apache.ibatis.session;
public interface ResultHandler {
void handleResult(ResultContext context);
}
ResultContext 参数给你访问结果对象本身的方法, 大量结果对象被创建, 你可以使用布 尔返回值的 stop()方法来停止 MyBatis 加载更多的结果。
5.2.3 事务控制方法
控制事务范围有四个方法。 当然, 如果你已经选择了自动提交或你正在使用外部事务管 理器,这就没有任何效果了。然而,如果你正在使用 JDBC 事务管理员,由 Connection 实 例来控制,那么这四个方法就会派上用场:
void commit()
void commit(boolean force)
void rollback()
void rollback(boolean force)
默认情况下 MyBatis 不会自动提交事务, 除非它侦测到有插入, 更新或删除操作改变了 数据库。如果你已经做出了一些改变而没有使用这些方法,那么你可以传递 true 到 commit 和 rollback 方法来保证它会被提交(注意,你不能在自动提交模式下强制 session,或者使用 了外部事务管理器时) 。很多时候你不用调用 rollback(),因为如果你没有调用 commit 时 MyBatis 会替你完成。然而,如果你需要更多对多提交和回滚都可能的 session 的细粒度控 制,你可以使用回滚选择来使它成为可能。
NOTE: Spring提供声明式事务处理
5.2.4 清理 Session 级的缓存
void clearCache()
SqlSession 实例有一个本地缓存在执行 update,commit,rollback 和 close 时被清理。要 明确地关闭它 ,你可以调用 clearCache()。
5.2.5 确保 SqlSession 被关闭
你必须保证的最重要的事情是你要关闭所打开的任何 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();
}
或者:
try (SqlSession session = sqlSessionFactory.openSession()) {
// following 3 lines pseudocode for "doing some work"
session.insert(...);
session.update(...);
session.delete(...);
session.commit();
}
5.2.6 使用映射器
一个更通用的方式来执行映射语句是使用映射器类。 一个映射器类就是一个简单 的接口,其中的方法定义匹配于 SqlSession 方法。下面的示例展示了一些方法签名和它们是 如何映射到 SqlSession 的。
public interface AuthorMapper {
// (Author) selectOne("selectAuthor",5);
Author selectAuthor(int id);
// (List) selectList("selectAuthors")
List selectAuthors();
// (Map) selectMap("selectAuthors", "id")
@MapKey("id")
Map selectAuthors();
// insert("insertAuthor", author)
int insertAuthor(Author author);
// updateAuthor("updateAuthor", author)
int updateAuthor(Author author);
// delete("deleteAuthor",5)
int deleteAuthor(int id);
}
- 注解
这个例子展示了如何使用 @SelectKey 注解来在插入前读取数据库序列的值:
@Insert("insert into table3 (id, name) values(#{nameId}, #{name})")
@SelectKey(statement="call next value for TestSequence", keyProperty="nameId", before=true, resultType=int.class)
int insertTable3(Name name);
这个例子展示了如何使用 @SelectKey 注解来在插入后读取数据库识别列的值:
@Insert("insert into table2 (name) values(#{name})")
@SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class)
int insertTable2(Name name);