> 文档中心 > 【MyBatis基础教学四】- Java API 【续精品3-下/4】

【MyBatis基础教学四】- Java API 【续精品3-下/4】


【MyBatis基础教学三】- SQL 映射的 XML 文件 【精品-下】

SQL 映射的 XML 文件

在这里插入图片描述

6、缓存

MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。MyBatis 3中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。

默认情况下是没有开启缓存的,除了局部的 session 缓存,可以增强变现而且处理循环
依赖也是必须的。要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:
cache/
字面上看就是这样。这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句将会被缓存。

  • 映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。

  • 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来回。

  • 根据时间表(比如 no Flush Interval,没有刷新间隔),缓存不会以任何时间顺序
    来刷新。

  • 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。

  • 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
    所有的这些属性都可以通过缓存元素的属性来修改。比如:

<cache   eviction="FIFO"   flushInterval="60000"   size="512"   readOnly="true"/>

这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。

可用的收回策略有:

  • LRU – 最近最少使用的:移除最长时间不被使用的对象。
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
  • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。默认的是 LRU。

flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。

使用自定义缓存

除了这些自定义缓存的方式,你也可以通过实现你自己的缓存或为其他第三方缓存方案
创建适配器来完全覆盖缓存行为。

<cache type=”com.domain.something.MyCustomCache”/>

这个示例展示了如何使用一个自定义的缓存实现。type 属性指定的类必须实现org.mybatis.cache.Cache 接口。这个接口是 MyBatis 框架中很多复杂的接口之一,但是简单给定它做什么就行。

public interface Cache {   String getId();   int getSize();   void putObject(Object key, Object value);   Object getObject(Object key);   boolean hasKey(Object key);   Object removeObject(Object key);   void clear();   ReadWriteLock getReadWriteLock();}

要配置你的缓存,简单和公有的 JavaBeans 属性来配置你的缓存实现,而且是通过 cache元素来传递属性,比如,下面代码会在你的缓存实现中调用一个称为“setCacheFile(String file)”的方法:

<cache type=”com.domain.something.MyCustomCache”>   <property name=”cacheFile” value=/tmp/my-custom-cache.tmp”/></cache>

你可以使用所有简单类型作为 JavaBeans 的属性,MyBatis 会进行转换。

记得缓存配置和缓存实例是绑定在 SQL 映射文件的命名空间是很重要的。因此,所有在相同命名空间的语句正如绑定的缓存一样。语句可以修改和缓存交互的方式,或在语句的语句的基础上使用两种简单的属性来完全排除它们。默认情况下,语句可以这样来配置:

<select ... flushCache=false” useCache=true/><insert ... flushCache=true/><update ... flushCache=true/><delete ... flushCache=true/>

因为那些是默认的,你明显不能明确地以这种方式来配置一条语句。相反,如果你想改变默认的行为,只能设置 flushCache 和 useCache 属性。比如,在一些情况下你也许想排除从缓存中查询特定语句结果,或者你也许想要一个查询语句来刷新缓存。相似地,你也许有一些更新语句依靠执行而不需要刷新缓存。

参照缓存

回想一下上一节内容,这个特殊命名空间的唯一缓存会被使用或者刷新相同命名空间内的语句。也许将来的某个时候,你会想在命名空间中共享相同的缓存配置和实例。在这样的情况下你可以使用 cache-ref 元素来引用另外一个缓存。

<cache-ref namespace=”com.someone.application.data.SomeMapper”/>

7、动态 SQL

MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力。如果你有使用 JDBC 或其他相似框架的经验,你就明白条件地串联 SQL 字符串在一起是多么的痛苦,确保不能忘了空格或在列表的最后省略逗号。动态 SQL 可以彻底处理这种痛苦。

通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语言来改进这种情形,这种语言可以被用在任意映射的 SQL 语句中。

动态 SQL 元素和使用 JSTL 或其它相似的基于 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多的元素需要来了解。MyBatis 3 大大提升了它们,现在用不到原先一半的元素就能工作了。MyBatis 采用功能强大的基于 OGNL 的表达式来消除其他元素。

  • if
  • choose(when,otherwise)
  • trim(where,set)
  • foreach
7.1 if

在动态 SQL 中所做的最通用的事情是包含部分 where 字句的条件。比如:

<select id=”findActiveBlogWithTitleLike”   parameterType=”Blog” resultType=”Blog”>   SELECT * FROM BLOG   WHERE state =ACTIVE<if test=”title != null>      AND title like #{title}   </if></select>
这条语句会提供一个可选的文本查找功能。如果你没有传递 title,那么所有激活的博客都会被返回。但是如果你传递了 title,那么就会查找相近的 title(对于敏锐的检索,这中情况下你的参数值需要包含任意的遮掩或通配符)的博客。
假若我们想可选地搜索 title 和 author 呢?首先,要改变语句的名称让它有意义。然后简单加入另外的一个条件。
<select id=”findActiveBlogLike”      parameterType=”Blog” resultType=”Blog”>   SELECT * FROM BLOG WHERE state =ACTIVE<if test=”title != null>      AND title like #{title}   </if>   <if test=”author != null and author.name != null>      AND title like #{author.name}   </if></select>
7.2 choose, when, otherwise

有时我们不想应用所有的条件,相反我们想选择很多情况下的一种。和 Java 中的 switch语句相似,MyBatis 提供 choose 元素。

我们使用上面的示例,但是现在我们来搜索当 title 提供时仅有 title 条件,当 author 提供时仅有 author 条件。如果二者都没提供,只返回 featured blogs(也许是由管理员策略地选择的结果列表,而不是返回大量没有意义的,随机的博客结果列表)。

<select id=”findActiveBlogLike”      parameterType=”Blog” resultType=”Blog”>   SELECT * FROM BLOG WHERE state =ACTIVE<choose>      <when test=”title != null>  AND title like #{title}      </when>      <when test=”author != null and author.name != null>  AND title like #{author.name}      </when>      <otherwise>  AND featured = 1      </otherwise>   </choose></select>
7.3 trim, where, set

前面的例子已经方便地处理了一个臭名昭著的动态 SQL 问题。要考虑我们回到“if”示例后会发生什么,但是这次我们将“ACTIVE = 1”也设置成动态的条件。

<select id=”findActiveBlogLike”parameterType=”Blog” resultType=”Blog”>   SELECT * FROM BLOG   WHERE   <if test=”state != null>      state = #{state}   </if>   <if test=”title != null>      AND title like #{title}   </if>   <if test=”author != null and author.name != null>      AND title like #{author.name}   </if></select>

如果这些条件都没有匹配上将会发生什么?这条 SQL 结束时就会成这样:
SELECT * FROM BLOG
WHERE
这会导致查询失败。如果仅仅第二个条件匹配是什么样的?这条 SQL 结束时就会是这样:
SELECT * FROM BLOG
WHERE
AND title like „someTitle‟

这个查询也会失败。这个问题不能简单的用条件来解决,如果你从来没有这样写过,那么你以后也不会这样来写。

MyBatis 有一个简单的处理,这在 90%的情况下都会有用。而在不能使用的地方,你可以自定义处理方式。加上一个简单的改变,所有事情都会顺利进行:

<select id=”findActiveBlogLike”parameterType=”Blog” resultType=”Blog”>   SELECT * FROM BLOG   <where>      <if test=”state != null>  state = #{state}      </if>      <if test=”title != null>  AND title like #{title}      </if>      <if test=”author != null and author.name != null>  AND title like #{author.name}      </if>   </where></select>

where 元素知道如果由被包含的标记返回任意内容,就仅仅插入“WHERE”。而且,如果以“AND”或“OR”开头的内容,那么就会跳过 WHERE 不插入。

如果 where 元素没有做出你想要的,你可以使用 trim 元素来自定义。比如,和 where元素相等的 trim 元素是:

<trim prefix="WHERE" prefixOverrides="AND |OR "></trim>

overrides 属性采用管道文本分隔符来覆盖,这里的空白也是重要的。它的结果就是移除在 overrides 属性中指定的内容,插入在 with 属性中的内容。

和动态更新语句相似的解决方案是 set。set 元素可以被用于动态包含更新的列,而不包含不需更新的。比如:

<update id="updateAuthorIfNecessary"parameterType="domain.blog.Author">   update Author   <set>      <if test="username != null">username=#{username},</if>      <if test="password != null">password=#{password},</if>      <if test="email != null">email=#{email},</if>      <if test="bio != null">bio=#{bio}</if>   </set>   where id=#{id}</update>

这里,set 元素会动态前置 SET 关键字,而且也会消除任意无关的逗号,那也许在应用条件之后来跟踪定义的值。

如果你对和这相等的 trim 元素好奇,它看起来就是这样的:

<trim prefix="SET" suffixOverrides=","></trim>

注意这种情况下我们覆盖一个后缀,而同时也附加前缀。

7.4、foreach

另外一个动态SQL通用的必要操作是迭代一个集合,通常是构建在IN条件中的。比如:

<select id="selectPostIn" resultType="domain.blog.Post">   SELECT *   FROM POST P   WHERE ID in   <foreach item="item" index="index" collection="list"      open="(" separator="," close=")">      #{item}   </foreach></select>

foreach 元素是非常强大的,它允许你指定一个集合,声明集合项和索引变量,它们可以用在元素体内。它也允许你指定开放和关闭的字符串,在迭代之间放置分隔符。这个元素是很智能的,它不会偶然地附加多余的分隔符。

注意:你可以传递一个 List 实例或者数组作为参数对象传给 MyBatis。当你这么做的时候,MyBatis 会自动将它包装在一个 Map 中,用名称作为键。List 实例将会以“list”作为键,而数组实例将会以“array”作为键。这个部分是对关于 XML 配置文件和 XML 映射文件的而讨论的。下一部分将详细讨论Java API,所以你可以得到你已经创建的最有效的映射。


8、Java API

既然你已经知道如何配置 MyBatis 和创建映射文件,你就已经准备好来提升技能了。MyBatis 的 Java API 就是你收获你所做的努力的地方。正如你即将看到的,和 JDBC 相比,MyBatis 很大程度简化了你的代码而且保持简洁,很容易理解和维护。MyBatis 3 已经引入了很多重要的改进来使得 SQL 映射更加优秀。
8.1、应用 目录结构

在我们深入 Java API 之前,理解关于目录结构的最佳实践是很重要的。MyBatis 非常灵活,你可以用你自己的文件来做几乎所有的事情。但是对于任一框架,都有一些最佳的方式。让我们看一下典型应用的目录结构:

/my_application   /bin   /devlib   /lib  ← MyBatis *.jar文件在这里。   /src      /org/myapp/  /action  /data ← MyBatis配置文件在这里,      /SqlMapConfig.xml包括映射器类,XML配置,      /BlogMapper.java XML映射文件。      /BlogMapper.xml  /model  /service  /view      /properties   ← 在你XML中配置的属性   /test      文件在这里。      /org/myapp/  /action  /data  /model  /service  /view      /properties   /web      要记得这只是参照,而不是      /WEB-INF      要求,可能其他人会感谢你  /web.xml   使用了通用的目录结构。

这部分内容剩余的示例将假设你使用了这种目录结构。

8.2、SqlSessions

使用 MyBatis 的主要 Java 接口就是 SqlSession。尽管你可以使用这个接口执行命令,获取映射器和管理事务。我们会讨论 SqlSession 本身更多,但是首先我们还是要了解如果获取一个 SqlSession 实例。SqlSessions 是由 SqlSessionFactory 实例创建的。SqlSessionFactory 对象 包 含 创 建 SqlSession 实 例 的 所 有 方 法 。 而 SqlSessionFactory 本 身 是 由SqlSessionFactoryBuilder 创建的,它可以从 XML 配置,注解或手动配置 Java 来创建SqlSessionFactory。

1、SqlSessionFactoryBuilder

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

SqlSessionFactory build(Reader reader)SqlSessionFactory build(Reader reader, String environment)SqlSessionFactory build(Reader reader, Properties properties)SqlSessionFactory build(Reader reader, String env, Properties props)SqlSessionFactory build(Configuration config)

第一种方法是最常用的,它使用了一个参照了 XML 文档或上面讨论过的更特定的SqlMapConfig.xml 文件的 Reader 实例。可选的参数是 environment 和 properties。Environment决定加载哪种环境,包括数据源和事务管理器。比如:

<environments default="development">   <environment id="development">      <transactionManager type="JDBC"><dataSource type="POOLED"></environment>   <environment id="production">      <transactionManager type="EXTERNAL"><dataSource type="JNDI"></environment></environments>

如果你调用了一个使用 environment 参数的 build 方法,那么 MyBatis 将会使用configuration 对象来配置这个 environment。当然,如果你指定了一个不合法的 environment,你会得到错误提示。如果你调用了其中之一没有 environment 参数的 build 方法,那么就使用默认的environment(在上面的示例中就会指定为 default=”development”)。

如果你调用了使用 properties 实例的方法,那么 MyBatis 就会加载那些 properties(属性配置文件),并你在你配置中可使用它们。那些属性可以用${propName}语法形式多次用在配置文件中。

回想一下,属性可以从 SqlMapConfig.xml 中被引用,或者直接指定它。因此理解优先 级是很重要的。我们在文档前面已经提及它了,但是这里要再次重申:
如果一个属性存在于这些位置,那么 MyBatis 将会按找下面的顺序来加载它们:

  • 在 properties 元素体中指定的属性首先被读取,
  • 从 properties 元素的类路径 resource 或 url 指定的属性第二个被读取,可以覆盖已经指定的重复属性,
  • 作为方法参数传递的属性最后被读取,可以覆盖已经从 properties 元素体和resource/url 属性中加载的任意重复属性。因此,最高优先级的属性是通过方法参数传递的,之后是 resource/url 属性指定的,最后是在 properties 元素体中指定的属性。

总结一下,前四个方法很大程度上是相同的,但是由于可以覆盖,就允许你可选地指定environment 和/或 properties。这里给出一个从SqlMapConfig.xml 文件创建 SqlSessionFactory的示例:

String resource = "org/mybatis/builder/MapperConfig.xml";Reader reader = Resources.getResourceAsReader(resource);SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();SqlSessionFactory factory = builder.build(reader); 

注意这里我们使用了 Resources 工具类,这个类在 org.mybatis.io 包中。Resources 类正如其名,会帮助你从类路径下,文件系统或一个web URL 加载资源文件。看一下这个类的源代码或者通过你的 IDE 来查看,就会看到一整套有用的方法。这里给出一个简表:

URL getResourceURL(String resource)URL getResourceURL(ClassLoader loader, String resource)InputStream getResourceAsStream(String resource)InputStream getResourceAsStream(ClassLoader loader, String resource)Properties getResourceAsProperties(String resource)Properties getResourceAsProperties(ClassLoader loader, Stringresource)Reader getResourceAsReader(String resource)Reader getResourceAsReader(ClassLoader loader, String resource)File getResourceAsFile(String resource)File getResourceAsFile(ClassLoader loader, String resource)InputStream getUrlAsStream(String urlString)Reader getUrlAsReader(String urlString)Properties getUrlAsProperties(String urlString)Class classForName(String className)

最后一个 build 方法使用了一个 Configuration 实例。configuration 类包含你可能需要了解 SqlSessionFactory 实例的所有内容。Configuration 类对于配置的自查很有用,包含查找和操作 SQL 映射(不推荐使用,因为应用正接收请求)。configuration 类有所有配置的开关,这些你已经了解了,只在 Java API 中露出来。这里有一个简单的示例,如何手动配置
configuration 实例,然后将它传递给 build()方法来创建 SqlSessionFactory。

DataSource dataSource = BaseDataTest.createBlogDataSource();TransactionFactory transactionFactory = new JdbcTransactionFactory();Environment environment =new Environment("development", transactionFactory, dataSource);Configuration configuration = new Configuration(environment);configuration.setLazyLoadingEnabled(true);configuration.setEnhancementEnabled(true);configuration.getTypeAliasRegistry().registerAlias(Blog.class);configuration.getTypeAliasRegistry().registerAlias(Post.class);configuration.getTypeAliasRegistry().registerAlias(Author.class);configuration.addMapper(BoundBlogMapper.class);configuration.addMapper(BoundAuthorMapper.class);SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();SqlSessionFactory factory = builder.build(configuration);

现在你有一个 SqlSessionFactory,可以用来创建 SqlSession 实例。

2、SqlSessionFactory

SqlSessionFactory 有六个方法可以用来创建 SqlSession 实例。通常来说,如何决定是你选择下面这些方法时:

  • Transaction(事务):你想为 session 使用事务或者使用自动提交(通常意味着很多数据库和/或 JDBC 驱动没有事务)?
  • Connection(连接):你想 MyBatis 获得来自配置的数据源的连接还是提供你自己定义的连接?
  • Execution(执行):你想 MyBatis 复用预处理语句和/或批量更新语句(包括插入和删除)?

重载的 openSession()方法签名设置允许你选择这些可选中的任何一个组合。

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:

  • 将会开启一个事务(也就是不自动提交)范围
  • 连接对象会从由活动环境配置的数据源实例中得到。
  • 事务隔离级别将会使用驱动或数据源的默认设置。
  • 预处理语句不会被复用,也不会批量处理更新。

这些方法大都可以自我解释的。开启自动提交,传递“true”给可选的 autoCommit 参数。提供自定义的连接,传递一个 Connection 实例给 connection 参数。注意没有覆盖同时设置Connection 和 autoCommit 两者的方法,因为 MyBatis 会使用当前 connection 对象提供的设置。MyBatis 为事务隔离级别调用使用一个 Java 枚举包装器,称为TransactionIsolationLevel,否 则 它 们 按 预 期 的 方 式 来 工 作 , 并 有 JDBC 支 持 的 5 级

(NONE,READ_UNCOMMITTED,READ_COMMITTED,REPEATABLE_READ,SERIALIZABLE)

还有一个可能对你来说是新见到的参数,就是 ExecutorType。这个枚举类型定义了 3 个值:

ExecutorType.SIMPLE
这个执行器类型不做特殊的事情。它为每个语句的执行创建一个新的预处理语句。
ExecutorType.REUSE
这个执行器类型会复用预处理语句。
ExecutorType.BATCH
这个执行器会批量执行所有更新语句,如果 SELECT 在它们中间执行还会标定它们是必须的,来保证一个简单并易于理解的行为。

注意:在 SqlSessionFactory 中还有一个方法我们没有提及,就是 getConfiguration()。这个方法会返回一个 Configuration 实例,在运行时你可以使用它来自检 MyBatis 的配置。

注意:如果你已经使用之前版本 MyBatis,你要回忆那些 session,transaction 和 batch都是分离的。现在和以往不同了,这些都包含在 session 的范围内了。你需要处理分开处理事务或批量操作来得到它们的效果。

3、 SqlSession

如上面所提到的,SqlSession 实例在 MyBatis 中是非常强大的一个类。在这里你会发现所有执行语句的方法,提交或回滚事务,还有获取映射器实例。

在 SqlSession 类中有超过 20 个方法,所以将它们分开成易于理解的组合。

语句执行方法

这些方法被用来执行定义在 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);

不同的驱动会实现这方面的不同级别的效率。对于最佳的表现,使用结果集类型的SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE(或句话说:不是 FORWARD_ONLY)。

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 方法来保证它会被提交(注意,你不能在自动提交模式下强制 session,或者使用了外部事务管理器时)。很多时候你不用调用 rollback(),因为如果你没有调用 commit 时MyBatis 会替你完成。然而,如果你需要更多对多提交和回滚都可能的 session 的细粒度控制,你可以使用回滚选择来使它成为可能。

清理 Session 级的缓存
void clearCache()

SqlSession 实例有一个本地缓存在执行 update,commit,rollback 和 close 时被清理。要明确地关闭它(获取打算做更多的工作),你可以调用 clearCache()。

确保 SqlSession 被关闭
void close()
你必须保证的最重要的事情是你要关闭所打开的任何 session。保证做到这点的最佳方式是下面的工作模式:

SqlSession session = sqlSessionFactory.openSession();try {   // 下面3行pseudocod来做一些工作   session.insert();   session.update();   session.delete();   session.commit();} finally {   session.close();}

注意:就像 SqlSessionFactory,你可以通过调用 getConfiguration()方法获得 SqlSession使用的 Configuration 实例
Configuration getConfiguration()

使用映射器
T getMapper(Class type)

上述的各个 insert,update,delete 和 select 方法都很强大,但也有些繁琐,没有类型安全,对于你的 IDE 也没有帮助,还有可能的单元测试。在上面的入门章节中我们已经看到了一个使用映射器的示例。

因此,一个更通用的方式来执行映射语句是使用映射器类。一个映射器类就是一个简单的接口,其中的方法定义匹配于 SqlSession 方法。下面的示例展示了一些方法签名和它们是如何映射到 SqlSession 的。

public interface AuthorMapper {   // (Author) selectOne(“selectAuthor”,5);   Author selectAuthor(int id);   // (List) 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”)注解。

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

映射器注解
因为最初设计时,MyBatis 是一个 XML 驱动的框架。配置信息是基于 XML 的,而且映射语句也是定义在 XML 中的。而到了 MyBatis 3,有新的可用的选择了。MyBatis 3 构建在基于全面而且强大的 Java 配置 API 之上。这个配置 API 是基于 XML 的 MyBatis 配置的基础,也是新的基于注解配置的基础。注解提供了一种简单的方式来实现简单映射语句,而
不会引入大量的开销。

注意:不幸的是,Java 注解限制了它们的表现和灵活。尽管很多时间都花调查,设计和实验上,最强大的 MyBatis 映射不能用注解来构建,那并不可笑。C#属性(做示例)就没有这些限制,因此 MyBatis.NET 将会比 XML 有更丰富的选择。也就是说,基于 Java 注解的配置离不开它的特性。

注解有下面这些:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4、 SelectBuilder

一个 Java 程序员面对的最痛苦的事情之一就是在Java 代码中嵌入 SQL 语句。通常这么做是因为 SQL 要动态的生成-否则你可以将它们放到外部的文件或存储过程中。正如你已经看到的,MyBatis 在它的 XML 映射特性中有处理生成动态 SQL 的很强大的方案。然而,有时必须在 Java 代码中创建 SQL 语句的字符串。这种情况下,MyBatis 有另外一种特性来帮助你,在减少典型的加号,引号,新行,格式化问题和嵌入条件来处理多余的逗号或 AND连接词之前,事实上,在 Java 代码中动态生成 SQL 就是一个噩梦。

MyBatis 3 引入了一些不同的理念来处理这个问题,我们可以创建一个类的实例来调用其中的方法来一次构建 SQL语句。但是我们的 SQL结尾时看起来很像 Java 代码而不是 SQL语句。相反,我们尝试了一些不同的做法。最终的结果是关于特定领域语言的结束,Java也不断实现它目前的形式…

SelectBuilder 的秘密

SelectBuilder 类并不神奇,如果你不了解它的工作机制也不会有什么好的作用。别犹豫,让我们来看看它是怎么工作的。SelectBuilder 使用了静态引入和 TreadLocal 变量的组合来开启简洁的语法可以很容易地用条件进行隔行扫描,而且为你保护所有 SQL 的格式。它允许你创建这样的方法:

public String selectBlogsSql() {   BEGIN(); // Clears ThreadLocal variable   SELECT("*");   FROM("BLOG");   return SQL();}

这是一个非常简单的示例,你也许会选择静态地来构建。所以这里给出一个复杂一点的示例:

private String selectPersonSql() {   BEGIN(); // Clears ThreadLocal variable   SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME");   SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON");   FROM("PERSON P");   FROM("ACCOUNT A");   INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID");   INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID");   WHERE("P.ID = A.ID");   WHERE("P.FIRST_NAME like ?");   OR();   WHERE("P.LAST_NAME like ?");   GROUP_BY("P.ID");   HAVING("P.LAST_NAME like ?");   OR();   HAVING("P.FIRST_NAME like ?");   ORDER_BY("P.ID");   ORDER_BY("P.FULL_NAME");   return SQL();}用字符串连接的方式来构建上面的 SQL 就会有一些繁琐了。比如:"SELECT P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME, ""P.LAST_NAME,P.CREATED_ON, P.UPDATED_ON " +"FROM PERSON P, ACCOUNT A " +"INNER JOIN DEPARTMENT D on D.ID = P.DEPARTMENT_ID " +"INNER JOIN COMPANY C on D.COMPANY_ID = C.ID " +"WHERE (P.ID = A.ID AND P.FIRST_NAME like ?) " +"OR (P.LAST_NAME like ?) " +"GROUP BY P.ID " +"HAVING (P.LAST_NAME like ?) " +"OR (P.FIRST_NAME like ?) " +"ORDER BY P.ID, P.FULL_NAME";

如果你喜欢那样的语法,那么你就可以使用它。它很容易出错,要小心那些每行结尾增加的空间。现在,即使你喜欢这样的语法,下面的示例比 Java 中的字符串连接要简单也是没有疑问的:

private String selectPersonLike(Person p){   BEGIN(); // Clears ThreadLocal variable   SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME,   P.LAST_NAME");   FROM("PERSON P");   if (p.id != null) {   WHERE("P.ID like #{id}");   }   if (p.firstName != null) {WHERE("P.FIRST_NAME like #{firstName}");   }   if (p.lastName != null) {WHERE("P.LAST_NAME like #{lastName}");   }   ORDER_BY("P.LAST_NAME");   return SQL();}

这个例子有什么特殊之处?如果你看得仔细,那就不同担心偶然会重复的“AND”关键字,或在“WHERE”和“AND”或两者都没有中选择!上面的语句将会由例子对所有PERSON 记录生成一个查询,有像参数一样的 ID 或 firstName 或 lastName-或这三者的任意组合。SelectBuilder 对理解哪里放置“WHERE”,哪里应该使用“AND”还有所有的字符串连接都是很小心的。最好的情况,无论你以何种顺序调用这些方法(只有一种例外使用 OR()方法)。

有两个方法会吸引你的眼球:BEGIN()和 SQL()。总之,每个SelectBuilder 方法应该以调用 BEGIN()开始,以调用 SQL()结束。当然你可以在中途提取方法来打断你执行的逻辑,但是 SQL 生成的范围应该以 BEGIN()方法开始而且以 SQL()方法结束。BEGIN()方法清理 ThreadLocal 变量,来确保你不会不小心执行了前面的状态,而且 SQL()方法会基于这些调用,从最后一次调用 BEGIN()开始组装你的 SQL 语句。注意 BEGIN()有一个称为 RESET()的代替方法,它们所做的工作相同,只是 RESET()会在特定上下文中读取的更好。

要按照上面示例的方式使用 SelectBuilder,你应该静态引入如下内容:

import static org.mybatis.jdbc.SelectBuilder.*;

只要这个被引入了,那么你使用的类就会拥有 SelectBuilder 的所有可用的方法。下表就是可用方法的完整列表:
在这里插入图片描述
在这里插入图片描述

4、 SqlBuilder

和 SelectBuilder 相似,MyBatis 也包含一个一般性的 SqlBuilder。它包含 SelectBuilder的所有方法,还有构建 insert,update 和 delete 的方法。在 DeleteProvider,InsertProvider 或UpdateProvider 中(还有 SelectProvider)构建 SQL 字符串时这个类就很有用。在上述示例中要使用 SqlBuilder,你只需简单静态引入如下内容:

import static org.mybatis.jdbc.SqlBuilder.*;

SqlBuilder 包含 SelectBuilder 中的所有方法,还有下面这些额外的方法:
在这里插入图片描述
这里是一些示例:

public String deletePersonSql() {   BEGIN(); // Clears ThreadLocal variable   DELETE_FROM("PERSON");   WHERE("ID = ${id}");   return SQL();}public String insertPersonSql() {   BEGIN(); // Clears ThreadLocal variable   INSERT_INTO("PERSON");   VALUES("ID, FIRST_NAME", "${id}, ${firstName}");   VALUES("LAST_NAME", "${lastName}");   return SQL();}public String updatePersonSql() {   BEGIN(); // Clears ThreadLocal variable   UPDATE("PERSON");   SET("FIRST_NAME = ${firstName}");   WHERE("ID = ${id}");   return SQL();}
如果世界总是这么简单就好了。

下期详细介绍【SQL 映射的 XML 文件】中的 【5. resultMap 】

To be continued…别忘了一键三连~

杭州女装网