# mybatis相关梳理

参考资料:mybatis简易教程 (opens new window)

# 1、简介

MyBatis 是一个开源、轻量级的数据持久化框架,是 JDBC 和 Hibernate 的替代方案。MyBatis 内部封装了 JDBC,简化了加载驱动、创建连接、创建 statement 等繁杂的过程,开发者只需要关注 SQL 语句本身。

MyBatis 支持定制化 SQL、存储过程以及高级映射,可以在实体类和 SQL 语句之间建立映射关系,是一种半自动化的 ORM 实现。其封装性低于 Hibernate,但性能优秀、小巧、简单易学、应用广泛。

解析:

  • 数据持久化:将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中数据模型的统称。例如,文件的存储、数据的读取以及对数据表的增删改查等都是数据持久化操作。
  • ORM:(Object Relational Mapping,对象关系映射)是一种数据持久化技术,它在对象模型和关系型数据库之间建立起对应关系,并且提供了一种机制,通过 JavaBean 对象去操作数据库表中的数据。

MyBatis三个基本要素:

  • 核心接口和类
  • MyBatis核心配置文件(mybatis-config.xml)
  • sql映射文件(mapper.xml)

# 2、mybatics映射器

映射文件和接口在同一package下:WebsiteMapper.java、WebsiteMapper.xml,映射器mapper.xml主要元素:

  • mapper:根节点,只有一个namespace属性,全局唯一绑定Dao接口(WebsiteMapper.java),不用写实现类。
  • select、insert、update、delete
  • parameterMap:定义参数映射关系
  • sql:定义一部分sql,在其他地方引用
  • resultMap:描述数据库结果集与对象对应关系
  • cache:配置给定命名空间的缓存
  • cache-ref:其他命名空间缓存的引用

# 3、select 标签

# 1、标签属性

  • id:唯一标识,保证namespace + id全局唯一
  • parameterType:传入参数类型的全限定名或别名,可选
  • resultType:SQL 语句执行后返回的类型(全限定名或者别名)
  • resultMap:映射集的引用,与 <resultMap>元素一起使用,返回时可以使用resultType或resultMap之一
  • fetchSize:获取记录总条数,默认值为数据库厂商提供的 JDBC 驱动所设置的条数
  • timeout:超时时间,单位s
  • flushCache:用于设置在调用 SQL 语句后是否要求 MyBatis 清空之前查询的本地缓存和二级缓存,默认值为 false,如果设置为 true,则任何时候只要 SQL
  • useCache:启动二级缓存的开关,默认值为 true,表示将査询结果存入二级缓存中
  • statementType:告诉 MyBatis 使用哪个 JDBC 的 Statement 工作,取值为 STATEMENT(Statement)普通的不带参的查询SQL、 PREPARED(PreparedStatement)支持可变参数的SQL、CALLABLE(CallableStatement)支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持。
  • resultSetType:这是针对 JDBC 的 ResultSet 接口而言,其值可设置为 FORWARD_ONLY(只允许向前访问)、SCROLL_SENSITIVE(双向滚动,但不及时更新)、SCROLLJNSENSITIVE(双向滚动,及时更新)

# 2、传递参数方法

# 1、使用map传递参数

使用map接口作为参数实现

//WebsiteMapper.xml
<select id="selectWebsiteByMap" resultType="net.biancheng.po.Website" parameterType="map">
    SELECT id,NAME,url FROM website
    WHERE name LIKE CONCAT ('%',#{name},'%')
    AND url LIKE CONCAT ('%',#{url},'%')
</select>

//WebsiteMapper.java
public List<Website> selectWebsiteByMap(Map<String, String> params);

//使用案例:map传入name、url参数
Map<String,String> paramsMap = new HashMap<String,String>();
paramsMap.put("name","编程");
paramsMap.put("url","biancheng");
websiteMapper.selectWebsiteByMap(paramsMap);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 2、使用注解传递参数

使用注解@Param()传递参数

//WebsiteMapper.xml
<select id="selectWebsiteByMap" resultType="net.biancheng.po.Website" parameterType="map">
    SELECT id,NAME,url FROM website
    WHERE name LIKE CONCAT ('%',#{name},'%')
    AND url LIKE CONCAT ('%',#{url},'%')
</select>
  
//WebsiteMapper.java
public List<Website> selectWebsiteByMap(@Param("name") String name, @Param("url") String url);
1
2
3
4
5
6
7
8
9

# 3、使用javaBean传递参数

在参数过多的情况下,MyBatis 允许组织一个 JavaBean,通过简单的 setter 和 getter 方法设置参数,提高可读性

//WebsiteMapper.xml
<select id="selectWebsiteByMap" resultType="net.biancheng.po.Website" parameterType="map">
    SELECT id,NAME,url FROM website
    WHERE name LIKE CONCAT ('%',#{name},'%')
    AND url LIKE CONCAT ('%',#{url},'%')
</select>
  
//WebsiteMapper.java
public List<Website> selectWebsiteByAn(Website website);
1
2
3
4
5
6
7
8
9

# 4、insert标签

# 1、标签属性

  • id、parameterType、flushCache、flushCache:同上
  • keyProperty:将插入操作的返回值赋给 PO 类的某个属性,通常为主键对应的属性。如果是联合主键,可以将多个值用逗号隔开。
  • keyColumn:该属性用于设置第几列是主键,当主键列不是表中的第 1 列时,就需要设置该属性。如果是联合主键,可以将多个值用逗号隔开。
  • useGeneratedKe:用来设置,是否使用 JDBC 提供的 getGenereatedKeys() 方法,获取数据库内部产生的主键并赋值到 keyProperty 属性设置的请求对象的属性中,例如 MySQL、SQL Server 等自动递增的字段,其默认值为 false。该属性值设置为 true 后,会将数据库生成的主键回填到请求对象中,以供其他业务使用。
  • databaseId:取值范围 oracle、mysql 等,表示数据库厂家;元素内部可通过 <if test="_databaseId = 'oracle'">来为特定数据库指定不同的 sql 语句。

# 2、主键处理

# 1、自动递增

MySQL、SQL Server 等数据库表可以采用自动递增的字段作为其主键,当向这样的数据库表插入数据时,即使不指定自增主键的值,数据库也会根据自增规则自动生成主键并插入到表中。一些特殊情况下,我们可能需要将这个刚刚生成的主键回填到请求对象(原本不包含主键信息的请求对象)中,供其他业务使用。此时,我们就可以通过在 insert 标签中添加 keyProperty 和 useGeneratedKeys 属性,来实现该功能。

<!-- WebsiteMapper.xml -->
<insert id="addWebsite" parameterType="net.biancheng.po.Website" keyProperty="id" useGeneratedKeys="true">
    insert into Website (name,url) values(#{name},#{url})
</insert>
  
// 添加一个网站信息
Website addsite = new Website();
//插入的对象中不包含主键 id 
addsite.setName("jian"); 
addsite.setUrl("https://www.baidu.com/");
int num = websiteMapper.addWebsite(addsite);
System.out.println("添加了 " + num + " 条记录");
System.out.println("添加记录的主键是:" + addsite.getId()); 
1
2
3
4
5
6
7
8
9
10
11
12
13

# 2、自定义主键

若数据库不支持主键自动递增(例如 Oracle),或者取消了主键自动递增的规则,我们可以使用 MyBatis 的 <selectKey>标签自定义生成主键。

<selectKey> 标签中属性说明如下:

  • keyProperty:用于指定主键值对应的 PO 类的属性。
  • order:该属性取值可以为 BEFORE 或 AFTER。BEFORE 表示先执行 <selectKey> 标签内的语句,再执行插入语句;AFTER 表示先执行插入语句再执行<selectKey>标签内的语句。
<!-- 添加一个网站,#{name}net.biancheng.po.Website 的属性值 -->
<insert id="insertWebsite" parameterType="net.biancheng.po.Website">
    <!-- 先使用selectKey标签定义主键,然后再定义SQL语句 -->
    <selectKey keyProperty="id" resultType="Integer" order="BEFORE">
        select if(max(id) is null,1,max(id)+1) as newId from Website
    </selectKey>
    insert into Website (id,name,url) values(#{id},#{name},#{url})
</insert>
1
2
3
4
5
6
7
8

# 5、resultMap标签

用于解决实体类属性名与数据库表中字段名不一致的情况,可以将查询结果映射成实体对象

<!-- id为唯一标识,type属性表示需要的POJO -->
<resultMap id="BaseResultMap" type="com.icbc.inforisk.rule.entity.AfwMagParameterArea"> 
    <constructor>					<!-- 类在实例化时用来注入结果到构造方法 -->
        <idArg/>					<!-- ID参数,结果为ID -->
        <arg/>						<!-- 注入到构造方法的一个普通结果 --> 
    </constructor>
    <!-- 用于表示哪个列是主键 -->
  	<id column="TABLE_NAME" jdbcType="VARCHAR" property="tableName"/>
    <!-- 用于表示POJO和SQL列名的映射关系 -->
    <result column="PARA_VALUE_1" jdbcType="VARCHAR" property="paraValue1"/>							
    <association property=""/>			<!-- 用于一对一关联 -->
    <collection property=""/>				<!-- 用于一对多、多对多关联 -->
    <discriminator javaType="">			<!-- 使用结果值来决定使用哪个结果映射 -->
        <case value=""/>						<!-- 基于某些值的结果映射 -->
    </discriminator>
</resultMap>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

id和result常用,有如下属性:

  • property:映射到列结果的字段或属性
  • column:对应sql列
  • javaType:配置java类型,可以是特定的类完全限定名或别名
  • jdbcType:配置数据库类型
  • typeHandler:类型处理器,需要指定jdbcType和javaType相互转化的规则

# 6、注解

可在mybatis-x.x.x.jar/org.apache.ibatis.annotations中查看

  • sql语句映射:@Insert、@Select、@Delete、@Param等
  • 结果集映射:@Result、@Results、@ResultMap
  • 关系映射:@one、@many

# 7、动态SQL

在映射xml文件里配置动态sql,有几种元素

  • if:判断语句
  • choose(when、otherwise):多条件判断,类似switch和case语句
  • trim、where:辅助元素,处理sql拼接问题
  • foreach:循环语句,在in语句等列举条件常用
  • bind:辅助元素,拼接参数

# 1、if标签

<select id="selectAllWebsite" resultMap="myResult">
    select id,name,url from website
    <if test="name != null">
        where name like #{name}
    </if>
</select>
1
2
3
4
5
6

# 2、choose标签

<mapper namespace="net.biancheng.mapper.WebsiteMapper">
    <select id="selectWebsite"
        parameterType="net.biancheng.po.Website"
        resultType="net.biancheng.po.Website">
        SELECT id,name,url,age,country
        FROM website WHERE 1=1
        <choose>
            <when test="name != null and name !=''">
                AND name LIKE CONCAT('%',#{name},'%')
            </when>
            <when test="url != null and url !=''">
                AND url LIKE CONCAT('%',#{url},'%')
            </when>
            <otherwise>
                AND age is not null
            </otherwise>
        </choose>
    </select>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 3、where标签

用于简化and、or语句,类型于where语句:if 语句中判断条件为 true 时,where 关键字才会加入到组装的 SQL 里面,否则就不加入。where 会检索语句,它会将 where 后的第一个 SQL 条件语句的 AND 或者 OR 关键词去掉。

<!-- if条件成立时,才会引入where语句 -->
<select id="selectWebsite" resultType="net.biancheng.po.Website">
    select id,name,url from website
    <where>
        <if test="name != null">
            AND name like #{name}
        </if>
        <if test="url!= null">
            AND url like #{url}
        </if>
    </where>
</select>
1
2
3
4
5
6
7
8
9
10
11
12

# 4、set标签

update 语句可以使用 set 标签动态更新列。set 标签可以为 SQL 语句动态的添加 set 关键字,剔除追加到条件末尾多余的逗号。

<update id="updateWebsite" parameterType="net.biancheng.po.Website">
  UPDATE website
  <set>
    <if test="name!=null">name=#{name}</if>
    <if test="url!=null">url=#{url}</if>
  </set>
  WHERE id=#{id}
</update>
1
2
3
4
5
6
7
8

# 5、foreach标签

对于一些 SQL 语句中含有 in 条件,需要迭代条件集合来生成的情况,可以使用 foreach 来实现 SQL 条件的迭代。

  1. foreach 标签主要有以下属性:
  • item:表示集合中每一个元素进行迭代时的别名
  • index:指定一个名字,表示在迭代过程中每次迭代到的位置
  • open:表示该语句以什么开始(既然是 in 条件语句,所以必然以(开始)
  • separator:表示在每次进行迭代之间以什么符号作为分隔符(既然是 in 条件语句,所以必然以,作为分隔符)
  • close:表示该语句以什么结束(既然是 in 条件语句,所以必然以)开始)
  1. 使用 foreach 标签时,最关键、最容易出错的是 collection 属性,该属性是必选的,但在不同情况下该属性的值是不一样的,主要有以下 3 种情况:
  • 如果传入的是单参数且参数类型是一个 List,collection 属性值为 list
  • 如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array
  • 如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key
<!-- WebsiteMapper.xml -->
<select id="selectWebsite" parameterType="net.biancheng.po.Website" resultType="net.biancheng.po.Website">
    SELECT id,name,url,age,country
    FROM website WHERE age in
    <foreach item="age" index="index" collection="list" open="(" separator="," close=")">
        #{age}
    </foreach>
</select>

//WebsiteMapper.java
public List<Website> selectWebsite(List<Integer> ageList);

//test.java
public class Test {
    public static void main(String[] args) throws IOException {
        // 读取配置文件mybatis-config.xml
        InputStream config = Resources.getResourceAsStream("mybatis-config.xml"); // 根据配置文件构建
        SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);
        // 通过SqlSessionFactory创建SqlSession
        SqlSession ss = ssf.openSession();
        List<Integer> ageList = new ArrayList<Integer>();
        ageList.add(10);
        ageList.add(12);
        List<Website> siteList = ss.selectList("net.biancheng.mapper.WebsiteMapper.selectWebsite", ageList);
        for (Website ws : siteList) {
            System.out.println(ws);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 6、bind标签

bind 标签可以通过 OGNL 表达式自定义一个上下文变量

<select id="selectWebsite" resultType="net.biancheng.po.Website">
    <bind name="pattern" value="'%'+_parameter+'%'" />
    SELECT id,name,url,age,country
    FROM website
    WHERE name like #{pattern}
</select>
1
2
3
4
5
6

# 7、trim标签

在 MyBatis 中除了使用 if+where 实现多条件查询,还有一个更为灵活的元素 trim 能够替代之前的做法。

trim 一般用于去除 SQL 语句中多余的 AND 关键字、逗号或者给 SQL 语句前拼接 where、set 等后缀,可用于选择性插入、更新、删除或者条件查询等操作。

<select id="selectWebsite" resultType="net.biancheng.po.Website">
    SELECT id,name,url,age,country
    FROM website
    <trim prefix="where" prefixOverrides="and">
        <if test="name != null and name !=''">
            AND name LIKE CONCAT ('%',#{name},'%')
        </if>
        <if test="url!= null">
            AND url like concat ('%',#{url},'%')
        </if>
    </trim>
</select>
1
2
3
4
5
6
7
8
9
10
11
12
Last Updated: 11/11/2021, 11:04:53 AM