MyBatis框架使用解析!数据库相关API的使用介绍

动态SQL

if

  • 根据条件包含where子句的一部分
<select id="findActiveBlogLike" resultType="Blog">
    SELECT * FROM BLOG WHERE state = 'ACTIVE'
    <where>
        <if test="title != null">
            AND title like #{title}
        </if>
        <if test="author != null and author.name != null">
            AND author_name like #{author.name}
        </if>
    </where>
</select>
  • 紧接着 < where > 的第一个条件不要加AND

choose-when-otherwise

  • 不使用所有的条件,只是想从多个条件中选择一个使用
<select id="findActiveBlogLike" resultType="Blog">
    SELECT * FROM BLOG WHERE state = 'ACTIVE'
    <choose>
        <when tset="title != null">
            AND title like #{title}
        </when>
        <when test="author != null and author.name != null">
            AND author_name like #{author.name}
        </when>
        <otherwise>
            AND featured = 1
        </otherwise>
    </choose>
</select>

where

  • where元素只会在子元素返回内容的情况下才会插入WHRER的子语句
  • 若子语句的开头为AND或者OR,where元素会将这些去除
<select id="findActiveBlogLike" 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 author_name like #{author.name}
        </if>
    </where>
</select>

trim

  • 可以通过自定义trim元素来定制where元素的功能
    • 比如和where元素等价的自定义trim元素.会移除所有prefixOverrides属性中指定的内容,并且插入prefix属性中指定的内容:
     <trim prefix="WHERE" prefixOverrides="AND |OR">
         ...
     </trim>
    
    prefixOverrides属性会忽略通过管道符分割的文本序列 ,不同的文本序列之间必须要有空格.
    • set元素等价的自定义trim元素. 覆盖了后缀值设置,并且自定义前缀值:
    <trim prefix="SET" suffixOverrides=",">
        ...
    </trim>
    

set

  • 用于动态更新语句的叫作set
  • set元素可以用于动态包含需要更新的列,忽略不更新的列
<update id="updateAuthorIfNecessary">
    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关键字,并会删掉额外的逗号,这些逗号是在使用条件语句给列赋值时引入的

foreach

  • 对集合进行遍历的时候使用foreach, 特别是在构建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允许指定一个集合:
    • 声明可以在元素体内使用的集合项item和索引index变量
    • 指定开头open与结尾close的字符串以及集合项迭代之间的分隔符separator
    • foreach不会错误地添加多余的分隔符
  • 使用foreach时:
    • 可以将任何可迭代对象,比如List,Set,Map对象或者数组对象作为集合参数传递给foreach
    • 当使用可迭代对象或者数组时:
      • index是当前迭代的序号
      • item的值是本次迭代获取到的元素
    • 当使用Map对象或者Map.Entry对象的集合
      • index是键
      • item是值

script

  • 要是想要在带注解的接口类中使用动态SQL语句,可以使用script元素
@update({"<script>",
            "update Author",
                "<set>",
                    "<if test='username != null'>username=#{username},</if>",
                    "<if test='password != null'>password=#{password},</if>",
                    "<if test='email != null'>email=#{email},</if>",    
                "</set>",
                "where id=#{id}",
                "</script>"})   
})
void updateAuthorValues(Author author);

bind

  • 可以使用bind元素在OGNL表达式以外创建一个变量,并绑定到上下文中
<select id="selectBlogsLike" resultType="Blog">
    <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
    SELECT * FROM BLOG
    WHERE title LIKE #{pattern}
</select>

多数据库支持

  • 如果配置了databaseIdProvider, 就可以在动态代码中使用名为 "_databaseId" 的变量来为不同的数据库构建特定的语句
<insert id="insert">
    <selectKey keyProperty="id" resultType="int" order="BEFORE">
        <if test="_databaseId == 'oracle'">
            select seq_users.nextval from dual
        </if>
        <if test="_databaseId == 'db2'">
            select nextval for seq_users from sysibm.sysdummy1
        </if>
    </selectKey>
    insert into users values (#{id}, #{name})
</insert>

动态SQL中插入脚本语言

  • MyBatis 3.2版本开始支持插入脚本语言
  • 允许插入一种语言驱动,并基于这种语言来编写动态SQL查询语句
  • 通过实现LanguageDriver接口插入语言:
public interface LanguageDriver {
    ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
    
    SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);
    SqlSource crateSqlSource(Configuration configuration, String script, Class<?> parameterType);
}
  • 实现自定义语言驱动后,可以在mybatis-config.xml文件中设置为默认语言:
<typeAliases>
    <typeAliase type="com.oxford.MyLanguageDriver" alias="myLanguage" />
</typeAliases>
<settings>
    <setting name="defaultScriptingLanguage" value="myLanguage" />
</settings>
  • 也可以使用lang属性为特定的语句指定语言:
<select id="selectBlog" lang="myLanguage">
    SELECT * FROM BLOG
</select>
  • 或者在mapper接口上使用 @Lang注解:
public interface Mapper {
    @Lang(MyLanguageDriver.class)
    @Select("SELECT * FROM BLOG")
    List<Blog> selectBlog();
}
  • MyBatis中的xml文件中的所有xml标签都由默认MyBatis语言提供,是由语言驱动org.apache.ibatis.scripting.xmltags.XmlLanguageDriver, 别名为xml. 提供的.

Java API

  • MyBatis的执行方法在SqlSession类中

语句执行方法

  • 这些方法被用来执行定义在SQL映射XML文件中的SELECT,INSERT,UPDATE和DELETE语句
    • 每一个方法都接收语句的ID以及参数对象
    • 参数可以是原始类型(支持自动装箱),包装类,JavaBean,POJO或者Map
selectOne
<T> T selectOne(String statement, Object parameter);
selectList
<E> List<E> selectList(String statement, Object parameter);
  • selectOne和selectList的不同点是:
    • selectOne必须返回一个对象或者null值, 如果返回值多于一个就会抛出异常
    • 如果不清楚返回对象会有多少个,就使用selectList
selectCursor
<T> Cursor<T> selectCursor(String statement, Object parameter);
  • 游标Cursor与列表List返回的结果相同,不同的是: 游标借助迭代器实现了数据的惰性加载
try (Cursor<Entity> entities = session.selectCursor(statement, param)) {
    for (Entity entity : entities) {
        // 处理单个实体
    }
}
     
selectMap
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey); 
  • selectMap会将返回对象的其中一个属性作为key值,将对象作为value值,从而将多个结果集转为Map类型值
insert
int insert(String statement, Object parameter);
update
int update(String statement, Object parameter);
delete
int delete(String statement, Obejct parameter);
  • 如果需要查看某个对象是否存在, 最好的办法就是查询一个count值,使用0或者1
  • 由于不是所有语句都需要参数,所以这些方法都具有一个不需要参数的重载形式
  • insert, updatedelete方法返回值表示受该语句影响的行数

select高级版本

  • 允许限制返回行数的范围
  • 提供自定义结果处理逻辑
  • 通常是在数据集非常庞大的情形下使用
selectList
<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
selectCursor
<T> List<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);
selectMap
<K, V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
select
void select(String statement, Object parameter, ResultHandler<T> handler);
select
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);
    
  • ResultHandler参数:
    • 允许自定义每行结果的处理过程
    • 可以添加到List中,创建Map和Set. 甚至丢弃每个返回值,只保留计算后的统计结果
  • ResultHandler接口:
package org.apache.ibatis.session;
public interface ResultHandler<T> {
    void handlerResult(ResultContext<? extends T> context);
}
  • ResultContext参数:
    • 允许访问结果对象和当前已被创建的对象数目
    • 提供一个返回值为Booleanstop方法,可以使用这个stop方法来停止MyBatis加载更多的结果
  • 使用ResultHandler要注意两条限制:
    • 使用带ResultHandler参数的方法时,收到的数据不会被缓存
    • 当使用高级的结果映射集resultMap,MyBatis很可能需要数行结果来构造一个对象.如果这时使用了ResultHandler, 可能会接收到关联association或者集合collection中尚未被完整填充的对象

清除批量更新方法

  • ExecutorType设置为ExecutorType.BATCH时,可以使用flushStatements清除缓存在JDBC驱动类中的批量更新语句
flushStatements
List<BatchResult> flushStatements();

事务控制方法

  • 控制事务作用域的方法有四个,如果已经设置了自动提交或者使用了外部事务管理器, 就不需要使用这些方法
  • 如果正在使用Connection实例控制的JDBC事务管理器,就可以使用以下的四个方法:
void commit();

void commit(boolean force);

void rollback();

void rollback(boolean force);
  • 默认情况下 ,MyBatis不会自动提交事务,除非发现到调用了插入,更新或删除方法改变了数据库
  • 如果没有使用这些方法提交修改,那么就可以在commitrollback方法参数传入true值来保证事务被正常提交
    • 注意: 在自动提交模式或者使用了外部事务管理器的情况下,设置force值对session无效
  • 大部分情况下,无需调用rollback(), 因为MyBatis会在没有调用commit() 时完成回滚操作
  • 但是,当要在一个可能多次提交或回滚的session中详细控制事务,就要使用到回滚rollback操作
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容