MyBatis入门学习教程

  • 1. MyBatis 原生使用入门篇
    • 1.1 什么是MyBatis?
    • 1.2 原生使用安装方法
    • 1.3 使用示例
      • 1.3.1 创建一个学习项目
      • 1.3.1 添加依赖
      • 1.3.2 创建业务实体类
      • 1.3.3 创建Mybatis 业务类接口
      • 1.3.4 创建MyBatis业务接口实现
        • 1.3.4.1 通过XML方式实现业务接口方法
          • dtd(xml 约束规则)
          • namespace(绑定Dao 接口)
          • resultMap(结果映射 )
          • 自动映射
          • sql 标签(定义可重用的 SQL 代码段 )
          • select (查询语句映射)
          • insert, update 和 delete (插入,更新,删除语句映射)
          • 指定传递参数类型
          • 动态SQL
        • 1.3.4.2 通过注解方式实现业务接口方法 (不推荐)
      • 1.3.5 mybatis-config.xml
      • 1.2.6 config.properties
      • 1.2.7 使用API调用
  • 2.MyBatis 日志攻略篇
    • 2.1 MyBatis 支持的日志框架
    • 2.2 Log4j 日志配置可能会被忽略掉
    • 2.3 防止日志被忽略掉的解决方案
    • 2.4 指定使用哪种日志框架
    • 2.5 日志配置
  • 3. MyBatis SQL语句构建器
    • 3.1 Java 动态生成SQL代码的噩梦
    • 3.2 解决方案

这篇博文主要用来总结下学习 MyBatis 的基础知识。

1. MyBatis 原生使用入门篇

MyBatis 官网

1.1 什么是MyBatis?

  • MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
  • 动态SQL 是MyBatis 的精华

1.2 原生使用安装方法

  • 要使用 MyBatis, 我们需要将 mybatis-x.x.x.jar 文件添加到 classpath 中。
  • 如果使用 Maven 来构建项目,则需将下面的 dependency 代码置于 pom.xml 文件中.
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.2</version>
</dependency>

点击查看最新版本

1.3 使用示例

  • 针对官方文档的Demo, 我当时提过一个Issue, https://github.com/mybatis/mybatis-3/issues/1625
  • MyBatis 官方维护人员回复给了一个Demo 库, JPetStore 6是一个完整的Web应用程序,构建于MyBatis 3,Spring 5和Stripes之上。

1.3.1 创建一个学习项目

创建项目配置信息如下:

1.3.1 添加依赖

  • 项目的pom.xml中引入如下依赖:
        <dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.2</version></dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><version>1.4.199</version><scope>runtime</scope></dependency><!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.8</version><scope>provided</scope></dependency>
  • 添加依赖说明如下:

    • 这里使用的是mybatis-3.5.2.jar 是mybatis的核心实现包
    • h2 内嵌文本数据库,也可以使用MySQL 或者Oracle 替换
    • lombok-1.18.8.jar 是可以通过注解省略写getter() setter() toString() 等方法
  • H2创建方法

    创建数据库

    创建表和字段信息

1.3.2 创建业务实体类

UserInfo.java 示例如下:

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;/*** 功能: 用户信息实体类* 作者: 星云* 时间: 2019/8/29 10:58*/
@Getter
@Setter
@ToString
//@Alias("UserInfo")
public class UserInfo {//用户信息IDprivate Long userInfoId;//用户信息名称private String userInfoName;//用户信息手机号private String userInfoMobile;//用户信息年龄private Integer userInfoAge;//用户信息性别private Boolean userInfoSex;
}

注意

  • 我们这里使用了lombok 注解,来省略getter(),setter() 以及toString()方法
  • 这样可以让代码看起来更清爽简洁。
  • 除了要添加lombok依赖之外,还需要给自己的IDE 安装lombok插件,关于如何安装,请自行搜索解决。

1.3.3 创建Mybatis 业务类接口

UserInfoMapper.java

import com.xingyun.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;import java.util.List;
import java.util.Map;@Mapper
public interface UserInfoMapper {int insertUserInfo(UserInfo userInfo);int deleteUserInfoByUserInfoId(Long userInfoId);int updateUserInfo(UserInfo userInfo);UserInfo selectUserInfoByUserInfoId(Long userInfoId);List<UserInfo> selectAllUserInfo();List<UserInfo> selectAllUserInfoByCondition(Map<String, Object> queryMap);
}

注意:

  • 这里我们添加了@Mapper注解,这样MyBatis 就可以检测到我们的UserInfoMapper是一个MyBatis 接口类,而不是普通的接口类。
  • 除了使用@Mapper 注解可以让MyBatis 识别我们的接口类是一个MyBatis 接口类之外,还可以在mybatis-config.xml 里面配置如下:
    <mappers><mapper resource="mapper/*Mapper.xml"/></mappers>
  • 关于mybatis-config.xml 配置可暂时跳过,稍后会进行详解。

1.3.4 创建MyBatis业务接口实现

  • 接下来写这个mybatis 业务接口的实现类,Mybatis接口实现类方式有两种:

    • 一种是通过xml方式实现业务接口方法
    • 一种是通过注解方式实现业务接口方法

Mybatis集成方式大致可以分为两种:

  • XML方式集成
    XML版本为老式的配置集成方式,集成XML文件,SQL语句也是全部写在XML中
  • 注解方式集成
    注解版版本,相对来说比较简约,不需要XML配置,只需要使用注解和代码来操作数据。
  • 使用注解来映射简单语句会使代码显得更加简洁
  • 然而对于稍微复杂一点的语句,Java 注解就力不从心了,并且会显得更加混乱。
  • 因此,如果你需要完成很复杂的事情,那么最好使用 XML 来映射语句。

1.3.4.1 通过XML方式实现业务接口方法

MyBatis 的真正强大在于它的映射语句,这是它的魔力所在。由于它的异常强大,映射器的 XML文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 为聚焦于 SQL 而构建,以尽可能地为你减少麻烦。

UserInfoMapper.xml 配置如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--
重点:namespace = *Mapper.java 包名+ 接口名
-->
<mapper namespace="com.xingyun.dao.mapper.UserInfoMapper"><!--resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象resultMap type 结点需要配置实体类的包名和类名全称,resultMap id 唯一映射结果集 Id<id/> 为主键的映射column 数据库查询结果返回的字段名称property 实体类字段名称一般生产环境,通过resultMap 建立数据库表字段和实体类的映射为了区分数据库表字段和实体类字段名称,数据库表字段和实体类字段名称一般设置不一样--><resultMap type="com.xingyun.model.UserInfo" id="userInfoMap"><id column="USER_INFO_ID" property="userInfoId"/><result column="USER_INFO_NAME" property="userInfoName"/><result column="USER_INFO_MOBILE" property="userInfoMobile"/><result column="USER_INFO_AGE" property="userInfoAge"/><result column="USER_INFO_SEX" property="userInfoSex"/></resultMap><!--    insert – 映射插入语句--><insert id="insertUserInfo" parameterType="com.xingyun.model.UserInfo" >INSERT INTO T_USER_INFO (USER_INFO_NAME,USER_INFO_MOBILE,USER_INFO_AGE,USER_INFO_SEX)VALUES(#{userInfoName},#{userInfoMobile},#{userInfoAge},#{userInfoSex});</insert><!--    delete – 映射删除语句--><delete id="deleteUserInfoByUserInfoId" parameterType="long">DELETE FROM T_USER_INFO WHERE USER_INFO_ID= #{userInfoId}</delete><!--    update – 映射更新语句--><update id="updateUserInfo" parameterType="com.xingyun.model.UserInfo" >UPDATE T_USER_INFO SET USER_INFO_NAME= #{userInfoName},USER_INFO_MOBILE= #{userInfoMobile},USER_INFO_AGE= #{userInfoAge},USER_INFO_SEX= #{userInfoSex} WHERE USER_INFO_ID= #{userInfoId}</update><!-- sql 可被其他语句引用的可重用语句块--><sql id="userInfoFieldSQLId">USER_INFO_ID,USER_INFO_NAME,USER_INFO_MOBILE,USER_INFO_AGE,USER_INFO_SEX</sql><!--  select – 映射查询语句include 引用一个sql片段--><select id="selectUserInfoByUserInfoId"parameterType="long"resultMap="userInfoMap">SELECT <include refid="userInfoFieldSQLId"/>FROM T_USER_INFO WHERE USER_INFO_ID = #{userInfoId}</select><!--  select – 映射查询语句include 引用一个sql片段--><select id="selectAllUserInfo"resultMap="userInfoMap">SELECT<include refid="userInfoFieldSQLId"/>FROM PUBLIC.T_USER_INFO</select><!-- 动态SQL由于定义了别名<typeAlias alias="map" type="java.util.Map"/>因此可以这么写 parameterType="map"否则就得这么写 parameterType="java.util.Map"--><select id="selectAllUserInfoByCondition"parameterType="map"resultMap="userInfoMap">SELECT<include refid="userInfoFieldSQLId"/>FROM T_USER_INFO<where><if test="null!=userInfoName and ''!=userInfoName">USER_INFO_NAME = #{userInfoName}</if><if test="null!=userInfoMobile and ''!=userInfoMobile ">AND USER_INFO_MOBILE= #{userInfoMobile}</if><if test="null!=userInfoAge and 0!=userInfoAge ">AND USER_INFO_AGE= #{userInfoAge}</if><if test="null!=userInfoSex ">AND USER_INFO_SEX= #{userInfoSex}</if><if test="null!=userInfoId and 0!=userInfoId ">AND USER_INFO_ID= #{userInfoId}</if></where></select>
</mapper>
dtd(xml 约束规则)
  • 首先,我们在UserInfoMapper.xml 中引入了http://mybatis.org/dtd/mybatis-3-mapper.dtd这个dtd 文件是对xml 中结点的约束。
namespace(绑定Dao 接口)

在根节点中我们可以看到一个namespace 结点 namespace="com.xingyun.dao.mapper.UserInfoMapper"

关于namespace官方的解释: 对命名空间的一点说明

  • 在之前版本的 MyBatis 中,命名空间(Namespaces)的作用并不大,是可选的。
  • 但现在,随着命名空间越发重要,你必须指定命名空间。
  • 命名空间的作用有两个,一个是利用更长的完全限定名来将不同的语句隔离开来,同时也实现了你上面见到的接口绑定。
  • 就算你觉得暂时用不到接口绑定,你也应该遵循这里的规定,以防哪天你改变了主意。
  • 长远来看,只要将命名空间置于合适的 Java 包命名空间之中,你的代码会变得更加整洁,也有利于你更方便地使用 MyBatis。

其他资料:

  • 在mybatis中,映射文件中的namespace是用于绑定Dao接口的,即面向接口编程
  • 当你的namespace绑定接口后,你可以不用写接口实现类,mybatis会通过该绑定自动帮你找到对应要执行的SQL语句
  • 这个namespace 的值是接口类的包名+ 类名,必须唯一。
  • 这个namespace 很重要,必须配置正确,只有这样才能将接口类和xml 关联起来。
  • 我们知道一个接口的实现是类似这样的
    public class UserInfoMapperImpl implements UserInfoMapper {}
    因此理解上UserInfoMapper 是一个接口类,UserInfoMapper.xml 则是接口实现类

— 这个xml 文件其本质是一个SQL 映射文件。 在这个 SQL 映射文件中只有很少的几个顶级元素(按照应被定义的顺序列出)
接下来我们大致看下这些顶级结点的作用:

  • cache – 对给定命名空间的缓存配置。(不常用)
  • cache-ref – 对其他命名空间缓存配置的引用。(不常用)
  • resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
  • parameterMap – 已被废弃!
  • sql – 可被其他语句引用的可重用语句块。
  • insert – 映射插入语句
  • update – 映射更新语句
  • delete – 映射删除语句
  • select – 映射查询语句
resultMap(结果映射 )
  • resultMap 元素是 MyBatis 中最重要最强大的元素。
  • 它可以让你从 90% 的 JDBC ResultSets数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。
  • 实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的长达数千行的代码。
  • ResultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。

说了那么多废话,那么resultMap 存在的意义何在?

  • 通常数据库列使用大写字母组成的单词命名,单词间用下划线分隔;比如: USER_ID
  • 而 Java 属性一般遵循驼峰命名法约定 比如: userId
  • 为了在这两种命名方式之间启用映射我们需要用resultMap.
  • 接下我们就来看这样的一个例子

  • 假设我们有一个实体类UserInfo.java内容如下:

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;@Getter
@Setter
@ToString
public class UserInfo {//用户信息IDprivate Long userInfoId;//用户信息名称private String userInfoName;//用户信息手机号private String userInfoMobile;//用户信息年龄private Integer userInfoAge;//用户信息性别private Boolean userInfoSex;
}
  • 但是实际上数据库列名是这样的
  • 如果我们需要这样查询所有的用户信息一般会这么做
    <select id="selectAllUserInfo"resultType="com.xingyun.model.UserInfo">SELECT*FROM t_user_info</select>
  • 但是如果执行上述查询返回的结果是USER_INFO_ID 但是实体类字段是userInfoId ,这就会造成不匹配,就会报错
  • resultMap 就是为解决这类的问题而成。
  • 我们只需要配置resultMap 如下:
    <resultMap type="com.xingyun.UserInfo" id="userInfoMap"><id column="USER_INFO_ID" property="userInfoId"/><result column="USER_INFO_NAME" property="userInfoName"/><result column="USER_INFO_MOBILE" property="userInfoMobile"/><result column="USER_INFO_AGE" property="userInfoAge"/><result column="USER_INFO_SEX" property="userInfoSex"/></resultMap>

然后我们修改如下:

  <select id="selectAllUserInfo"resultMap="userInfoMap">SELECT*FROM t_user_info</select>

注意

  • 我们去掉了 resultType 属性
  • 我们在引用它的语句中使用 resultMap 属性

这样就执行成功不会出现映射出错的问题了,那么为什么呢?

  • 那是因为一旦执行上述操作后,mybatis 会在幕后替我们做这样一个操作
SELECTUSER_INFO_ID AS "userInfoId",USER_INFO_NAME AS "userInfoName",USER_INFO_MOBILE AS "userInfoMobile",USER_INFO_AGE AS "userInfoAge",USER_INFO_SEX AS "userInfoSex"
FROMt_user_info
自动映射

为什么要有自动映射?

如果一个项目中有很多实体类,那么我们难道要手动给所有的实体类都创建resultMap?如果真的那样的话岂不是要麻烦死,因此官方提供了一种新的解决方案,开启自动映射。

自动映射须知
官方文档

  • 通常数据库列使用大写字母组成的单词命名,单词间用下划线分隔;比如: USER_ID
  • 而 Java 属性一般遵循驼峰命名法约定 比如: userId
  • 为了在这两种命名方式之间启用自动映射需将 mapUnderscoreToCamelCase 设为 true。
  • 甚至在提供了resultMap结果映射后,自动映射也能工作。
  • 在这种情况下,对于每一个结果映射,在 ResultSet 出现的列,如果没有设置手动映射,将被自动映射。
  • 在自动映射处理完毕后,再处理手动映射。

有三种自动映射等级:

  • NONE - 禁用自动映射。仅对手动映射的属性进行映射。
  • PARTIAL - 对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射 , 默认值是PARTIAL
  • FULL - 自动映射所有属性。要谨慎使用 FULL。

自动映射设置方式
如果要开启自动映射,那么需要在mybatis-config.xml 中添加如下配置:

    <settings><!--mapUnderscoreToCamelCase 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 --><setting name="mapUnderscoreToCamelCase" value="true"/><!--指定 MyBatis 应如何自动映射列到字段或属性 ONE, PARTIAL, FULL --><setting name="autoMappingBehavior" value="PARTIAL"/></settings>

部分禁用自动映射

无论设置的自动映射等级是哪种,你都可以通过在结果映射上设置 autoMapping 属性来为指定的结果映射设置启用/禁用自动映射。

<resultMap id="userResultMap" type="User" autoMapping="false">  <result property="password" column="hashed_password"/></resultMap>
sql 标签(定义可重用的 SQL 代码段 )

为什么要用sql 标签?

  • 如果在xml中有大量重复的SQL语句,我们难道要手动复制粘贴所有的代码么? 那样的话一旦有一处需要修改,全部将都要修改,这无疑是一种灾难。
  • 定义可重用的 SQL 代码段

优化之前
假设我们有一个查询所有用户信息SQL, 这个操作将 t_user_info 中的所有字段

 <select id="selectAllUserInfo"  resultMap="userInfoMap">SELECTUSER_INFO_ID,USER_INFO_NAME,USER_INFO_MOBILE,USER_INFO_AGE,USER_INFO_SEX FROMt_user_info</select>

然后我们有第二个SQL 查询也需要返回该表的所有字段

 <select id="selectAllUserInfoByCondition"parameterType="map"resultMap="userInfoMap">SELECTUSER_INFO_ID,USER_INFO_NAME,USER_INFO_MOBILE,USER_INFO_AGE,USER_INFO_SEX FROM t_user_infoWHEREUSER_INFO_NAME = #{userInfoName}</select>

两个SQL 都需要t_user_info 表中的所有字段,那么为了简化使用,我们可以这样做。

优化之后

  • 我们可以通过 <sql id=" " ></sql> 标签 来定义一个可重用的 SQL 代码段
 <!-- sql 可被其他语句引用的可重用语句块--><sql id="userInfoFieldSQLId">USER_INFO_ID,USER_INFO_NAME,USER_INFO_MOBILE,USER_INFO_AGE,USER_INFO_SEX</sql>
  • 然后通过 <include refid="userInfoFieldSQLId"/> 来引用刚才定义的代码片段

对于第一个查询可以简化为:

    <select id="selectAllUserInfo"resultMap="userInfoMap">SELECT<include refid="userInfoFieldSQLId"/>FROM t_user_info</select>

第二个查询也可以简化为:

    <select id="selectAllUserInfoByCondition"parameterType="map"resultMap="userInfoMap">SELECT<include refid="userInfoFieldSQLId"/>FROM t_user_infoWHEREUSER_INFO_NAME = #{userInfoName}
select (查询语句映射)

查询语句是 MyBatis 中最常用的元素之一,光能把数据存到数据库中价值并不大,只有还能重新取出来才有用,多数应用也都是查询比修改要频繁

Select 元素的属性
属性 描述
id 在命名空间中唯一的标识符,可以被用来引用这条语句
parameterType 将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap 这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。
resultType 从这条语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同时使用。
resultMap 外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂映射的情形都能迎刃而解。可以使用 resultMap 或 resultType,但不能同时使用。
flushCache 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
useCache 将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。
fetchSize 这是一个给驱动的提示,尝试让驱动程序每次批量返回的结果行数和这个设置值相等。 默认值为未设置(unset)(依赖驱动)。
statementType STATEMENT,PREPARED 或 CALLABLE 中的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetType FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖驱动)。
databaseId 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。
resultOrdered 这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。 这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。
resultSets 这个设置仅对多结果集的情况适用。它将列出语句执行后返回的结果集并给每个结果集一个名称,名称是逗号分隔的。
  • select 元素允许你配置很多属性来配置每条语句的作用细节。
<selectid="selectPerson"parameterType="int"parameterMap="deprecated"resultType="hashmap"resultMap="personResultMap"flushCache="false"useCache="true"timeout="10"fetchSize="256"statementType="PREPARED"resultSetType="FORWARD_ONLY">

示例如下:

  • 接口类中定义假如有这样一个语句
public interface UserInfoMapper {UserInfo selectUserInfoByUserInfoId(Long userInfoId);
}
  • 那么在xml 定义如下:
    <select id="selectUserInfoByUserInfoId"parameterType="long"resultMap="userInfoMap">SELECT <include refid="userInfoFieldSQLId"/>FROM t_user_info WHERE USER_INFO_ID = #{userInfoId}</select>
  • id 是接口方法的名称selectUserInfoByUserInfoId
  • parameterType 是传递参数的类型Long
  • resultMap 映射返回的结果

值得注意的是 #{userInfoId} ,这是预处理参数话查询。

这就告诉 MyBatis 创建一个预处理语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL中会由一个“?”来标识,并被传递到一个新的预处理语句中,就像这样 ```// 近似的 JDBC 代码,非 MyBatis 代码

// 近似的 JDBC 代码,非 MyBatis 代码...
String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);
insert, update 和 delete (插入,更新,删除语句映射)
Insert, Update, Delete 元素的属性
属性 描述
id 命名空间中的唯一标识符,可被用来代表这条语句
parameterType 将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap 这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。
flushCache 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:true(对于 insert、update 和 delete 语句)
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。
statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。
keyProperty (仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认值:未设置(unset)。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
keyColumn (仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望使用多个生成的列,也可以设置为逗号分隔的属性名称列表。
databaseId 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。
  • mysql 和SQL Server 插入更新和删除示例如下:
    <!--    insert – 映射插入语句--><insert id="insertUserInfo" parameterType="UserInfo" >INSERT INTO t_user_info (USER_INFO_NAME,USER_INFO_MOBILE,USER_INFO_AGE,USER_INFO_SEX)VALUES(#{userInfoName},#{userInfoMobile},#{userInfoAge},#{userInfoSex});</insert><!--    delete – 映射删除语句--><delete id="deleteUserInfoByUserInfoId" parameterType="long">DELETE FROM t_user_info WHERE USER_INFO_ID= #{userInfoId}</delete><!--    update – 映射更新语句--><update id="updateUserInfo" parameterType="UserInfo" >UPDATE t_user_info SET USER_INFO_NAME= #{userInfoName},USER_INFO_MOBILE= #{userInfoMobile},USER_INFO_AGE= #{userInfoAge},USER_INFO_SEX= #{userInfoSex} WHERE USER_INFO_ID= #{userInfoId}</update>
指定传递参数类型
  • xml 集成方式中指定传递参数类型

mybatis xml中通过parameterType 来接受传递的参数类型

 <select id="selectUserInfoByUserInfoId"parameterType="long"resultMap="userInfoMap">SELECT <include refid="userInfoFieldSQLId"/>FROM t_user_info WHERE USER_INFO_ID = #{userInfoId}</select>

赋值方式

  • #{} 参数占位符方式赋值
    默认情况下,使用 #{} 格式的语法会导致 MyBatis 创建 PreparedStatement 参数占位符并安全地设置参数(就像使用 ? 一样) 这样做更安全,更迅速,通常也是首选做法
  • ${} 不转义的方式赋值
    不过有时你就是想直接在 SQL 语句中插入一个不转义的字符串, ${columnName}
    这里 MyBatis 不会修改或转义字符串
  • 注解集成方式中指定传递参数类型

mybatis注解中通过@Param 注解 传递参数

示例如下:

    @Delete(value = "DELETE FROM t_user_info WHERE USER_INFO_ID= #{userInfoId}")int deleteUserInfoByUserInfoId(@Param("userInfoId")Long userInfoId);
动态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

if
动态 SQL 通常要做的事情是根据条件包含 where 子句的一部分

 <select id="selectAllUserInfoByCondition"parameterType="map"resultMap="userInfoMap">SELECT<include refid="userInfoFieldSQLId"/>FROM t_user_infoWHEREUSER_INFO_NAME = #{userInfoName}<if test="null!=userInfoMobile and ''!=userInfoMobile ">AND USER_INFO_MOBILE= #{userInfoMobile}</if><if test="null!=userInfoAge and 0!=userInfoAge ">AND USER_INFO_AGE= #{userInfoAge}</if><if test="null!=userInfoSex ">AND USER_INFO_SEX= #{userInfoSex}</if><if test="null!=userInfoId and 0!=userInfoId ">AND USER_INFO_ID= #{userInfoId}</if></select>

choose, when, otherwise

有时我们不想应用到所有的条件语句,而只想从中择其一项。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

<select id="findActiveBlogLike"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 author_name like #{author.name}</when><otherwise>AND featured = 1</otherwise></choose>
</select>

trim, where, set
可能存在的问题
前面几个例子已经合宜地解决了一个臭名昭著的动态 SQL 问题。现在回到“if”示例,这次我们将“ACTIVE = 1”也设置成动态的条件,看看会发生什么。

<select id="findActiveBlogLike"resultType="Blog">SELECT * FROM BLOGWHERE<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>
</select>

如果这些条件没有一个能匹配上会发生什么?最终这条 SQL 会变成这样:

SELECT * FROM BLOG
WHERE

这会导致查询失败。如果仅仅第二个条件匹配又会怎样?这条 SQL 最终会是这样:

SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’

这个查询也会失败。这个问题不能简单地用条件句式来解决,如果你也曾经被迫这样写过,那么你很可能从此以后都不会再写出这种语句了。

MyBatis 有一个简单的处理,这在 90% 的情况下都会有用。而在不能使用的地方,你可以自定义处理方式来令其正常工作。一处简单的修改就能达到目的

<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>

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>

foreach

动态 SQL 的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建 IN 条件语句的时候

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

注解方式如何实现动态SQL?

   @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>","    <if test='bio != null'>bio=#{bio}</if>","  </set>","where id=#{id}","</script>"})void updateAuthorValues(Author author);
  • config.properties 配置如下
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis_spring_boot_db?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username=root
password=xingyun

1.3.4.2 通过注解方式实现业务接口方法 (不推荐)

1.3.5 mybatis-config.xml

mybatis-config.xml配置内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!--    在 properties 元素体内指定的属性首先被读取。--><!--    然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。--><!--    最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。--><!--加载外部的config.properties 配置文件--><properties resource="config/config.properties"><!--如果config.properties中没有配置username和password那么就使用这里的配置--><property name="username" value="sa"/><property name="password" value="sa"/><!-- 方式二写法必须添加如下配置启用默认值特性 --><property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/><!-- 方式三写法必须添加如下配置修改默认值的分隔符-->
<!--        <property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/>--></properties><!--更多配置详情,参考 http://www.mybatis.org/mybatis-3/zh/configuration.html#settings--><settings><!--全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。默认值 true--><setting name="cacheEnabled" value="true"/><!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。默认值 false--><setting name="lazyLoadingEnabled" value="false"/><!--当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载(参考 lazyLoadTriggerMethods)。--><setting name="aggressiveLazyLoading" value="false"/><!--是否允许单一语句返回多结果集(需要驱动支持)--><setting name="multipleResultSetsEnabled" value="true"/><!--使用列标签代替列名。不同的驱动在这方面会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。--><setting name="useColumnLabel" value="true"/><!--允许 JDBC 支持自动生成主键,需要驱动支持。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能支持但仍可正常工作(比如 Derby)。--><setting name="useGeneratedKeys" value="false"/><!--指定 MyBatis 应如何自动映射列到字段或属性。 --><!--NONE 表示取消自动映射;--><!--PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 --><!--FULL 会自动映射任意复杂的结果集(无论是否嵌套)。--><setting name="autoMappingBehavior" value="PARTIAL"/><!--指定发现自动映射目标未知列(或者未知属性类型)的行为。NONE: 不做任何反应,默认值WARNING: 输出提醒日志 ('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN)FAILING: 映射失败 (抛出 SqlSessionException)--><setting name="autoMappingUnknownColumnBehavior" value="NONE"/><!--配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements);BATCH 执行器将重用语句并执行批量更新。--><setting name="defaultExecutorType" value="SIMPLE"/><!--设置超时时间,它决定驱动等待数据库响应的秒数。默认值null--><setting name="defaultStatementTimeout" value="25"/><!--为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖默认值null--><setting name="defaultFetchSize" value="100"/><!--允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false--><setting name="safeRowBoundsEnabled" value="false"/><!--允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为 false--><setting name="safeResultHandlerEnabled" value="false"/><!--是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。--><setting name="mapUnderscoreToCamelCase" value="false"/><!--MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。--><setting name="localCacheScope" value="SESSION"/><!--当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。--><setting name="jdbcTypeForNull" value="OTHER"/><!--指定哪个对象的方法触发一次延迟加载。--><setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/></settings><!--配置别名--><!--配置详情:http://www.mybatis.org/mybatis-3/zh/configuration.html#typeAliases--><typeAliases><typeAlias alias="UserInfo" type="com.xingyun.model.UserInfo"/><!--也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean--><!--每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名-->
<!--        <package name="com.xingyun.model"/>--></typeAliases><!--配置自定义类型处理器--><!--配置详情:http://www.mybatis.org/mybatis-3/zh/configuration.html#typeHandlers --><!-- <typeHandlers><typeHandler handler="org.mybatis.example.ExampleTypeHandler"/><typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="java.math.RoundingMode"/></typeHandlers>--><!--指定默认启用开发环境--><!--默认使用的环境 ID--><environments default="development"><!--开发环境--><environment id="development"><!--事务管理器的配置 type="[JDBC|MANAGED]" 这两种事务管理器类型都不需要设置任何属性--><transactionManager type="JDBC"/><!--如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器, 因为 Spring 模块会使用自带的管理器来覆盖前面的配置--><!--数据源的配置 有三种内建的数据源类型 type="[UNPOOLED|POOLED|JNDI]"--><dataSource type="POOLED"><property name="driver" value="${driver}"/><!-- 这是数据库的 JDBC URL 地址。--><property name="url" value="${url}"/><!--方式一:-->
<!--                <property name="username" value="${username}"/>-->
<!--                <property name="password" value="${password}"/>--><!--方式二:从 MyBatis 3.4.2 开始,你可以为占位符指定一个默认值--><property name="username" value="${username:root}"/><property name="password" value="${password:toor}"/><!--方式三:-->
<!--                <property name="username" value="${db:username?:ut_user}"/>-->
<!--                <property name="password" value="${db:password?:toor}"/>--></dataSource></environment><!--生产环境--><environment id="prod"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment></environments><!--这些配置会告诉了 MyBatis 去哪里找映射文件 --><mappers><!-- 使用相对于类路径的资源引用 --><!-- 最新版本测试发现,*Mapper.xml文件不可以放入多层级文件夹下 --><!--单个配置 --><mapper resource="mapper/UserInfoMapper.xml"/><!-- 最新版本测试发现,*Mapper.xml如下配置会报错 --><!--模糊匹配配置 --><!-- <mapper resource="mapper/*Mapper.xml"/>--><!-- 使用完全限定资源定位符(URL) --><!--<mapper url="file:///var/mappers/AuthorMapper.xml"/>--><!-- 使用映射器接口实现类的完全限定类名 --><!--<mapper class="org.mybatis.builder.AuthorMapper"/>--><!-- 将包内的映射器接口实现全部注册为映射器 --><!-- <package name="org.mybatis.builder"/>--></mappers>
</configuration>

1.2.6 config.properties

config.properties 配置内容如下:

# H2 Config
# 配置H2数据库的驱动
driver=org.h2.Driver
# 配置H2数据库的URL
url=jdbc:h2:~/mybatis_test_db
# 配置H2数据库的连接账号
username=sa
# 配置H2数据库的连接密码
password=sa# MySQL
#driver=com.mysql.cj.jdbc.Driver
#url=jdbc:mysql://localhost:3306/mybatis_test_db?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
#username=root
#password=xingyun

1.2.7 使用API调用

MainTest.java内容如下:

import com.xingyun.dao.mapper.UserInfoMapper;
import com.xingyun.model.UserInfo;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;/*** 功能: 从 XML 中构建 SqlSessionFactory* 作者: 星云* 时间: 2019/9/2 9:41*/
public class MainTest {private static final String resource = "config/mybatis-config.xml";private static InputStream inputStream = null;private static SqlSessionFactory sqlSessionFactory=null;private static SqlSession sqlSession=null;private static int effectRowLine=0;public static void main(String[] args) {try {//从classpath根路径开始加载配置文件inputStream = Resources.getResourceAsStream(resource);//每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的//通过 SqlSessionFactoryBuilder创建SqlSessionFactory//而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//打开Session//SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法sqlSession=sqlSessionFactory.openSession();//制造一些假数据UserInfo toAddUserInfo=makeUserInfo();//方式一 调用插入方法effectRowLine=sqlSession.insert("com.xingyun.dao.mapper.UserInfoMapper.insertUserInfo",toAddUserInfo);System.out.println("执行影响行数:"+effectRowLine);//方式二 推荐这种 这种方法有很多优势// 首先它不依赖于字符串字面值,会更安全一点;// 其次,如果你的 IDE 有代码补全功能,那么代码补全可以帮你快速选择已映射的 SQL 语句。UserInfoMapper userInfoMapper=sqlSession.getMapper(UserInfoMapper.class);//执行插入方法effectRowLine=userInfoMapper.insertUserInfo(toAddUserInfo);System.out.println("执行影响行数:"+effectRowLine);//执行查询方法List<UserInfo> userInfoList=userInfoMapper.selectAllUserInfo();System.out.println("当前数据总条数:\r\n"+userInfoList.size());System.out.println("当前所有数据结果:\r\n"+userInfoList.toString());} catch (IOException e) {System.err.println(e);}finally {if(null!=sqlSession){sqlSession.close();if(null!=inputStream){try {inputStream.close();} catch (IOException e) {System.err.println("InputStream关闭出错"+e);}}}}}/*** 创建一些假数据*/private static UserInfo makeUserInfo(){//封装要插入的数据UserInfo userInfo=new UserInfo();userInfo.setUserInfoId(1L);userInfo.setUserInfoAge(27);userInfo.setUserInfoName("xingyun");userInfo.setUserInfoMobile("183****5684");userInfo.setUserInfoSex(false);return userInfo;}
}

输出内容如下:

2.MyBatis 日志攻略篇

2.1 MyBatis 支持的日志框架

我们知道,市面上日志框架有很多,MyBatis内置支持如下几种日志框架:

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j
  • JDK logging

一旦程序启动后,程序会从classpath 下按上面列举的顺序查找合适的日志依赖,
它会使用第一个查找得到的日志依赖,如果都未找到日志功能就会被禁用。

2.2 Log4j 日志配置可能会被忽略掉

  • 不少应用服务器(如 TomcatWebShpere)的类路径中已经包含 Commons Logging,所以在这种配置环境下的 MyBatis 会把它作为日志工具,记住这点非常重要。
  • 这将意味着,在诸如 WebSphere 的环境中,它提供了 Commons Logging 的私有实现,你的 Log4J 配置将被忽略。MyBatis 将你的 Log4J 配置忽略掉是相当令人郁闷的(事实上,正是因为在这种配置环境下,MyBatis 才会选择使用Commons Logging 而不是 Log4J)。

2.3 防止日志被忽略掉的解决方案

如果你的应用部署在一个类路径已经包含 Commons Logging 的环境中,而你又想使用其它日志工具,你可以通过在 MyBatis 配置文件 mybatis-config.xml 里面添加一项 setting 来选择别的日志工具。

2.4 指定使用哪种日志框架

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- MyBatis 全局配置 --><settings><!--logImpl 可选的值有:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING --><setting name="logImpl" value="LOG4J"/></settings>
</configuration>

2.5 日志配置

  • 添加 Log4J 的 jar 包

对于 web 应用或企业级应用,则需要将 log4j.jar 添加到 WEB-INF/lib 目录下

  • 在应用的类路径中创建一个名称为 log4j.properties 的文件
# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration
# 记录打印SQL
log4j.logger.com.xingyun.springbootwithmybatisxmlsample.dao.mapper=TRACE
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 输出格式
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
  • 如需对 XML 文件记录日志,只要对命名空间增加日志记录功能即可
log4j.logger.org.mybatis.example.BlogMapper=TRACE
  • 要记录具体语句的日志可以这样做:
log4j.logger.org.mybatis.example.BlogMapper.selectBlog=TRACE

3. MyBatis SQL语句构建器

官方文档

3.1 Java 动态生成SQL代码的噩梦

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

什么意思呢?

假设我们在Java代码中来动态生成如下的SQL代码

String 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";

如果全部是用StringBuffer 或者StringBuilder 追加,仍然是个很麻烦的事情。
因为有些特殊字符需要转义等,是件很头大的事情。

3.2 解决方案

  • MyBatis 3提供了方便的工具类来帮助解决该问题。
  • 使用SQL类,简单地创建一个实例来调用方法生成SQL语句。

上面示例中的问题就像重写SQL类那样

private String selectPersonSql() {return new SQL() {{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");}}.toString();
}

这种方式大大简化了字符串的拼接



自从MyBatis 3.5.2 之后, 我们如果想创建拼接成这样的SQL插入语句,

 INSERT INTO PERSON(ID, FULL_NAME)VALUES(#{mainPerson.id}, #{mainPerson.fullName}) , (#{subPerson.id}, #{subPerson.fullName})

只需要这么做:

public String insertPersonsSql() {return new SQL().INSERT_INTO("PERSON").INTO_COLUMNS("ID", "FULL_NAME").INTO_VALUES("#{mainPerson.id}", "#{mainPerson.fullName}").ADD_ROW().INTO_VALUES("#{subPerson.id}", "#{subPerson.fullName}").toString();
}

我们如果想创建拼接成这样的SQL插叙语句

public String selectPersonsWithOffsetLimitSql() {// SELECT id, name FROM PERSON//     LIMIT #{limit} OFFSET #{offset}return new SQL().SELECT("id", "name").FROM("PERSON").LIMIT("#{limit}").OFFSET("#{offset}").toString();
}public String selectPersonsWithFetchFirstSql() {// SELECT id, name FROM PERSON//     OFFSET #{offset} ROWS FETCH FIRST #{limit} ROWS ONLYreturn new SQL().SELECT("id", "name").FROM("PERSON").OFFSET_ROWS("#{offset}").FETCH_FIRST_ROWS_ONLY("#{limit}").toString();
}

本篇完~

MyBatis入门学习教程相关推荐

  1. MyBatis入门学习教程-调用存储过程

    一.提出需求 查询得到男性或女性的数量, 如果传入的是0就女性否则是男性 二.准备数据库表和存储过程 1 create table p_user( 2 id int primary key auto_ ...

  2. MAYA 2022基础入门学习教程

    流派:电子学习| MP4 |视频:h264,1280×720 |音频:AAC,48.0 KHz 语言:英语+中英文字幕(根据原英文字幕机译更准确)|大小解压后:3.41 GB |时长:4.5小时 包含 ...

  3. 3dmax Vray建筑可视化入门学习教程

    面向初学者的3Ds Max Vray最佳Archviz可视化课程 从安装到最终图像的一切都将从头开始教授,不需要任何经验 大小解压后:3.25G 时长4h 6m 1280X720 MP4 语言:英语+ ...

  4. Blender 3.0基础入门学习教程 Introduction to Blender 3.0

    成为Blender通才,通过这个基于项目的循序渐进课程学习所有主题的基础知识. 你会学到什么 教程获取:Blender 3.0基础入门学习教程 Introduction to Blender 3.0- ...

  5. Maya游戏角色绑定入门学习教程 Game Character Rigging for Beginners in Maya

    准备好开始为游戏制作自己的角色动画了吗? 你会学到什么 了解Maya的界面 优化并准备好你的模型,为游戏做准备 了解关节以及如何使用它们来构建健壮的角色骨骼,以便在任何游戏引擎中制作动画 了解IK和F ...

  6. 三维地形制作软件 World Machine 基础入门学习教程

    <World Machine课程>涵盖了你需要的一切,让你有一个坚实的基础来构建自己的高质量的电影或视频游戏地形. 你会学到什么 为渲染或游戏开发创建高分辨率.高细节的地形. 基于Worl ...

  7. Blender3.0动画制作入门学习教程 Learn Animation with Blender (2021)

    要求 下载并安装Blender.免费下载和免费用于任何目的. 描述 加入我的动画课程. 在本课程中,我将从头开始讲述在Blender中创建动画场景的过程. 从第一步到最终渲染.在这个课程中,我们将使用 ...

  8. UE5真实环境设计入门学习教程

    大小解压后:4.69G 时长4h 30m 1280X720 MP4 语言:英语+中英文字幕(根据原英文字幕机译更准确) 虚幻引擎5–面向初学者的真实环境设计 Unreal Engine 5 – Rea ...

  9. ZBrush全面入门学习教程 Schoolism – Introduction to ZBrush

    ZBrush全面入门学习教程 Schoolism – Introduction to ZBrush ZBrush全面入门学习教程 Schoolism – Introduction to ZBrush ...

最新文章

  1. duilib 自带树形控件的认识
  2. git 拉取分支代码 合分支
  3. AMD CPU真烂!售后服务也很可恶!
  4. IE haslayout的理解与bug修复
  5. 我所知道的前端组件化与模块化
  6. 什么是pdi检测_为什么国人买车钟爱白色?这几点购车陷阱不要踩
  7. Pandas简单写入数据到csv文件
  8. vsftpd虚拟用户【公司系统部分享】
  9. property、setter、deleter装饰器的使用
  10. 计算机休眠下睡眠的不同点是什么,电脑休眠和睡眠的区别是什么
  11. 自学单片机是否先学c语言,学习单片机一定要先学好C语言再去学单片机吗
  12. 微分中值定理之柯西中值定理
  13. 欢迎使用CSDN-markdown编辑器额企鹅去恶趣味
  14. pytest assert 封装
  15. 索尼BDP-S580回顾 - 一个最好的蓝光播放器在2011年
  16. 手机翻译html工具,1分钟教你用手机实时翻译,自带翻译功能就是强大,各牌手机均可...
  17. 「备战春招/秋招系列」程序员的简历就该这样写
  18. SQL 多表查询例题
  19. 纳米饮水思源,原子结构探秘
  20. 产品用户手册难写在哪里?

热门文章

  1. 发现丢失的身份证被人拿去注册公司该怎么办
  2. shadow dom的作用和用法详解(createShadowRoot, attachShadow)
  3. python安装Crypto
  4. 关于bootstrap日期选择器显示时分秒的问题
  5. java for循环打印爱心
  6. Android自定义九宫格输入框
  7. 暴力破解(Burte Force)
  8. 【论文翻译】Orthographic Feature Transform for Monocular 3D Object Detection
  9. 二叉树已知前序中序求后序(超简单)(java)
  10. linux 系统盘做软raid,Linux下软raid实现方案