介绍
Mapper映射文件,作用是用来配置SQL映射语句,根据不同的SQL语句性质,使用不同的标签,mapper文件中常用的标签有<select>、<insert>、<update>、<delete>
增删改查的实现
一个简单的Mapper映射文件案例
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace:此处使用包名+文件名 的形式 -->
<mapper namespace="com.sl.mapper.ProductMapper">
<!--select – 映射查询语句
id:唯一标识 parameterType:参数类型,此处是根据id查询,类型为int resultType:返回值类型,
#{id}: 参数 -->
<select id="selectProduct" parameterType="int" resultType="com.sl.po.Product">
select * from products where id = #{id}
</select>
<!-- insert – 映射插入语句
注: sql中参数最好和po中属性命名一致
-->
<insert id="insertProduct" parameterType="com.sl.po.Product">
insert into products(name,description,UnitPrice,IsNew)
values(#{name},#{description},#{UnitPrice},#{IsNew})
</insert>
<!-- update – 映射更新语句 -->
<update id="updateProduct" parameterType="com.sl.po.Product">
update products set UnitPrice = #{UnitPrice},IsNew=#{IsNew} where id=#{id}
</update>
<!-- delete – 映射删除语句-->
<delete id="deleteProduct" parameterType="int">
delete from products where id=#{id}
</delete>
</mapper>
Mapper 输入参数映射配置
Mybatis中parameterType为输入参数类型,可以配置为基本数据类型、基本数据包装类型、或自定义数据类型(JavaBean),Sql语句中使用#{}或${}传入参数。
#{}实现的是向prepareStatement中的预处理语句中设置参数值,sql语句中#{}表示一个占位符,使用占位符#{}可以防止sql注入,使用时不需要关心参数值的类型,mybatis将自动进行java类型和jdbc类型的转换。
可
以
将
参
数
拼
接
在
s
q
l
中
且
不
进
行
j
d
b
c
类
型
转
换
,
{}可以将参数拼接在sql中且不进行jdbc类型转换,
可以将参数拼接在sql中且不进行jdbc类型转换,{}可以接收简单类型值或pojo属性值,如果传入的是个简单类型,括号中只能是value,即:${value}:
注:parameterType为非必填属性,下面的情况中parameterType省略不写也不会报错。
使用示例:
<!-- ${} sql拼接 -->
<select id="selectProductByName" parameterType="string" resultType="com.sl.po.Product">
select * from products where name like '%${value}%'
</select>
<!-- #{} 参数化 -->
<select id="selectProductByName2" parameterType="string" resultType="com.sl.po.Product">
select * from products where name like #{value}
</select>
映射基本数据类型
映射基本数据类型时,参数名也可用value代替,#{value} 即: select * from products where id = #{value}
使用示例:
<select id="selectProduct" parameterType="int" resultType="com.sl.po.Product">
select * from products where id = #{id}
</select>
映射pojo对象
参数名必须与pojo对象属性一致,此处UnitPrice 与 IsNew为Product属性。
<!-- pojo -->
<select id="selectProductByPoJo" parameterType="com.sl.po.Product" resultType="com.sl.po.Product">
select * from products where unitprice>#{UnitPrice} and isnew =#{IsNew}
</select>
映射pojo封装对象
参数名必须与pojo对象属性一致,这种方法不经常使用,直接使用上面映射pojo对象的方法就可以了。
使用示例:
<!-- pojo包装对象 -->
<select id="selectProductByVo" parameterType="com.sl.po.ProductVo" resultType="com.sl.po.Product">
select * from products where citycode=#{product.cityCode} and isnew =#{product.isNew}
</select>
映射hashmap
参数名必须与map中的key一致
使用示例:
<!-- hashmap -->
<select id="selectProductByHashMap" parameterType="hashmap" resultType="com.sl.po.Product">
select * from products where citycode=#{cityCode} and isnew =#{isNew}
</select>
Mapper 结果集映射配置
MyBatis的mapper映射文件中,resultType为输出结果集类型,同样支持基本数据类型及自定义数据类型。SQL语句查询后返回的结果集会映射到配置标签的输出映射属性对应的Java类型上。Mapper的输出映射有两种配置,分别是resultType和resultMap,注意两者不能同时使用。
映射基本数据类型
返回结果只有一行一列时可以使用基本数据类型
使用示例:
<select id="countProducts" resultType="int">
select count(1) from products
</select>
映射pojo对象或对象列表
返回自定义实体类类型 , 返回List<PoJo>的话,注意: resultType配置也是为PoJo类型,而不是List。查询的结果列名必须和实体类属性名一致。
使用示例:
<select id="selectProductById" parameterType="int" resultType="com.sl.po.Product">
select * from products where id = #{id}
</select>
映射hashmap或hashmap列表
返回自定义HashMap类型,与实体类的差别在于它不在乎查询的列名是什么。
使用示例:
<!-- 返回hashmap -->
<select id="selectProductById2" parameterType="int" resultType="hashmap">
select * from products where id = #{id}
</select>
<!-- 返回List<hashmap> -->
<select id="selectAllProduct2" resultType="hashmap">
select * from products
</select>
使用resultMap映射结果集
使用resultType可以映射结果集时需要pojo的属性名和sql查询结果的列名一致才可以映射成功。如果sql查询结果集字段名和pojo的属性名不一致,则需要通过resultMap将字段名和属性名作一个对应关系(sql 查询取别名与pojo属性一致也可以) ,resultMap实质上还需要将查询结果映射到pojo对象中。resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。
使用示例:
<!-- 使用resultMap映射结果集 -->
<select id="selectProductInfo" resultMap="productInfoResultMap">
select p.`Name` pName,p.Description,c.citycode,c.`name` cName
from products p
join city c
on p.citycode = c.citycode
</select>
<resultMap id="productInfoResultMap" type="com.sl.po.ProductInfo">
<result property="ProductName" column="pName"/>
<result property="CityName" column="cName"/>
</resultMap>
动态SQL
在mapper配置文件中,有时需要根据查询条件选择不同的SQL语句,或者将一些使用频率高的SQL语句单独配置,在需要使用的地方引用。Mybatis的一个特性:动态SQL,来解决这个问题。
mybatis动态sql语句是基于OGNL表达式的,主要有以下几类:
- if 语句 (简单的条件判断)
- choose (when,otherwize) ,相当于java 语言中的 switch ,与 jstl 中的choose 很类似
- trim (对包含的内容加上 prefix,或者 suffix 等,前缀,后缀)
- where (主要是用来简化sql语句中where条件判断的,能智能的处理 and or ,不必担心多余导致语法错误)、
- set (主要用于更新时)
- foreach (在实现 mybatis in 语句查询时特别有用)
if标签语句
if标签用来实现根据条件拼接sql语句,下面示例用来判断参数如果不为null,则拼接sql。
使用示例:
<select id="ifTest" resultType="com.sl.po.Product">
select * from products where
<if test="ProductName!=null">
name like #{ProductName}
</if>
<if test="description!=null">
and description like CONCAT(CONCAT('%', #{Description, jdbcType=VARCHAR}),'%')
</if>
</select>
当参数ProductName和Description不为null,则正常拼接处sql语句:select * from products where name like ? and description like CONCAT(CONCAT(’%’, ?),’%’)
where标签语句
当 where 中的条件使用的 if 标签较多时,这样的组合可能会导致错误, “where”标签会自动判断如果它包含的标签中有返回值的话,就在sql中插入一个‘where’,如果where标签最后返回的内容是以 and 或者or 开头的,也会被自动移除掉。
使用示例:
<select id="whereTest" resultType="com.sl.po.Product">
select * from products
<!-- where标签自动移除第一个and-->
<where>
<if test="Name!=null">
and name like #{Name}
<!--name like #{Name}-->
</if>
<if test="description!=null">
and description like #{Description}
</if>
</where>
</select>
set标签语句
set 标签是用在更新操作的时候,功能和 where 标签元素差不多,主要是在包含的语句前输出一个 set,然后如果包含的语句是以逗号结束的话将会把该逗号忽略,如果 set 标签最终返回的内容为空的话则可能会出错(update table where id=1)
使用示例:
<!-- if + set 实现按条件更新-->
<update id="setTest">
update products <!-- set标签将移除最后一个“,” -->
<set>
<if test="cityCode!=null">
citycode = #{cityCode} ,
</if>
<if test="Name!=null">
name = #{Name} ,
</if>
<if test="description!=null">
description = #{Description} ,
</if>
</set>
where id =#{id}
</update>
trim标签语句
trim 元素的主要功能是可以在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀,与之对应的属性是 prefix 和 suffix;可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,对应的属性是 prefixOverrides 和 suffixOverrides;正因为 trim 有这样的功能,它可以用来实现 where 和 set 的效果。
使用示例:
<!-- if+trim 使用trim代理where-->
<select id="trimwhereTest" resultType="com.sl.po.Product">
select * from products
<!-- 移除首部所有指定在 prefixOverrides 属性中的内容,并且插入 prefix 属性中指定的内容-->
<trim prefix="WHERE" prefixOverrides="AND | OR">
<if test="Name!=null">
and name like #{Name}
</if>
<if test="description!=null">
and description like #{Description}
</if>
</trim>
</select>
choose (when, otherwise)标签
choose标签是按顺序判断其内部when标签中的test条件出否成立,如果有一个成立,则 choose 结束。当 choose 中所有 when 的条件都不满则时,则执行 otherwise 中的sql,类似于sql server语句(case when then)
使用示例:
<!-- choose + when + otherwise 只能选择一个作为查询条件 作用类似sql case when then -->
<select id="choosewhenotherwiseTest" resultType="com.sl.po.Product">
select * from products
<where>
<choose>
<when test="name!=null">
and name like #{Name}
</when>
<when test="description!=null">
and description like #{Description}
</when>
<otherwise>
and unitprice > #{UnitPrice}
</otherwise>
</choose>
</where>
</select>
如果name!=null,则解析出sql: select * from product where name like ?
Name==null&& description!=null,则解析出sql: select * from product where description like ?
否则:select * from product where unitprice >?
foreach标签语句
mybatis提供foreach标签,用来对一个集合进行遍历,通常是用来构建 IN 条件语句,也可用于其他情况下动态拼接sql语句。
foreach标签有以下几个属性collection, item,index,open,separator,close。
- collection表示需要遍历的集合
- item 表示每次遍历时生成的对象名
- index表示在迭代过程中,每次迭代到的位置)
- open表示开始遍历时要拼接的字符串
- separator表示在每次遍历时两个对象直接的连接字符串
- close表示结束遍历时要拼接的字符串
当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
在使用foreach的时候针对不同的参数类型, collection属性值要分为以下3种情况:
7. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
8. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
9. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map或者Object。
单个集合使用示例:
<!-- 只有一个List参数时它的参数名为list,即collection="list" ; 如果参数类型时数组object[],则 collection="array" -->
<select id="foreachTest" resultType="com.sl.po.Product">
select * from products
<where>
<if test="list!=null">
<foreach item="id" index="index" collection="list" open="id in(" separator="," close=")">#{id}</foreach>
</if>
</where>
</select>
对象中含有集合属性使用示例:
<!-- 通过pojo传递list, collection值为pojo中对应的属性名-->
<select id="foreachVoTest" resultType="com.sl.po.Product">
select * from products
<where>
<if test="name!=null"> and name like #{name} </if>
<if test="ids!=null">
<foreach item="item" index="index" collection="ids" open="and id in(" separator="," close=")">#{item}</foreach>
</if>
</where>
</select>
Sql片段
Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的。
使用示例:
<select id="sqlTest" resultType="com.sl.po.Product">
select * from products
<where> <!-- 引用sql片段 -->
<include refid="sqltemp"/>
<!-- 提取sql片段
<if test="cityCode!=null">
and citycode = #{cityCode}
</if>
<if test="Name!=null">
and name like #{Name}
</if>
<if test="description!=null">
and description like #{Description}
</if>
-->
</where>
</select>
<!-- 定义sql片段 :将where条件提取 -->
<sql id="sqltemp">
<if test="cityCode!=null">
and citycode = #{cityCode}
</if>
<if test="Name!=null">
and name like #{Name}
</if>
<if test="description!=null">
and description like #{Description}
</if>
</sql>
Mapper接口动态代理
实现原理及规范
Mapper接口动态代理的方式需要手动编写Mapper接口,Mybatis框架将根据接口定义创建接口的动态代理对象,代理对象的方法体实现Mapper接口中定义的方法。
使用Mapper接口需要遵守以下规范:
- Mapper.xml文件中的namespace与mapper接口的类路径相同
- Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
- Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
- Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
编写Mapper.xml映射文件
定义mapper映射文件ProductMapper.xml,需要修改namespace的值为 ProductMapper接口路径。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace:此处使用包名+文件名 的形式 -->
<mapper namespace="com.sl.mapper.ProductMapper">
<!-- 返回自定义类型 注意 selectAllProduct返回的是集合,这种情况下resultType是集合包含的类型,而不能是集合本身 -->
<select id="selectAllProduct" resultType="com.sl.po.Product">
select * from products
</select>
<select id="selectProductsByVo" resultType="com.sl.po.Product">
select * from products
<where>
<if test="product.cityCode!=null">
and citycode = #{product.cityCode}
<!-- citycode = #{cityCode} -->
</if>
<if test="product.Name!=null">
and name like #{product.Name}
</if>
<if test="product.Description!=null">
and description like #{product.Description}
</if>
</where>
</select>
</mapper>
编写Mapper.java接口
接口定义注意点:
- Mapper接口方法名和Mapper.xml中定义的statement的id相同
- Mapper接口方法的输入参数类型和mapper.xml中定义的statement的parameterType的类型相同
- Mapper接口方法的输出参数类型和mapper.xml中定义的statement的resultType的类型相同
package com.sl.mapper;
import java.util.List;
import com.sl.po.Product;
import com.sl.po.ProductVo;
public interface ProductMapper {
List<Product> selectAllProduct();
List<Product> selectProductsByVo(ProductVo vo);
}
注册Mapper.xml配置文件(或者Mapper.java接口)
修改SqlMapConfig.xml文件:
<mappers>
<!-- 注册productMapper.xml文件 -->
<mapper resource="mapper/productMapper.xml"></mapper> <!-- mapper.xml文件和mapper接口可以不在一个包下 -->
<!-- 注册mapper接口 -->
<!-- <mapper class="com.sl.mapper.ProductMapper"></mapper> --> <!--通过注册mapper接口方式: Mapper接口和mapper.xml必须在同一个包下 -->
</mappers>
由于参数类型在mapper.xml配置文件中ParameterType配置,所以Mapper.java中接口方法只有一个参数,且类型与mapper.xml中配置的相同(mapper.xml可省略参数类型配置)。