一、输入映射
通过
parameterType
指定输入参数的类型,类型可以是简单类型、HashMap
、pojo
的包装类型。对于
HashMap
类型:
<select id="findUserByHashmap" parameterType="hashmap" resultType="User">
select * from user where id=#{id} and username like '%${username}%'
</select>
这里我们主要说明一下pojo
包装类型的使用情况。
- 需求:完整一个用户信息的综合查询,需要传入很多的查询条件,可能会包含用户信息,甚至一些其他的信息,比如商品、订单。针对这里的需求我们建议使用自定义的包装类型的
pojo
。
在pojo
中将复杂的条件包装进去。(工程mybatis04
)
UserQueryVo.java
package cn.itcast.pojo;
//包装类型
public class UserQueryVo {
//包装所需要的查询条件
private UserCustom userCustom ;
//还可以包装其他的查询条件,订单、商品。
public UserCustom getUserCustom() {
return userCustom;
}
public void setUserCustom(UserCustom userCustom) {
this.userCustom = userCustom;
}
}
注意:这里我们使用的UserCustom.java
类是User.java
类的一个增强类,当然其实这里这个类和User.java
是一样的,这里主要是为了说明以后如果发现某个pojo
的属性不够用,需要增强时不要直接在类中进行添加,而应该编写一个其增强类。
UserCustom.java
package cn.itcast.pojo;
//用户类的扩展类
public class UserCustom extends User{
//可以来扩展用户的信息
}
- 定义映射文件
mapper.xml
在UserMapper.xml
中定义用户信息的综合查询(查询条件复杂)。
<!-- 用户信息综合查询
#{userCustom.sex}:取出包装类型中性别的值
${userCustom.username}:取出包装类型中用户的名字 -->
<select id="findUserList" parameterType="UserQueryVo" resultType="UserCustom">
select * fromuser where user.sex = #{userCustom.sex} and user.username like '%${userCustom.username}%'
</select>
- 测试
UserMapperTest.java
//用户信息综合查询测试
@Test
public void findUserList(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//创建包装对象,设置查询条件
UserQueryVo userQueryVo = new UserQueryVo();
UserCustom userCustom = new UserCustom();
userCustom.setSex("1");
userCustom.setUsername("小明");
userQueryVo.setUserCustom(userCustom);
List<UserCustom> list = userMapper.findUserList(userQueryVo);
for(UserCustom user : list){
System.out.println(user.getUsername());
}
sqlSession.close();
}
二、输出映射
2.1 resultType
当我们使用这样sql时:
select * from user where user.sex = #{userCustom.sex} and user.username like '%${userCustom.username}%'
我们测试发现可以映射成功,但是如果这样:
SELECT id id_, username username_, sex, birthday FROM USER
WHERE user.sex = #{userCustom.sex} and user.username like '%${userCustom.username}%'
这里凡是使用了别名的,在测试时发现都不能映射成功,但是我们在数据库中是能够使用别名的。
总结:如果使用resultType
进行输出的映射,只有查询出来的列名和pojo
中的属性名一致,该列才可以映射成功,如果全部不一致,则不会创建pojo
对象,只要查询出的列名有一个一致,就会创建pojo
对象。
- 有一种需求:用户信息的综合查询列表总数,通过查询总数和上面用户综合查询列表才可以实现分页。也就是输出简单类型。
<!-- 用户信息综合查询总数 -->
<select id="findUserCount" parameterType="UserQueryVo" resultType="int">
select count(*) from user where user.sex = #{userCustom.sex} and user.username like '%${userCustom.username}%'
</select>
说明:查询出来的结果集只有一行且一列,才可以使用简单类型进行输出映射。
输出
pojo
对象和pojo
列表
不管是输出的单个对象还是输出的对象列表,resultType
指定的类型是一样的,不一样的是在接口中指定方法的返回值不一样,一个是User
,一个是List<User>
。输出
HashMap
输出pojo
对象可以该用HashMap
输出类型,将输出的字段名作为Map
的key
,value
为字段值。
2.2 resultMap
mybatis中可以使用
resultMap
完成高级输出结果映射,这里我们讲入门使用。如果查询出来的列名和
pojo
的属性名不一致,通过定义一个resultMap
对列名和属性名之间做一个映射。-
使用方法
- 定义
resultMap
- 使用
resultMap
作为statement
的输出映射类型
- 定义
需求:将下面的
sql
使用UserCutom
完成映射
SELECT id id_, username username_ FROM USER WHERE id = #{value}
这里User
类中属性名和sql
中查询列名不一致。注意:resultMap
的值就是下面定义的resultMap
的id
。
- 定义
resultMap
<!-- 定义resultMap,将SELECT id id_, username username_ FROM USER和User类中属性做一个映射
type:resultMap最终映射的java对象类型,可以使用别名
id:对resultMap的一个唯一标识 -->
<resultMap type="User" id="userResultMap">
<!-- id:表示查询结果集中唯一标识,column表示查询出来的列名,property表示type指定的pojo类型中的属性名
最终resultMap对column和property作一个映射关系(对应关系) -->
<id column="id_" property="id"></id>
<!-- result表示对普通名映射定义。column表示查询出来的列名,property表示type指定的pojo类型中的属性名
最终resultMap对column和property作一个映射关系(对应关系)-->
<result column="username_" property="username"/>
</resultMap>
说明:这里注意唯一标识和普通属性的区别,唯一标识一般是主键。
- 使用
resultMap
作为statement
的输出映射类型
<!-- 使用resultMap进行输出的映射,注意:如果上面定义的resultMap在其他mapper文件中,请加上namespace -->
<select id="findUserByIdResultMap" parameterType="UserQueryVo" resultMap="userResultMap">
SELECT id id_, username username_ FROM USER WHERE id = #{value}
</select>
- 接口
public User findUserByIdResultMap(int id );
- 测试
//用户信息综合查询总数测试
@Test
public void findUserByIdResultMap(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//创建包装对象,设置查询条件
UserQueryVo userQueryVo = new UserQueryVo();
UserCustom userCustom = new UserCustom();
userCustom.setSex("1");
userCustom.setUsername("小明");
userQueryVo.setUserCustom(userCustom);
User user = userMapper.findUserByIdResultMap(1);
System.out.println(user.getUsername());
sqlSession.close();
}
三、动态sql
mybatis的核心:对sql语句进行灵活的操作,通过表达式sql来进行判断,对sql进行灵活的拼接、组装。
3.1 需求(工程mybatis05
)
用户信息综合查询列表和用户信息查询列表总数这两个statement
的定义使用动态sql
。
- 对查询条件进行判断,如果如果输入参数不为空才进行查询条件的拼接。
<!-- 用户信息综合查询总数 -->
<select id="findUserCount" parameterType="UserQueryVo" resultType="int">
select count(*) from user
<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>
<!-- 用户信息综合查询-->
<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>
说明:这里我们就对这两个查询进行了改造。更多的标签请参考文档。
- 测试
//用户信息综合查询测试
@Test
public void findUserList(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//创建包装对象,设置查询条件
UserQueryVo userQueryVo = new UserQueryVo();
UserCustom userCustom = new UserCustom();
//由于这里使用了动态sql,如果不设置值,条件就不会拼接到sql中
//userCustom.setSex("1");
//userCustom.setUsername("小明");
userQueryVo.setUserCustom(userCustom);
List<UserCustom> list = userMapper.findUserList(userQueryVo);
for(UserCustom user : list){
System.out.println(user.getUsername());
}
sqlSession.close();
}
3.2 需求(工程mybatis06
)
将上面实现sql
动态判断的代码抽取出来,组成一个sql
片段。其他的statement
中就可以引用此sql
片段。
- 定义
sql
片段
<sql id="query_user_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>
</sql>
说明:
id
: 表示sql
片段的唯一标识。经验:一般定义
sql
片段都是基于单表定义,这样的话这个sql
片段的可重用性才会很高。在sql
片段中不要包括where
,便于在以后进行任意组装。引用
sql
片段
<!-- 用户信息综合查询总数 -->
<select id="findUserCount" parameterType="UserQueryVo" resultType="int">
select count(*) from user
<where>
<!-- 引用sql片段。如果指定的id不在本mapper文件中,需要在前面加上namespace,当然我们可能会引用多个sql片段 -->
<include refid="query_user_where"></include>
</where>
</select>
<!-- 用户信息综合查询-->
<select id="findUserList" parameterType="UserQueryVo" resultType="UserCustom">
select * from user
<!-- where可以自动的去掉条件中的第一个and -->
<where>
<include refid="query_user_where"></include>
</where>
</select>
- 测试
测试方法和之前的一样,这里不再多说。
3.3 foreach标签
如果现在向sql
中传递了一个List
(或数组等),mybatis使用foreach
解析
- 需求(工程
mybatis07
)
在用户查询列表和查询总数的statement
中增加多个id
输入查询。这里给出sql
语句:
(1)SELECT * FROM USER WHERE ... and (id = 1 OR id = 10 OR id = 16);
(2)SELECT * FROM USER WHERE ... and id IN(1, 10, 16)
这里两个sql
的功能是一样的。当然sql中可能会有多个条件。
- 在输出的参数类型中添加
List<Integer> ids
来传入多个id
UserQueryVo.java
package cn.itcast.pojo;
import java.util.List;
//包装类型
public class UserQueryVo {
//传入多个id
private List<Integer> ids ;
//包装所需要的查询条件
private UserCustom userCustom ;
//还可以包装其他的查询条件,订单、商品。
public UserCustom getUserCustom() {
return userCustom;
}
public void setUserCustom(UserCustom userCustom) {
this.userCustom = userCustom;
}
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
}
- 修改配置
需要修改sql片段
<!-- 定义一个sql片段-->
<sql id="query_user_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>
<if test="ids != null">
<!-- 这里我们要将:and ( id = 1 or id = 10 or id = 16)拼接进去 -->
<!-- 使用ForEach遍历传入的ids
collection:指定输入对象中集合属性
item:每次遍历生成的对象名
open:开始遍历时所要拼接的sql串
close:结束遍历时所要拼接的sql串
separator:遍历的两个对象中需要拼接的sql串-->
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
<!-- 每次遍历要拼接的sql串,下面#{id}中的id和item一致 -->
id = #{id}
</foreach>
</if>
</sql>
- 测试
//用户信息综合查询测试
@Test
public void findUserList(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//创建包装对象,设置查询条件
UserQueryVo userQueryVo = new UserQueryVo();
UserCustom userCustom = new UserCustom();
//由于这里使用了动态sql,如果不设置值,条件就不会拼接到sql中
userCustom.setSex("1");
//传入多个id
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(10);
ids.add(16);
userQueryVo.setIds(ids);
userQueryVo.setUserCustom(userCustom);
List<UserCustom> list = userMapper.findUserList(userQueryVo);
for(UserCustom user : list){
System.out.println(user.getUsername());
}
sqlSession.close();
}
- 如果我们使用第二条sql则可以这样写
<!-- 对 and id in(1, 10, 16)的拼接 -->
<foreach collection="ids" item="id" open="and id in(" close=")" separator=",">
<!-- 每次遍历要拼接的sql串,下面#{id}中的id和item一致 -->
#{id}
</foreach>