5. SQL映射文件
Mapper.xml映射文件中定义了操作数据库的sql,每个sql是一个statement,映射文件是mybatis的核心。
5.1 parameterType(输入类型)
5.1.1 #{}与${}
#{}实现的是向prepareStatement中的预处理语句中设置参数值,sql语句中#{}表示一个占位符即?。
<!-- 根据id查询用户信息 -->
<select id="findUserById" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
使用占位符#{}可以有效防止sql注入,在使用时不需要关心参数值的类型,mybatis会自动进行java类型和jdbc类型的转换。#{}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
${}
和#{}
不同,通过${}
可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}
可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}
括号中只能是value。使用${}
不能防止sql注入,但是有时用${}会非常方便,如下的例子:
<!-- 根据名称模糊查询用户信息 -->
<select id="selectUserByName" parameterType="string" resultType="user">
select * from user where username like '%${value}%'
</select>
如果本例子使用#{}则传入的字符串中必须有%号,而%是人为拼接在参数中,显然有点麻烦,如果采用${}在sql中拼接为%的方式则在调用mapper接口传递参数就方便很多。
//如果使用#{}占位符号则必须人为在传参数中加%
List<User> list = userMapper.selectUserByName("%张%");
//如果使用${}原始符号则不用人为在参数中加%
List<User>list = userMapper.selectUserByName("张");
再比如order by排序,如果将列名通过参数传入sql,根据传的列名进行排序,应该写为:
ORDER BY ${columnName}
如果使用#{}将无法实现此功能。
5.1.2 java的对象类型
略,参考前面对user的操作
5.1.3 Java的包装对象类型(一个类包含另外一个类的属性)
开发中通过传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。
解决方式: 建议使用自定义的包装类型。在包装类型中将复杂的查询条件包装进去。
- 创建一个类,用来封装查询User的条件:
/**
* 用户条件类
* @author pactera
*
*/
public class UserCondition {
//为了程序的可扩展性,建议重新创建一个类来作为User的查询条件类
private User user;
//...还可以加入其它的条件
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
- UserMapper接口
//根据用户条件查询用户
public List<User> queryByCondition(UserCondition userCondtion) throws Exception
- UserMapper.xml
<!-- 根据查询条件查询用户 -->
<select id="findUserByCondition" resultType="User" parameterType="UserCondition">
select * from t_user where id > #{user.id}
and username like '%${user.username}%'
</select>
5.2 输出映射
5.2.1 resultType
5.2.1.1 输出简单类型
Mapper.xml文件
<!-- 获取用户列表总数 -->
<select id="findUserCount" resultType="int">
select count(1) from user
</select>
Mapper接口:
public int findUserCount() throws Exception;
总结:
查询出来的结果集只有一行且一列,可以使用简单类型进行输出映射
5.2.1.2 输出Java对象类型
不管是输出的单个对象还是一个列表(list中包括对象),在mapper.xml中resultType指定的类型是一样的。都是对象类型
注意:
使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。
如果查询出来的列名和对象中的属性名全部不一致,没有创建对象。
只要查询出来的列名和对象中的属性有一个一致,就会创建对象。
5.2.2 resultMap
在使用resultType作为输出参数,会有以下问题:
-
如果我们的表的列名与对象的属性名不一致,使用resultType时,进行属性映射.
-
如果我们的对象中包含了另外一个对象(一对一,一对多,多对多关联关系),使用resultType进行映射 .
解决上面问题,就需要使用resultMap来作为输出参数.
1. 定义resultMap
用户表:
User类:
在Sql映射文件中,先要手动定义一个resultMap,把对象的属性与表的字段进行映射.
注意: resultType与resultMap不能共存.
6. 动态SQL
mybatis核心 对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接、组装。
6.1 if
<!-- 传递pojo综合查询用户信息 -->
<select id="findUserList" parameterType="user" resultType="user">
select * from user
where 1=1
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</select>
6.2 where
上边的sql也可以改为:
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</where>
</select>
注意:
<where />
可以自动处理第一个and。or
6.3 set
<!-- 修改用户 -->
<update id="updateUser" parameterType="org.csmf.mybatis.entity.User">
update t_user
<set>
<if test="name!=null and name !='' ">
username =#{name},
</if>
<if test="password !=null and password !='' ">
password =#{password},
</if>
<if test="sex!=null and sex !='' ">
sex =#{sex},
</if>
<if test="brithday!=null and brithday !='' ">
brithday =#{brithday},
</if>
<if test="address!=null and address !='' ">
address =#{address},
</if>
</set>
<where>
<if test="id!=null and id!=''">
id = #{id}
</if>
</where>
</update>
<set>
可以处理最后一个逗号
6.4 foreach
向sql传递List,mybatis使用foreach解析.
现在需要通过一组ID查询用户,该sql语句有两种
select * from t_user where id =1 or id=2 or id=3 or id=4;
select * from t_user where id in (1,2,3,4);
那么对应的Sql映射文件中的sql语句也有两种写法:
第一种:
UserMapper接口:
Sql映射文件:
<!-- 根据一组Id查询用户 -->
<select id="findUserByIds" resultType="User" parameterType="QueryVo">
select * from t_user
<where>
<!--select * from t_user where id=? or id=? or =? ... -->
<foreach collection="list" item="id" open="and ( " close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>
第二种sql的写法:
<!-- 根据一组Id查询用户 -->
<select id="findUserByIds" resultType="User" parameterType="QueryVo">
select * from t_user
<where>
<!--select * from t_user where id in (? ,?, ?...) -->
<foreach collection="list" item="id" open="and id in ( " close=")" separator=" , ">
#{id}
</foreach>
</where>
</select>
注意:
<foreach>
标签中collection属性不是写参数名,而是写list/array: List集合的参数写: list, 数组类型的参数写: array
6.5 sql片段
Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的,如下:
<!--综合查询用户信息 -->
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</where>
</select>
我们可以把上面的sql的条件单独抽取出来,方便其他的sql复用:
<!--将where条件抽取出来:-->
<sql id="query_user_where">
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</sql>
然后在其他需要使用到该sql片段的sql中使用<include>
标签引入
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<include refid="query_user_where"/>
</where>
</select>
注意:如果引用其它mapper.xml的sql片段,则在引用时需要加上namespace,如下:
<include refid="namespace.sql片段”/>