MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL

经过前面几讲对MyBatis框架的学习,我们对Mapper.xml映射文件或多或少有了一些了解。本文将会对Mapper.xml映射文件作更加细致的梳理,我首先会从Mapper.xml映射文件中的输入和输出映射开始。温馨提示:本文案例代码的编写是建立在前文《MyBatis快速入门第四讲——SqlMapConfig.xml配置文件详解》案例基础之上的!

输入映射和输出映射

Mapper.xml映射文件中定义了操作数据库的SQL,每个SQL就是一个Statement,映射文件是MyBatis框架的核心。

parameterType(输入类型)

传递简单类型

传递简单类型,我之前就已经讲过了,这里只给出案例,如下图所示。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第1张图片

传递pojo对象

MyBatis会使用OGNL表达式解析对象字段的值,#{}或者${}花括号中的值就是pojo属性名称。传递pojo对象,之前我也讲过了,这里同样只给出案例,如下图所示。
在这里插入图片描述

传递pojo包装对象

在实际开发中,通常都可以通过pojo来传递查询条件,这样的查询条件一般是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时便可以使用包装对象来传递输入参数了,即pojo类中包含pojo类。
例如有这样一个需求:根据用户名称模糊查询用户信息列表,查询条件要放到QueryVo类的user属性中。有需求,就要解决它。首先,我们要在com.meimeixia.mybatis.pojo包下新建一个QueryVo类,该类的内容如下:

package com.meimeixia.mybatis.pojo;

/**
 * 包装的POJO
 * @author liayun
 *
 */
public class QueryVo {

	private User user;
	
	public User getUser() {
		return user;
	}

	public void setUser(User user) {
		this.user = user;
	}
	
}

然后,我们就要在UserMapper.xml映射文件中编写SQL语句了,即在UserMapper.xml映射文件中添加如下配置信息。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第2张图片
使用包装类型查询用户时,可使用OGNL表达式从对象中获取属性值,并且如果是包装对象还可以使用.操作符来进行获取。
接着,我们就要在UserMapper接口中添加如下方法了。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第3张图片
最后,在UserMapperTest单元测试类编写一个如下的测试方法。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第4张图片
试着运行以上单元测试方法,你便可以看到Eclipse控制台打印出了如下内容。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第5张图片

传递HashMap

传递HashMap在实际开发中用的很少,但我还是要讲一下。以传递HashMap综合查询用户信息为例来说,首先我们要在UserMapper.xml映射文件中添加如下配置信息。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第6张图片
然后,在UserMapper接口中添加如下方法。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第7张图片
最后,在UserMapperTest单元测试类编写一个如下的测试方法。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第8张图片
试着运行以上单元测试方法,你便可以看到Eclipse控制台打印出了如下内容。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第9张图片

resultType(输出类型)

输出简单类型

有这样一个需求:要查询用户表中的总记录数。有需求,我们就要解决它。首先,在UserMapper.xml映射文件中添加如下配置信息。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第10张图片
然后,在UserMapper接口中添加一个如下方法声明。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第11张图片
接着,在UserMapperTest单元测试类编写一个如下的测试方法。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第12张图片
注意,在输出简单类型时,查询出来的结果集必须是只有一条记录,紧接着会将第一个字段的值转换为输出类型。试着运行以上单元测试方法,你便可以看到Eclipse控制台打印出了如下内容。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第13张图片

输出pojo对象

输出pojo对象,我之前就已经讲过了,这里只给出案例,如下图所示。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第14张图片

输出pojo列表

输出pojo列表,我之前同样已讲过,这里只给出案例,如下图所示。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第15张图片
这儿再给出一个案例,即查询订单的所有信息。咱们的mybatis数据库中已经有了一个订单表(orders表),如下图所示。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第16张图片
如何编写代码查询所有的订单呢?大家可能想都不会想,就会像下面这样做。首先在com.meimeixia.mybatis.pojo包下新建一个Order类。

package com.meimeixia.mybatis.pojo;

import java.util.Date;

public class Order {
	private Integer id;

	private Integer userId;

	private String number;

	private Date createtime;

	private String note;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public Integer getUserId() {
		return userId;
	}

	public void setUserId(Integer userId) {
		this.userId = userId;
	}

	public String getNumber() {
		return number;
	}

	public void setNumber(String number) {
		this.number = number == null ? null : number.trim();
	}

	public Date getCreatetime() {
		return createtime;
	}

	public void setCreatetime(Date createtime) {
		this.createtime = createtime;
	}

	public String getNote() {
		return note;
	}

	public void setNote(String note) {
		this.note = note == null ? null : note.trim();
	}

	@Override
	public String toString() {
		return "Order [id=" + id + ", userId=" + userId + ", number=" + number + ", createtime=" + createtime
				+ ", note=" + note + "]";
	}

}

然后,在com.meimeixia.mybatis.mapper包下创建一个OrderMapper.xml映射文件,其内容如下:



<mapper namespace="com.meimeixia.mybatis.mapper.OrderMapper">
	<select id="getOrderList" resultType="order">
		SELECT
			`id`,
			`user_id`,
			`number`,
			`createtime`,
			`note` 
		FROM
			`order`
	select>
mapper>

接着,在com.meimeixia.mybatis.mapper包下创建一个OrderMapper接口。

package com.meimeixia.mybatis.mapper;

import java.util.List;
import com.meimeixia.mybatis.pojo.Order;

/**
 * 订单持久化接口
 * @author liayun
 *
 */
public interface OrderMapper {

	/**
	 * 获取订单列表
	 * @return
	 */
	List<Order> getOrderList();
	
}

紧接着,创建以上OrderMapper接口的一个单元测试类,例如OrderMapperTest.java。

package com.meimeixia.mybatis.test;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import com.meimeixia.mybatis.mapper.OrderMapper;
import com.meimeixia.mybatis.pojo.Order;
import com.meimeixia.mybatis.utils.SqlSessionFactoryUtils;

public class OrderMapperTest {

	@Test
	public void testGetOrderList() {
		SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
		//获取OrderMapper接口的代理实现类
		OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
		List<Order> list = orderMapper.getOrderList();
		for (Order order : list) {
			System.out.println(order);
		}
		sqlSession.close();
	}

}

在以上单元测试方法中的for (Order order : list) {这句代码处打一个断点,继而以Debug模式运行该方法,可以发现查询出来的每一个Order对象中的userId属性值都为null,如下图所示。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第17张图片
很明显这并不是我们所想要的结果。为了达到我们预期的效果,可为user_id这一列加个别名,即将OrderMapper.xml映射文件中id为getOrderList的select元素修改成下面这个样子。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第18张图片
只要你返回的结果的列名和pojo类中的属性名一致,那么就可以自动映射了。这样当我们再次以Debug模式运行testGetOrderList方法时,就能达到我们预期的结果了,如下图所示。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第19张图片
这种为列取别名的方式比较简单粗暴,其实要达到我们所预期的效果,还有另一种方式,那就是使用resultMap这个属性,下面我就会讲到。

resultMap

resultType虽然可以指定将查询结果映射为pojo,但需要pojo的属性名和SQL语句查询的列名一致方可映射成功。如果SQL语句查询字段名和pojo的属性名不一致,那么可以通过resultMap将字段名和属性名作一个对应关系,resultMap实质上还是需要将查询结果映射到pojo对象中。resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和List集合就能分别实现一对一查询和一对多查询。
下面我就使用resultMap这个属性来实现查询所有订单信息的需求,而不是像上面那样简单粗暴地给user_id列加个别名就完事了。首先,在OrderMapper.xml映射文件中添加一个如下的元素。

<select id="getUserByPojo" parameterType="user" resultType="com.meimeixia.mybatis.pojo.User">
	SELECT
		`id`,
		`username`,
		`birthday`,
		`sex`,
		`address` 
	FROM
		`user` 
	WHERE 1 = 1
		<if test="username != null and username != ''">
			and username LIKE '%${username}%'
		if>
		<if test="sex != null and sex != ''">
			and sex = #{sex}
		if>
select>

注意,username要做不等于空字符串的校验。然后,在UserMapper接口中添加一个如下方法声明。

/**
  * 演示if标签的使用
  * @param user
  * @return
  */
List<User> getUserByPojo(User user);

接着,在UserMapperTest单元测试类中添加一个如下的测试方法。

@Test
public void testGetUserByPojo() {
	SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
	//获取接口的代理实现类,只不过不需要我们去写了
	UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
	User user = new User();
	user.setSex("1");
	user.setUsername("范");
	List<User> list = userMapper.getUserByPojo(user);
	for (User user2 : list) {
		System.out.println(user2);
	}
	sqlSession.close();
}

运行以上单元测试方法,你便可以看到Eclipse控制台打印出了如下内容。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第25张图片
读者也可以试着只给User对象的username属性赋值,或者只为sex属性赋值,又或者两者同时赋值。

where

UserMapper.xml映射文件中如下的元素。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第27张图片
向SQL语句中传递数组或List集合,MyBatis框架将使用foreach标签来解析。接着,在UserMapper接口中添加一个如下方法声明。

/**
  * 演示foreach标签的使用,根据用户id列表查询用户 
  * @param queryVo
  * @return
  */
List<User> getUserByIds(QueryVo queryVo);

紧接着,在UserMapperTest单元测试类中添加一个如下测试方法。

@Test
public void testGetUserByIds() {
	SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
	//获取接口的代理实现类,只不过不需要我们去写了
	UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
	QueryVo queryVo = new QueryVo();
	//Arrays.asList(1,25,29,30)用于构建id列表
	queryVo.setIds(Arrays.asList(1,25,29,30));
	List<User> list = userMapper.getUserByIds(queryVo);
	for (User user2 : list) {
		System.out.println(user2);
	}
	sqlSession.close();
}

运行以上单元测试方法,你便可以看到Eclipse控制台打印出了如下内容。
MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL_第28张图片

SQL片段

SQL语句中可将重复的语句给提取出来,使用时用include标签引用即可,最终达到SQL语句重用的目的。例如像下面这样的