MyBatis 核心对象

使用 MyBatis 的主要 Java 接口就是SqlSession。可以通过这个接口来执行命令,获取映射器实例和管理事务。

SqlSessions是由SqlSessionFactory实例创建的。SqlSessionFactory对象包含创建SqlSession实例的各种方法。而SqlSessionFactory本身是由SqlSessionFactoryBuilder创建的,它可以从 XML、注解或 Java 配置代码来创建SqlSessionFactory

当 Mybatis 与一些依赖注入框架(如 Spring )搭配使用时,SqlSession将被依赖注入框架创建并注入,所以不需要使用SqlSessionFactoryBuilder或者SqlSessionFactory

SqlSessionFactoryBuilder

SqlSessionFactoryBuilder有五个build()方法,每一种都允许你从不同的资源中创建一个SqlSessionFactory实例。

1
2
3
4
5
SqlSessionFactory build(InputStream inputStream)
SqlSessionFactory build(InputStream inputStream, String environment)
SqlSessionFactory build(InputStream inputStream, Properties properties)
SqlSessionFactory build(InputStream inputStream, String env, Properties props)
SqlSessionFactory build(Configuration config)

第一种方法是最常用的,它接受一个指向 XML 文件的InputStream实例。可选的参数是environmentpropertiesenvironment决定加载哪种环境,包括数据源和事务管理器。

如果你调用了带environment参数的build方法,那么 MyBatis 将使用该环境对应的配置。当然,如果你指定了一个无效的环境,会收到错误。如果你调用了不带environment参数的build方法,那么就会使用默认的环境配置。

如果你调用了接受properties实例的方法,那么 MyBatis 就会加载这些属性,并在配置中提供使用。绝大多数场合下,可以用${propName}形式引用这些配置值。

mybatis-config.xml中,可以引用属性值,也可以直接指定属性值。因此,理解属性的优先级是很重要的。

如果一个属性存在于下面的多个位置,那么 MyBatis 将按照以下顺序来加载它们:

  • 首先,读取在properties元素体中指定的属性;
  • 其次,读取在properties元素的类路径resourceurl指定的属性,且会覆盖已经指定了的重复属性;
  • 最后,读取作为方法参数传递的属性,且会覆盖已经从properties元素体和resourceurl属性中加载了的重复属性。

因此,通过方法参数传递的属性的优先级最高,resourceurl指定的属性优先级中等,在properties元素体中指定的属性优先级最低。

1
2
3
4
String resource = "org/mybatis/builder/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsReader(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);

SqlSessionFactory

SqlSessionFactory有六个方法创建SqlSession实例。

1
2
3
4
5
6
7
8
9
SqlSession openSession()
SqlSession openSession(boolean autoCommit)
SqlSession openSession(Connection connection)
SqlSession openSession(TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType)
SqlSession openSession(ExecutorType execType, boolean autoCommit)
SqlSession openSession(ExecutorType execType, Connection connection)
Configuration getConfiguration();

默认的openSession()方法没有参数,它会创建具备如下特性的SqlSession

  • 事务作用域将会开启(也就是不自动提交)。
  • 将由当前环境配置的DataSource实例中获取Connection对象。
  • 事务隔离级别将会使用驱动或数据源的默认设置。
  • 预处理语句不会被复用,也不会批量处理更新。

autoCommit可选参数传递true值即可开启自动提交功能。若要使用自己的Connection实例,传递一个Connection实例给connection参数即可。注意,我们没有提供同时设置ConnectionautoCommit的方法,这是因为 MyBatis 会依据传入的Connection来决定是否启用autoCommit。对于事务隔离级别,MyBatis 使用了一个 Java 枚举包装器来表示,称为TransactionIsolationLevel,事务隔离级别支持 JDBC 的五个隔离级别(NONE、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READSERIALIZABLE),并且与预期的行为一致。

ExecutorType这个枚举类型定义了三个值:

  • ExecutorType.SIMPLE:该类型的执行器没有特别的行为。它为每个语句的执行创建一个新的预处理语句。
  • ExecutorType.REUSE:该类型的执行器会复用预处理语句。
  • ExecutorType.BATCH:该类型的执行器会批量执行所有更新语句,如果SELECT在多个更新中间执行,将在必要时将多条更新语句分隔开来,以方便理解。

SqlSessionFactory中还有一个方法就是getConfiguration()。这个方法会返回一个Configuration实例,你可以在运行时使用它来检查 MyBatis 的配置。

SqlSession

SqlSession类包含了所有执行语句、提交或回滚事务以及获取映射器实例的方法。SqlSession类的方法超过了 20 个,为了方便理解,我们将它们分成几种组别。

语句执行方法

这些方法被用来执行定义在 SQL 映射 XML 文件中的SELECT、INSERT、UPDATEDELETE语句。每一方法都接受语句的 ID 以及参数对象,参数可以是原始类型(支持自动装箱或包装类)、JavaBeanPOJOMap

1
2
3
4
5
6
<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
<K,V> Map<K,V> 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)

selectOneselectList的不同仅仅是selectOne必须返回一个对象或null值。如果返回值多于一个,就会抛出异常。如果你不知道返回对象会有多少,请使用selectList。如果需要查看某个对象是否存在,最好的办法是查询一个count 值(0 或 1)。selectMap稍微特殊一点,它会将返回对象的其中一个属性作为key值,将对象作为value值,从而将多个结果集转为Map类型值。由于并不是所有语句都需要参数,所以这些方法都具有一个不需要参数的重载形式。

insert、update以及delete方法返回的值表示受该语句影响的行数。

1
2
3
4
5
6
<T> T selectOne(String statement)
<E> List<E> selectList(String statement)
<K,V> Map<K,V> selectMap(String statement, String mapKey)
int insert(String statement)
int update(String statement)
int delete(String statement)

最后,还有select方法的三个高级版本,它们允许你限制返回行数的范围,或是提供自定义结果处理逻辑,通常在数据集非常庞大的情形下使用。

1
2
3
4
<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类的offsetlimit值只有在构造函数时才能传入,其它时候是不能修改的。

1
2
3
int offset = 100;
int limit = 25;
RowBounds rowBounds = new RowBounds(offset, limit);

数据库驱动决定了略过记录时的查询效率。为了获得最佳的性能,建议将ResultSet类型设置为SCROLL_SENSITIVESCROLL_INSENSITIVE(换句话说:不要使用FORWARD_ONLY)。

ResultHandler参数允许自定义每行结果的处理过程。你可以将它添加到List中、创建MapSet,甚至丢弃每个返回值,只保留计算后的统计结果。你可以使用ResultHandler做很多事,这其实就是 MyBatis 构建 结果列表的内部实现办法。

ResultHandler会在存储过程的REFCURSOR输出参数中传递使用的CALLABLE语句。

1
2
3
4
package org.apache.ibatis.session;
public interface ResultHandler<T> {
void handleResult(ResultContext<? extends T> context);
}

ResultContext参数允许你访问结果对象和当前已被创建的对象数目,另外还提供了一个返回值为Booleanstop方法,你可以使用此stop方法来停止 MyBatis 加载更多的结果。

使用ResultHandler的时候需要注意以下两个限制:

  • 使用带ResultHandler参数的方法时,收到的数据不会被缓存。
  • 当使用高级的结果映射集(resultMap)时,MyBatis 很可能需要数行结果来构造一个对象。如果你使用了ResultHandler,你可能会接收到关联(association)或者集合(collection)中尚未被完整填充的对象。

立即批量更新方法

当你将ExecutorType设置为ExecutorType.BATCH时,可以使用这个方法清除(执行)缓存在 JDBC 驱动类中的批量更新语句。

1
List<BatchResult> flushStatements()

事务控制方法

有四个方法用来控制事务作用域。当然,如果你已经设置了自动提交或你使用了外部事务管理器,这些方法就没什么作用了。然而,如果你正在使用由Connection实例控制的 JDBC 事务管理器,那么这四个方法就会派上用场:

1
2
3
4
void commit()
void commit(boolean force)
void rollback()
void rollback(boolean force)

默认情况下 MyBatis 不会自动提交事务,除非它侦测到调用了插入、更新或删除方法改变了数据库。如果你没有使用这些方法提交修改,那么你可以在commitrollback方法参数中传入true值,来保证事务被正常提交(注意,在自动提交模式或者使用了外部事务管理器的情况下,设置force值对session无效)。大部分情况下你无需调用rollback(),因为 MyBatis 会在你没有调用commit时替你完成回滚操作。不过,当你要在一个可能多次提交或回滚的session中详细控制事务,回滚操作就派上用场了。

本地缓存

Mybatis 使用到了两种缓存:本地缓存(local cache)和二级缓存(second level cache)。

每当一个新session被创建,MyBatis 就会创建一个与之相关联的本地缓存。任何在session执行过的查询结果都会被保存在本地缓存中,所以,当再次执行参数相同的相同查询时,就不需要实际查询数据库了。本地缓存将会在做出修改、事务提交或回滚,以及关闭session时清空。

默认情况下,本地缓存数据的生命周期等同于整个session的周期。由于缓存会被用来解决循环引用问题和加快重复嵌套查询的速度,所以无法将其完全禁用。但是你可以通过设置localCacheScope=STATEMENT来只在语句执行时使用缓存。

注意,如果localCacheScope被设置为SESSION,对于某个对象,MyBatis 将返回在本地缓存中唯一对象的引用。对返回的对象(例如list)做出的任何修改将会影响本地缓存的内容,进而将会影响到在本次session中从缓存返回的值。因此,不要对 MyBatis 所返回的对象作出更改,以防后患。

你可以随时调用以下方法来清空本地缓存:

1
void clearCache()

确保 SqlSession 被关闭

1
void close()

对于你打开的任何session,你都要保证它们被妥善关闭,这很重要。

SqlSessionFactory一样,你可以调用当前使用的SqlSessiongetConfiguration方法来获得Configuration实例。

1
Configuration getConfiguration()

使用映射器

1
<T> T getMapper(Class<T> type)

上述的各个insert、update、deleteselect方法都很强大,但也有些繁琐,它们并不符合类型安全,对你的 IDE 和单元测试也不是那么友好。因此,使用映射器类来执行映射语句是更常见的做法。

一个映射器类就是一个仅需声明与SqlSession方法相匹配方法的接口。下面的示例展示了一些方法签名以及它们是如何映射到SqlSession上的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface AuthorMapper {
// (Author) selectOne("selectAuthor",5);
Author selectAuthor(int id);
// (List<Author>) selectList(“selectAuthors”)
List<Author> selectAuthors();
// (Map<Integer,Author>) selectMap("selectAuthors", "id")
@MapKey("id")
Map<Integer, Author> selectAuthors();
// insert("insertAuthor", author)
int insertAuthor(Author author);
// updateAuthor("updateAuthor", author)
int updateAuthor(Author author);
// delete("deleteAuthor",5)
int deleteAuthor(int id);
}

总之,每个映射器方法签名应该匹配相关联的SqlSession方法,字符串参数ID无需匹配。而是由方法名匹配映射语句的ID

此外,返回类型必须匹配期望的结果类型,返回单个值时,返回类型应该是返回值的类,返回多个值时,则为数组或集合类,另外也可以是游标(Cursor)。所有常用的类型都是支持的,包括:原始类型、Map、POJOJavaBean

映射器接口不需要去实现任何接口或继承自任何类。只要方法签名可以被用来唯一识别对应的映射语句就可以了。

映射器接口可以继承自其他接口。在使用 XML 来绑定映射器接口时,保证语句处于合适的命名空间中即可。唯一的限制是,不能在两个具有继承关系的接口中拥有相同的方法签名(这是潜在的危险做法,不可取)。

你可以传递多个参数给一个映射器方法。在多个参数的情况下,默认它们将会以param加上它们在参数列表中的位置来命名,比如:#{param1}、#{param2}等。如果你想(在有多个参数时)自定义参数的名称,那么你可以在参数上使用@Param("paramName")注解。

你也可以给方法传递一个RowBounds实例来限制查询结果。

打赏
  • Copyrights © 2017-2023 WSQ
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信