Mybatis中的输入输出映射
输入映射 -- 输入的参数类型:
1.简单类型 (略)
2.简单的pojo类型(类类型)(略)
3.自定义包装类型:
从前面的mapper接口的方法中可以看出来,每个方法都只能够传递一个参数,而且确实只能传一个参数。但是有些时候我们在关联查询的时候,可能查询条件有用户的信息还有商品的信息甚至还有订单的信息。那么这个时候,我们只能将所有的查询信息封装到一个对象中,然后使用这个对象作为输入参数。
举例:
查询: 根据用户的姓名和性别查询用户的信息
步骤:
1.定义用户扩展类
public class UserCustomer extends User {
private Double weight;
}
2.定义一个自定义的包装类,封装所有的查询条件
/**
* 自定义包装类, 封装所有的查询条件
* Author menglanyingfei
* Created on 2018.01.20 16:27
*/
public class QueryVo {
// 封装用户信息
private UserCustomer userCustomer;
// 封装商品信息
// private Items items;
public UserCustomer getUserCustomer() {
return userCustomer;
}
public void setUserCustomer(UserCustomer userCustomer) {
this.userCustomer = userCustomer;
}
}
3.定义接口中的方法
/**
* mapper开发的接口
* Created by menglanyingfei on 2018/1/20.
*/
public interface UserMapper {
// 根据用户的姓名和性别查询用户的信息, 采用包装类型作为输入参数
List<UserCustomer> findUserByNameAndSex(QueryVo queryVo);
}
4.编写映射文件(注意, 这里的输入输出类型都采用了类型别名)
<!-- 根据用户名和性别查询用户的信息, 使用包装类类型作为输入参数
取值的时候, 首先通过包装类中 属性名.属性名.属性名 -->
<select id="findUserByNameAndSex" parameterType="QueryVo"
resultType="UserCustomer">
select * from `user` where sex=#{userCustomer.sex}
and username like '%${userCustomer.username}%'
</select>
5.测试代码
@Test
public void findUserBySexAndUsername() {
// 获取SqlSession
SqlSession session = factory.openSession();
// 通过SqlSession对象得到Mapper接口的一个代理对象
// 需要传递的参数是Mapper接口的类型
UserMapper userMapper = session.getMapper(UserMapper.class);
// 构建输入参数QueryVo
QueryVo queryVo = new QueryVo();
// 创建UserCustomer对象作为queryVo的属性
UserCustomer userCustomer = new UserCustomer();
userCustomer.setUsername("小");
userCustomer.setSex("1");
// 将UserCustomer对象设置到QueryVo中
queryVo.setUserCustomer(userCustomer);
// 通过代理对象调用UserMapper中的方法
List<UserCustomer> userList = userMapper.findUserByNameAndSex(queryVo);
System.out.println(userList);
// 红色标识!
System.err.println(userMapper);
// 关闭资源
session.close();
}
输入映射为HashMap类型
查询:根据用户的姓名和性别查询用户的信息, 使用HashMap作为输入参数类型
接口中的方法:
List<UserCustomer> findUserByHashMap(HashMap hashMap);
映射文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
如果采用的mapper代理开发模式, 那么映射文件的命名空间必须是
Mapper接口的全路径 -->
<mapper namespace="com.wtu.mapper.UserMapper">
<!-- 根据用户名和性别查询用户的信息, 使用HashMap作为输入参数
占位符中的参数是HashMap中key -->
<select id="findUserByHashMap" parameterType="java.util.HashMap"
resultType="UserCustomer">
select * from `user` where sex=#{sex} and username like '%${username1}%'
</select>
</mapper>
测试代码:
@Test
public void findUserByHashMap() {
// 获取SqlSession
SqlSession session = factory.openSession();
// 通过SqlSession对象得到Mapper接口的一个代理对象
// 需要传递的参数是Mapper接口的类型
UserMapper userMapper = session.getMapper(UserMapper.class);
// 创建HashMap对象
HashMap<String, Object> map = new HashMap<>();
map.put("sex", "1");
map.put("username1", "小");
// 通过代理对象调用UserMapper中的方法
List<UserCustomer> userList = userMapper.findUserByHashMap(map);
System.err.println(userList);
System.out.println(map.get("username1"));
// 关闭资源
session.close();
}
输出映射
ResultType关键字的使用
输出简单类型
根据用户名和性别统计一共有多少个这样的用户
映射文件的编写
<!-- 根据姓名和性别统计人数, 采用QueryVo作为输入参数 -->
<select id="findUserCount" parameterType="queryVo" resultType="int">
select count(*) from `user`
where username like '%${userCustomer.username}%'
and sex = #{userCustomer.sex}
</select>
接口中的方法:
public interface UserMapper {
// 根据姓名和性别统计人数, 采用QueryVo作为输入参数
Integer findUserCount(QueryVo queryVo);
}
测试代码:
public class UserMapperTest {
private SqlSessionFactory factory;
@Before
public void getFactory() throws Exception {
this.factory = new SqlSessionFactoryBuilder().build(
Resources.getResourceAsStream("mybatis/sqlMapConfig.xml")
);
}
@Test
public void findUserCount() {
SqlSession session = factory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
QueryVo queryVo = new QueryVo();
UserCustomer userCustomer = new UserCustomer();
userCustomer.setSex("1");
userCustomer.setUsername("小");
queryVo.setUserCustomer(userCustomer);
Integer count = userMapper.findUserCount(queryVo);
System.out.println(count);
// System.err.println(userMapper);
session.close();
}
}
ResultType输出HashMap
将一条查询记录的字段名作为map的key,字段值作为map的value 进行输出
根据id查询用户:
接口:
// 根据id查询用户, 采用HashMap作为输出映射
HashMap<String, Object> findUserById(Integer id);
映射文件:
<!-- 根据id查询用户, 采用HashMap作为输出映射 -->
<select id="findUserById" parameterType="int" resultType="java.util.HashMap">
select * from `user` where id = #{value}
</select>
测试代码:
@Test
public void findUserById() {
SqlSession session = factory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
HashMap<String, Object> map = userMapper.findUserById(28);
System.out.println(map);
session.close();
}
ResultType的小结
只有当数据库中查询出来的字段名和pojo中的set方法的方法名的后面一截相同的时候才能够将该字段的值成功的映射到该对象中。如果不同则不能映射成功。
如果查询出来的字段名和pojo中的set方法的方法名的后面一截没有一个相同的,那么这个时候连对象都没有,那么如何解决这个问题: 使用ResultMap 来完成映射关系
ResultMap的使用
根据id查询用户, 采用ResultMap 进行输出
映射文件:
<!-- 创建ResultMap片段
type: 输出的类型
id: 唯一表示该ResultMap
-->
<resultMap id="findUserResultMap" type="user">
<!-- id: 查询结果集中唯一标示该条记录的字段名
column: 字段名 property: pojo中的属性名
result: 普通字段名 -->
<id column="id_" property="id"/>
<result column="姓名" property="username"/>
<result column="性别" property="sex"/>
<result column="生日" property="birthday"/>
<result column="地址" property="address"/>
</resultMap>
<!-- 根据id查询用户, 采用ResultMap作为输出 -->
<select id="findUserByResultMap" parameterType="int" resultMap="findUserResultMap">
select id id_, sex 性别, username 姓名,
birthday 生日, address 地址 from `user` where id = #{value}
</select>
接口:
// 根据id查询用户, 采用ResultMap作为输出类型
User findUserByResultMap(Integer id);
测试代码:
@Test
public void findUserByResultMap() {
SqlSession session = factory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.findUserByResultMap(28);
System.out.println(user);
session.close();
}
动态sql
Mybatis中提供了<where>
和<if>
来进行sql片段的拼接 ,满足不同的需求.
根据性别和姓名查询用户信息,如果没有输入查询条件那么将会查询所有信息
sql片段
将多条sql中公共的部分抽取出来, 定义一个sql片段, 然后可以在其他地方引入该sql片段,但是要注意, 如果引入是其他映射文件中的sql片段,那么在sql片段的id前面要加上命名空间
定义sql片段
<!-- 定义SQL片段 id: 唯一标识 -->
<sql id="queryUser">
<if test="userCustomer != null">
<if test="userCustomer.username != null and userCustomer.username != ''">
and username like '%${userCustomer.username}%'
</if>
<if test="userCustomer.sex != null and userCustomer.sex != ''">
and sex = #{userCustomer.sex}
</if>
</if>
</sql>
引入sql片段
映射文件:
<!-- 根据性别和姓名查询用户信息, 如果没有输入查询条件, 则查询所有 -->
<select id="findUserByUserNameAndSex" parameterType="queryVo"
resultType="userCustomer">
select * from `user`
<!-- where标签会自动去掉拼接段里面的第一个and -->
<where>
<!-- 引入一个SQL片段, refid: 引入那个SQL片段的id -->
<include refid="queryUser"/>
</where>
</select>
查询用户名中带狗字, 性别为1, id为28或者40的用户信息
QueryVo中封装一个ids集合属性, 用来传递多个id值
/**
* 自定义包装类, 封装所有的查询条件
* Author menglanyingfei
* Created on 2018.01.20 16:27
*/
public class QueryVo {
// 封装用户信息
private UserCustomer userCustomer;
private List<Integer> ids;
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
public UserCustomer getUserCustomer() {
return userCustomer;
}
public void setUserCustomer(UserCustomer userCustomer) {
this.userCustomer = userCustomer;
}
}
映射文件的编写:
<!-- 查询用户名中带小字, 性别为1, 用户id为28, 30的用户 -->
<select id="findUserByUserNameAndSex2" parameterType="queryVo" resultType="userCustomer">
select * from `user`
<!-- where标签会自动去掉拼接段里面的第一个and -->
<where>
<include refid="queryUser"/>
<!-- and id IN(28, 30)
collection: 遍历的集合属性名
open: 开始拼接的字符串
close: 结束时的字符串
separator: 集合中元素之间的分隔符
item: 遍历出来的每一个元素对象
-->
<if test="ids != null">
<foreach collection="ids" open="and id IN(" close=")" separator="," item="id1">
#{id1}
</foreach>
</if>
</where>
</select>
Mybatis的高级映射
一对一映射
查询订单信息,并且关联查询出该订单对应的用户的姓名,性别,地址
这个过程中存在着 订单 ---> 用户 的一对一关系
通过观察发现结果集中既有订单的信息,也有用户的信息,所以我们可以定义一个类封装了所有的查询字段。然后采用该类型作为输出类型。
实体类:
public class OrdersCustomer extends Orders {
// 封装用户信息
private String username;
private String sex;
private String address;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
映射文件的编写:
<!--查询订单信息, 并且查询该订单对应的用户部分信息 -->
<select id="findOrdersAndUser" resultType="ordersCustomer">
select orders.*, `user`.username, `user`.sex , `user`.address
from orders, `user`
where orders.`user_id` = user.`id`
</select>
接口中方法编写:
// 查询订单信息, 并且查询该订单对应的用户部分信息
List<OrdersCustomer> findOrdersAndUser();
测试代码:
public class OrdersMapperTest {
private SqlSessionFactory factory;
@Before
public void getFactory() throws Exception {
this.factory = new SqlSessionFactoryBuilder().build(
Resources.getResourceAsStream("mybatis/sqlMapConfig.xml")
);
}
// 查询订单信息, 并且查询该订单对应的用户部分信息
@Test
public void findOrdersAndUser() {
SqlSession session = factory.openSession();
OrdersMapper ordersMapper = session.getMapper(OrdersMapper.class);
List<OrdersCustomer> ordersCustomerList = ordersMapper.findOrdersAndUser();
for (OrdersCustomer oc : ordersCustomerList) {
System.out.println(oc.getUsername() + " -- " + oc.getSex() + " -- "
+ oc.getAddress());
}
session.close();
}
}
使用ResultMap 实现一对一映射
此时我们可以在Orders类中直接对用户信息进行封装,直接可以封装一个User对象
public class Orders {
private Integer id;
private Integer user_id;
private Integer number;
private Date createtime;
private String note;
// 封装用户信息
private User user;
// ...
}
映射文件的编写:
<!--定义查询订单和用户的ResultMap
type: 单条查询结果集映射的类型
<id/>: 结果集中唯一标识一行记录的字段
column: 字段名 property: 该字段名映射的Orders中的属性名
<result/>: 普通字段 -->
<resultMap id="findOrdersAndUser" type="orders">
<id column="id" property="id"/>
<result column="user_id" property="user_id"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 映射用户相关的信息
<association/>: 将查询结果映射到一个pojo中的另一个pojo中
映射到Orders中的user属性
property: 查询结果映射到Orders类哪个属性中
javaType: 该pojo的类型 -->
<association property="user" javaType="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作为输出参数类型 -->
<select id="findOrdersAndUserByResultMap" resultMap="findOrdersAndUser">
select orders.*, `user`.username, `user`.sex , `user`.address
from orders, `user`
where orders.`user_id` = user.`id`
</select>
接口中方法定义:
// 查询订单信息, 并且查询该订单对应的用户部分信息, 采用ResultMap作为输出参数类型
List<Orders> findOrdersAndUserByResultMap();
测试代码:
// 查询订单信息, 并且查询该订单对应的用户部分信息, 采用ResultMap作为输出参数
@Test
public void findOrdersAndUserByResultMap() {
SqlSession session = factory.openSession();
OrdersMapper ordersMapper = session.getMapper(OrdersMapper.class);
List<Orders> ordersList = ordersMapper.findOrdersAndUserByResultMap();
System.out.println(ordersList);
session.close();
}
一对多映射
分析订单表和订单明细表:
Orderdetail(orders_id) ------->关联------>orders(id)
Orders(user_id) ----->关联----->user(id)
orders---->orderdetail : 一对多的关系
orderdetail----->orders : 一对一的关系
查询订单信息并且关联查询用户信息和对应的订单明细的信息
实体类:
public class Orders {
private Integer id;
private Integer user_id;
private Integer number;
private Date createtime;
private String note;
// 封装用户信息
private User user;
// 封装订单明细的信息
private List<Orderdetail> odlist;
// ...
}
映射文件:
<!-- 查询订单信息并且关联用户信息和对应的订单明细的信息
extends: 一个resultMap片段去继承另外一个resultMap片段公共的部分 -->
<resultMap id="findOrdersAndUserAndOrderdetail" type="orders"
extends="findOrdersAndUser">
<!-- 映射订单中订单明细的信息, 映射一对多的关系
property: 映射订单类中的哪个属性
ofType: 集合中元素的类型
<id/>: 结果集中唯一标识一行记录的字段
<result/>: 普通字段 -->
<collection property="odlist" ofType="orderdetail">
<id column="orderdetail_id" property="id"/>
<result column="items_num" property="items_num"/>
<result column="orders_id" property="orders_id"/>
</collection>
</resultMap>
<!-- 查询订单信息并且关联用户信息和对应的订单明细的信息 -->
<select id="findOrdersAndUserAndOrderdetail" resultMap="findOrdersAndUserAndOrderdetail">
select orders.*, `user`.username, `user`.sex , `user`.address
,orderdetail.id orderdetail_id, orderdetail.items_num, orderdetail.orders_id
from orders, `user`, `orderdetail`
where orders.`user_id` = user.`id` AND
orders.`id` = orderdetail.orders_id
</select>
接口中的方法:
// 查询订单信息并且关联用户信息和对应的订单明细的信息
List<Orders> findOrdersAndUserAndOrderdetail();
多对多映射
查询用户信息并且关联查询用户购买的商品的信息
通过查看表结构我们知道 用户表和商品表在数据库的结构上面没有任何直接关联
通过ResultMap进行输出
用户-->订单 一对多
订单-->订单明细 一对多
订单明细-->商品 一对一
实现过程:
1.在User类中 有一个List<Orders> ordersList;
2.在Orders类型 有一个List<Orderdetail> odlist;
3.在Orderdetail类中 有一个Items items
实体类:
public class Items {
private Integer id;
private String name;
private Double price;
private String detail;
private String pic;
private Date createtime;
// ...
}
public class Orderdetail {
private Integer id;
private Integer orders_id;
private Integer items_id;
private Integer items_num;
// 一个订单明细包括一个商品
private Items items;
// ...
}
public class Orders {
private Integer id;
private Integer user_id;
private Integer number;
private Date createtime;
private String note;
// 封装用户信息
private User user;
// 封装订单明细的信息
private List<Orderdetail> odlist;
// ...
}
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
// 一个用户包括多个订单
List<Orders> ordersList;
// ...
}
映射文件:
<!-- 查询用户信息并且关联查询用户购买的商品的信息 -->
<resultMap id="findUserAndItems" type="user">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<!-- 映射订单的信息 -->
<collection property="ordersList" ofType="orders">
<id column="id" property="id"/>
<result column="user_id" property="user_id"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 映射订单明细的信息 -->
<collection property="odlist" ofType="orderdetail">
<id column="orderdetail_id" property="id"/>
<result column="items_num" property="items_num"/>
<!-- 映射商品的信息 -->
<association property="items" javaType="items">
<id column="商品id" property="id"/>
<result column="name" property="name"/>
<result column="price" property="price"/>
<result column="detail" property="detail"/>
</association>
</collection>
</collection>
</resultMap>
<!-- 查询用户信息并且关联查询用户购买的商品的信息 -->
<select id="findUserToItems" resultMap="findUserAndItems">
select orders.*,
`user`.username,
`user`.sex,
`user`.address,
orderdetail.id orderdetail_id,
orderdetail.items_num,
items.`name`,
items.price,
items.detail,
items.id 商品id
from `user`, items, orders, orderdetail
where `user`.id = orders.user_id
and orders.id = orderdetail.orders_id
and orderdetail.items_id = items.id
</select>
接口中方法定义:
// 查询用户信息并且关联查询用户购买的商品的信息
List<User> findUserToItems();
测试代码:
// 查询用户信息并且关联查询用户购买的商品的信息
@Test
public void findUserToItems() {
SqlSession session = factory.openSession();
OrdersMapper ordersMapper = session.getMapper(OrdersMapper.class);
List<User> userList = ordersMapper.findUserToItems();
System.out.println(userList.size());
session.close();
}
完整代码地址:(含SQL脚本)
https://github.com/menglanyingfei/SSMLearning/tree/master/mybatis_day03