经过前面几讲对MyBatis框架的学习,我们对Mapper.xml映射文件或多或少有了一些了解。本文将会对Mapper.xml映射文件作更加细致的梳理,我首先会从Mapper.xml映射文件中的输入和输出映射开始。温馨提示:本文案例代码的编写是建立在前文《MyBatis快速入门第四讲——SqlMapConfig.xml配置文件详解》案例基础之上的!
Mapper.xml映射文件中定义了操作数据库的SQL,每个SQL就是一个Statement,映射文件是MyBatis框架的核心。
传递简单类型,我之前就已经讲过了,这里只给出案例,如下图所示。
MyBatis会使用OGNL表达式解析对象字段的值,#{}
或者${}
花括号中的值就是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映射文件中添加如下配置信息。
使用包装类型查询用户时,可使用OGNL表达式从对象中获取属性值,并且如果是包装对象还可以使用.
操作符来进行获取。
接着,我们就要在UserMapper接口中添加如下方法了。
最后,在UserMapperTest单元测试类编写一个如下的测试方法。
试着运行以上单元测试方法,你便可以看到Eclipse控制台打印出了如下内容。
传递HashMap在实际开发中用的很少,但我还是要讲一下。以传递HashMap综合查询用户信息为例来说,首先我们要在UserMapper.xml映射文件中添加如下配置信息。
然后,在UserMapper接口中添加如下方法。
最后,在UserMapperTest单元测试类编写一个如下的测试方法。
试着运行以上单元测试方法,你便可以看到Eclipse控制台打印出了如下内容。
有这样一个需求:要查询用户表中的总记录数。有需求,我们就要解决它。首先,在UserMapper.xml映射文件中添加如下配置信息。
然后,在UserMapper接口中添加一个如下方法声明。
接着,在UserMapperTest单元测试类编写一个如下的测试方法。
注意,在输出简单类型时,查询出来的结果集必须是只有一条记录,紧接着会将第一个字段的值转换为输出类型。试着运行以上单元测试方法,你便可以看到Eclipse控制台打印出了如下内容。
输出pojo对象,我之前就已经讲过了,这里只给出案例,如下图所示。
输出pojo列表,我之前同样已讲过,这里只给出案例,如下图所示。
这儿再给出一个案例,即查询订单的所有信息。咱们的mybatis数据库中已经有了一个订单表(orders表),如下图所示。
如何编写代码查询所有的订单呢?大家可能想都不会想,就会像下面这样做。首先在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,如下图所示。
很明显这并不是我们所想要的结果。为了达到我们预期的效果,可为user_id这一列加个别名,即将OrderMapper.xml映射文件中id为getOrderList的select元素修改成下面这个样子。
只要你返回的结果的列名和pojo类中的属性名一致,那么就可以自动映射了。这样当我们再次以Debug模式运行testGetOrderList方法时,就能达到我们预期的结果了,如下图所示。
这种为列取别名的方式比较简单粗暴,其实要达到我们所预期的效果,还有另一种方式,那就是使用resultMap这个属性,下面我就会讲到。
resultType虽然可以指定将查询结果映射为pojo,但需要pojo的属性名和SQL语句查询的列名一致方可映射成功。如果SQL语句查询字段名和pojo的属性名不一致,那么可以通过resultMap将字段名和属性名作一个对应关系,resultMap实质上还是需要将查询结果映射到pojo对象中。resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和List集合就能分别实现一对一查询和一对多查询。
下面我就使用resultMap这个属性来实现查询所有订单信息的需求,而不是像上面那样简单粗暴地给user_id列加个别名就完事了。首先,在OrderMapper.xml映射文件中添加一个如下的元素。
然后,定义一个上面已经命名为order_list_result_map的resultMap。由于上面的OrderMapper.xml映射文件中SQL语句查询的列和Order类中的属性不一致,因此需要定义resultMap,这样可以将SQL语句查询的列和Order类中的属性相对应起来。
针对以上
标签的配置,我觉得有必要说明如下几点:
:此标签表示查询结果集的唯一标识,非常重要。如果是多个字段为复合唯一约束,那么就需要定义多个
标签了;
:普通列使用result标签映射。id标签与result标签均有如下两个属性:
接着,在OrderMapper接口添加一个如下方法声明。
紧接着,在OrderMapperTest单元测试类中添加一个如下测试方法。
同样在以上单元测试方法中的for (Order order : list) {
这句代码处打一个断点,继而以Debug模式运行该方法,可以发现查询出来的每一个Order对象中的userId属性都有值了。
我们可通过MyBatis提供的各种标签方法实现动态拼接SQL语句。
现有这样一个需求:传递pojo类综合查询用户信息,更具体地说就是我们要能使用用户的username和sex更加灵活地查询用户信息。为了解决这个需求,我们就要使用
标签了。首先在UserMapper.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控制台打印出了如下内容。
读者也可以试着只给User对象的username属性赋值,或者只为sex属性赋值,又或者两者同时赋值。
UserMapper.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>
也可使用
标签改成下面这个样子。
<select id="getUserByPojo" parameterType="user" resultType="com.meimeixia.mybatis.pojo.User">
SELECT
`id`,
`username`,
`birthday`,
`sex`,
`address`
FROM
`user`
<where>
<if test="username != null and username != ''">
and username LIKE '%${username}%'
if>
<if test="sex != null and sex != ''">
and sex = #{sex}
if>
where>
select>
标签可以自动补上SQL语句中的where关键字,而且还能处理掉where条件中的第一个and关键字。还有一点需要我们注意,那就是只要用了
标签,就不能再手动地加上where关键字了!
现有这样一个需求:传入多个id查询用户信息。如若编写SQL语句,可用下面两条SQL语句来实现:
SELECT * FROM USER WHERE username LIKE '%张%' AND (id =10 OR id =89 OR id=16)
SELECT * FROM USER WHERE username LIKE '%张%' id IN (10,89,16)
为了解决这个需求,我们可以在QueryVo类中定义一个List集合类型的ids属性存储多个用户id,并为其添加getter/setter方法。
package com.meimeixia.mybatis.pojo;
import java.util.List;
/**
* 包装的POJO
* @author liayun
*
*/
public class QueryVo {
private User user;
private List<Integer> ids;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
}
然后,在UserMapper.xml映射文件中添加一个如下元素。
向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控制台打印出了如下内容。
SQL语句中可将重复的语句给提取出来,使用时用include标签引用即可,最终达到SQL语句重用的目的。例如像下面这样的元素:
<select id="getUserByIds" parameterType="queryvo" resultType="user">
SELECT
`id`,
`username`,
`birthday`,
`sex`,
`address`
FROM
`user`
<where>
<foreach collection="ids" open="`id` IN (" item="uId" separator="," close=")">
#{uId}
foreach>
where>
select>
我们可以将要查询的字段抽取出来。
<sql id="user_sql">
`id`,
`username`,
`birthday`,
`sex`,
`address`
sql>
然后,使用include标签来引用。
<select id="getUserByIds" parameterType="queryvo" resultType="user">
SELECT
<include refid="user_sql" />
FROM
`user`
<where>
<foreach collection="ids" open="`id` IN (" item="uId" separator="," close=")">
#{uId}
foreach>
where>
select>
注意:如果引用其它映射文件的SQL片段,则需要在引用时需要加上namespace,如下所示。
<include refid="namespace.SQL片段"/>