Mapper.xml映射文件中定义了操作数据库的sql,每一个sql是一个statement,映射文件是myBatis的核心。
1.输入映射(ParameterType)
通过parameterType指定输入参数的类型,类型可以是简单类型,pojo,包装类型,hashmap。
1.1#{}与${}
#{}实现向prepareStatement中的预处理语句中设置参数值,sql语句中#{}表示一个占位符。
<select id="findUserById" parameterType="int" resultType="User"> SELECT * FROM USER WHERE id = #{value} </select>
使用占位符可以防止sql的注入,在使用时不需要关心参数的类型,mybatis会自动的进行Java与jdbc的转换。#{}可以接受简单类型和pojo的属性值。如果parameterType中指定的是单个的简单类型,#{}中可以使用value或是其他值。
#{}与${}的不同:
通过${}可以将parameterType传入的内容拼接在sql中,并且不进行jdbc类型转换,${}可以接受简单类型和pojo的属性值,若parameterType为单个简单类型时,${}中必须使用value。使用${}不能防止sql注入,但有时会很方便,例如:
<select id="findUserByUsername" parameterType="java.lang.String" resultType="User"> SELECT * FROM USER WHERE username LIKE '%${value}%' </select>
再比如order by排序,如果将列名通过参数传入sql,根据传的列名进行排序,应该写为:
ORDER BY${columnName}
如果使用#{}将无法实现此功能。
1.2传递包装类型对象:
开发中通过pojo传递查询条件,查询条件是综合的查询条件时,不仅包含用户的查询条件,而且包含其他的查询条件,这时可以使用包装对象类型传递参数。
1.定义包装类型对象:
public class QueryVo { private User user; //自定义用户扩展类 private UserCustom userCustom;
2.mapper.xml文件:
<!-- 用户信息的综合查询 这是输入映射传递pojo包装类对象的测试 UserQueryVo:就是UserCustom的包装类,在其中也可以包装其他类的对象 UserCustom:是User的扩展类--> <select id="findUserList" parameterType="queryVo" resultType="UserCustom"> SELECT * FROM USERwhere user.sex=#{userCustom.sex} and user.username LIKE '%${userCustom.username}%' </select>
说明:mybatis底层是通过ognl从pojo中获取属性值:#{userCustom.sex},userCustom即是传入的pojo包装对象的属性值。 queryVo即是上面包装对象的别名。
1.3传递HashMap:
1.mapper.xml文件:
<select id="findUserListHashmap" parameterType="hashmap" resultType="UserCustom"> SELECT * FROM USERwhere user.sex=#{sex} AND user.username LIKE '%${name}%' </select>
2.测试文件:
public void findUserListHashmap() throws Exception { sqlSession session = sqlSessionFactory.openSession(); //通过session自动创建mapper代理对象 UserMapper userMap = session.getMapper(UserMapper.class); Map<String,Object> map = new HashMap<String,Object>(); map.put("sex","1"); map.put("name","小明"); List<UserCustom> list = userMap.findUserListHashmap(map); System.out.println(list); }
2.输出映射:(ResultType、ResultMap)
2.1ResultType,输出为简单类型:
1.mapper.xml文件:
<!-- 输出映射的测试,输出一个简单类型(查询数据记录总数) --> <select id="findCount" parameterType="UserQueryVo" resultType="int"> SELECT COUNT(*) FROM USERWhere </select>
输出简单类型时,必须查询出来的结果集有一条记录,最终将第一个字段的值转换为输出类型。使用session中的selectOne可进行查询单条记录。
2.2 ResultType,输出为pojo类型:
1.mapper.xml
<select id="findUserById" parameterType="int" resultType="User"> SELECT * FROM USER WHERE id = #{value} </select>
<select id="findUserById" parameterType="user" resultType="User"> SELECT * FROM USER WHERE name like '%${name}%' </select>
2.3ResultType总结:
输出pojo对象和输出pojo列表在sql中定义的resultType是一样的。
返回单个pojo对象要保证sql查询出来的结果集为单条,内部使用session.selectOne方法调用,mapper接口使用pojo对象作为方法返回值。
返回pojo列表表示查询出来的结果集可能为多条,内部使用session.selectList方法,mapper接口使用List<pojo>对象作为方法返回值。
2.4输出类型为hashMap:
输出类型可以改为hashMap,输出的字段名作为key,输出的字段值为value
2.5ResultMap(重点)
ResultType可以指定pojo,将查询结果映射成pojo的属性。但是要求数据库列名与pojo属性名必须相同,才能映射成功。
如过查询的字段名与pojo的属性名不同,则通过resultMap,将字段名和属性名做一个映射,resultMap实质上,还是将数据库字段映射到pojo中。
resultMap可以实现将查询结果映射为复杂类型的pojo。如:在查询结果映射对象中,包括pojo和list实现一对一和一对多的查询。
例子:
selectid,id_,name,name_ from user where id=#{id}
定义ResultMap:
<resultMap type="cn.edu.hpu.ssm.po.Orders" id="ordersMap"> <id column="id" property="id"/> <result column="user_id" property="userId"/> <result column="number" property="number"/> <result column="createtime" property="createtime"/> <result column="note" property="note"/> <association property="user" javaType="cn.edu.hpu.ssm.po.User"> <id column="user_id" property="id"/> <result column="username" property="username"/> <result column="sex" property="sex"/> <result column="address" property="address"/> </association> </resultMap>
ResultMap中参数的含义:
type:定义映射的最终类型。id:引用时的标识
<id: column="" property=""> :
id:表示查询结果集中唯一标识。column:查询出来的列名。property:type指定的pojo类中的属性名。
<result column="" property=""/>
reslut:对普通列的标识。
2.6小结:
使用ResultType进行结果映射时,只有查询出来的列名和pojo中的属性名一致时,才能映射成功。如果列名和pojo的属性名不一致,则需要通过ResultMap对列名和属性名进行一个映射。
3.动态sql:(重点)
1.whereif
mybatsi的核心,对sql语句进行灵活的操作,通过表达式进行判断,对sql进行灵活的拼接、组装。
可以对输出参数进行判断,若果输入参数不为空,或是符合条件才进行sql拼接。
<!-- 用户信息的综合查询 这是输入映射传递pojo包装类对象的测试 UserQueryVo:就是UserCustom的包装类,在其中也可以包装其他类的对象 UserCustom:是User的扩展类--> <select id="findUserList" parameterType="UserQueryVo" resultType="UserCustom"> SELECT * FROM USER <!-- where 子句能够自动消除第一个and --> <where> <if test="userCustom != null"> <if test="userCustom.sex != null and userCustom.sex != '' "> and user.sex=#{userCustom.sex} </if> <if test="userCustom.username != null and userCustom.username !='' "> and user.username LIKE '%${userCustom.username}%' </if> </if> </where> </select>
2.sql片段:
将上面动态sql判断代码块抽取出来,组成一个sql片段。其他的statement可以对其引用。
<!-- 自定义sql片段,便于复用 --> <sql id="sql_mQUery"> <if test="userCustom != null"> <if test="userCustom.sex != null and userCustom.sex != '' "> and user.sex=#{userCustom.sex} </if> <if test="userCustom.username != null and userCustom.username !='' "> and user.username LIKE '%${userCustom.username}%' </if> </if> </sql>
引用sql片段:
<!-- 输出映射的测试,输出一个简单类型(查询数据记录总数) --> <select id="findCount" parameterType="UserQueryVo" resultType="int"> SELECT COUNT(*) FROM USER <where> <!-- 如果指定的id对应的sql片段不在本mapper.xml中,则需要加入namespace --> <include refid="sql_mQUery"></include> <!-- 这里可以再指定sql片段 --> </where> </select>
3.ForEach
向sql传递数组或者List,Mybatis使用foreach解析:
例如:
SELECT * FROM USER WHERE id=1 OR id=10 ORid=16
1.在输入参数类型中添加List<Integer>:
public class UserQueryVo { private UserCustom userCustom; private List<Integer> ids;
2.mapper.xml:
<!-- 动态sql之foreach的练习 --> <select id="findUserByIds" parameterType="UserQueryVo" resultType="UserCustom"> SELECT * FROM USER <where> <if test="ids != null"> <!--使用foreach循环遍历 collection:指定集合的输入对象 item:每个遍历生成的对象 open:开始遍历时生成 close:结束遍历时生成 separator:遍历两个对象中间的拼接 --> <foreach collection="ids" item="user_id" open="And ( " close=")" separator="OR"> <!-- 每个遍历中所需拼接的字符串 --> id=#{user_id} </foreach> </if> </where> </select>