06-Mybatis关联查询映射
这里采用一个案例来进行分析:(下面的案例都是采用Mapper接口编程的方式来实现)
数据库准备:在数据库中创建一个Mybatis数据库,并在中创建user(用户),orders(订单),orderdetail(订单详情),items(商品)表。
各表的设计:
user表:
orders表:
orderdetail表:
items表:
表与表的关系:
user/orders表:
user——>orders:一个用户可以有多个订单;一对多
orders——>user:一个订单只能由一个用户创建;一对一
orders/orderdetail表:
orders——>orderdetail:一个订单有多个订单详情,因为一个订单可能包含多个商品;一对多
orderdetail——>orders:一个订单详情只能包含在一个订单当中;一对一
orderdetail/items表:
orderdetail——>items:一个订单明细只能对应一个商品;一对一
items——>orderdetail:一个商品可以有多个订单明细;一对多。
为每一个表创建一个javabean对象
user表对应的User类
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//省略getter和setter方法
}
orders表应得Orders类
public class Orders {
private Integer id;
private Integer user_id;
private String number;
private Date createtime;
private String note;
//省略getter和setter方法
}
orderdetail表对应的Orderdetail类:
public class Orderdetail {
private Integer id;
private Integer ordersId;
private Integer itemsId;
private Integer itemsNum;
//省略getter和setter方法
}
items表对应的Items类:
public class Items {
private Integer id;
private String name;
private float price;
private String text;
private String pic;
private Date createtime;
//省略getter和setter方法
}
一,一对一查询
需求分析:
查询订单信息,关联查询用户信息。
sql语句:
确定查询主表:订单表
确定查询从表:用户表
SELECT
od.'id',od.'user_id' AS userId,od.'number',us.'username',us.'sex'
FROM orders AS od,user AS us
WHERE od.'user_id'=us.'id'
方式一:采用resultType结果集映射:
由于resultType结果集映射时把查询出来得结果集中得每一列和javaBean对象中得每个属性进行一一对应。然而在我们创建得javaBean中Orders表并不满足查询结果集得要求,因此这里就对Orders类进行拓展。
拓展javaBean:OrdersExt
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrdersExt extends Orders {
private String username;
private String sex;
}
编辑Mapper接口
public interface OrdersMapper {
public List<OrdersExt> findOrdersAndUser();
}
编写OrdersMapper.xml映射文件:
<?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 namespace="com.qingzi.mybatisdemo.repository.OdersMapper">
<select id="findOrdersAndUser" resultType="com.qingzi.mybatisdemo.entity.OrdersExt">
SELECT
od.'id',
od.'user_id' AS userId,
od.'number',
us.'username',
us.'sex'
FROM
orders AS od,
user AS us
WHERE od.'user_id'=us.'id'
</select>
</mapper>
编辑mybatis的主配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--通过properties标签,读取java配置文件的内容-->
<properties resource="db.properties" />
<!-- 配置mybatis环境配置信息 -->
<environments default="developments">
<environment id="developments">
<!-- 配置jdbc的事务控制,由mybatis进行管理-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源,采用mybatis连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${db.properties}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
<property name="url" value="${db.url}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--指定mapper.xml文件-->
<mapper resource="mapper/OrdersMapper.xml" />
</mappers>
</configuration>
方式二:采用resultMap结果集映射
使用resultMap对结果集映射,需要先声明resultMap,因为resultMap是对查询出来的结果集中的每一列进行手动指定映射到javaBean对象中哪个属性上去。
修改OrdersExt类:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderExt1 extends Orders {
private User user;
}
修改OrdersMapper.xml映射文件:
其中映射文件中的:; 该标签是一对一所使用的关联标签。
<?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 namespace="com.qingzi.mybatisdemo.repository.OrdersMapper">
<!-- 一对一resultMap结果映射 -->
<!-- 声明resultMap -->
<resultMap id="findOrdersAndUserRstMap" type="com.qingzi.mybatisdemo.entity.OrderExt1">
<!-- 订单信息 -->
<id column="id" property="id" />
<result column="userId" property="userId" />
<result column="number" property="number"/>
<!--用户信息(一对一)-->
<!-- property属性:指定关联信息映射到OrdersExt的哪个属性上 -->
<!-- javaType属性:指定关联属性的java类型 -->
<association property="user" javaType="com.qingzi.mybatisdemo.entity.User">
<id column="userId" property="id" />
<result column="username" property="userName" />
<result column="sex" property="sex" />
</association>
</resultMap>
<select id="findOrdersAndUserRstMap" resultMap="findOrdersAndUserRstMap">
SELECT
od.id,
od.user_id AS userId,
od.number,
us.username,
us.sex
FROM
orders AS od,
user AS us
WHERE od.user_id=us.id
</select>
</mapper>
修改Mapper接口:
public interface OrdersMapper {
public List<OrdersExt> findOrdersAndUserRstMap();
}
测试类:
public class OrdersMapperTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindOrdersAndUser(){
SqlSession sqlSession = sqlSessionFactory.openSession();
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
List<OrdersExt> lists = ordersMapper.findOrdersAndUser();
List<OrderExt1> lists1 = ordersMapper.findOrdersAndUserRstMap();
System.out.println(lists);
System.out.println(lists1);
sqlSession.close();
}
}
二,一对多关联查询
需求分析:
查询订单信息和查询订单明细信息
sql语句:
查询主表:订单表
查询从表:用户表和订单详情表
SELECT
orders.id,
orders.user_id,
orders.number,
user.sex,
orderdetail.id detailId,
orderdetail.items_id,
orderdetail.items_num
FROM
orders,
USER,
orderdetail
WHERE orders.user_id = user.id
AND orders.id = orderdetail.orders_id
编写javaBean:
这里查询信息有订单详情和用户信息,因此,Orders类不能满足我们映射得需求,这里同样采用对Orders类进行拓展
编写mapper接口:
编写OrderMapper.xml映射文件:
在映射文件中使用了 标签;该标签时一对多关联映射所使用的,把查询的结果集中有多个结果放在集合中去。
<?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 namespace="com.qingzi.mybatisdemo.repository.OrdersMapper">
<!-- 声明resultMap -->
<resultMap type="com.qingzi.mybatisdemo.entity.OrdersExt2" id="ordersAndOrderdetail">
<!-- 订单信息 -->
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<!-- 用户信息(一对一) -->
<association property="user" javaType="com.qingzi.mybatisdemo.entity.User">
<id column="user_id" property="id"/>
<result column="username" property="userName"/>
<result column="sex" property="sex"/>
</association>
<!-- 订单详情信息(一对多) -->
<!-- collection标签:由于一对多映射 -->
<!-- property属性:指关联信息映射到OrdeersExt的哪个属性上 -->
<!-- ofType属性: 集合中java类型-->
<collection property="orderdetailLists" ofType="com.qingzi.mybatisdemo.entity.OrderDetail">
<id column="detailId" property="id"/>
<result column="items_id" property="itemsId"/>
<result column="items_num" property="itemsNum"/>
</collection>
</resultMap>
<select id="findOrdersAndOrderdetail" resultMap="ordersAndOrderdetail">
SELECT
orders.id,
orders.user_id,
orders.number,
user.sex,
orderdetail.id detailId,
orderdetail.items_id,
orderdetail.items_num
FROM
orders,
USER,
orderdetail
WHERE orders.user_id = user.id
AND orders.id = orderdetail.orders_id
</select>
</mapper>
编写测试类
private SqlSessionFactory sqlSessionFactory ;
@Before
public void setUp() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindOrdersAndOrderdetail() {
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理类
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
List<OrdersExt> lists = ordersMapper.findOrdersAndOrderdetail();
sqlSession.close();
}
}
三,多对多关联查询
其实多对多就是特殊得一对一得关联关系
需求分析:
查询用户信息及用户购买的商品信息。
sql语句:
主表:user表
从表:orders,orderdetail,items
SELECT
odr.id,
odr.user_id,
odr.number,
us.username,
us.sex,
odrdl.id detailId,
odrdl.items_id,
odrdl.items_num,
itm.name,
itm.price
FROM
orders odr,
user us,
orderdetail odrdl,
items itm
WHERE odr.user_id = us.id
AND odr.id = odrdl.orders_id
AND odrdl.items_id = itm.id
修改orders表对应的Orders类:
public class Orders {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
private List<Orderdetail> orderdetailList;
修改orderdetail表对应的Orderdetail类;
public class Orderdetail {
private Integer id;
private Integer ordersId;
private Integer itemsId;
private Integer itemsNum;
private Items items;
修改items表对应的items类:
public class Items {
private Integer id;
private String name;
private float price;
private String text;
private String pic;
private Date createtime;
编写Mapper接口:
public interface UserMapper {
public List<User> findUserAndItems();
}
编写用户信息及其关联的商品信息是先查用户信息及其关联到的订单信息再到订单详情信息最后到商品信息的一个映射过程,所以再映射文件中要注意各层级的映射关系以及映射标签的嵌套。
<?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 namespace="com.qingzi.mybatisdemo.repository.UserMapper">
<!-- 多对多:查询用户购买商品的详细信息 -->
<resultMap id="userAndItems" type="com.qingzi.mybatisdemo.entity.User">
<!--用户信息-->
<id column="id" property="id" />
<result column="username" property="userName" />
<result column="sex" property="sex"/>
<!--订单信息(一对多)-->
<collection property="ordersLists" ofType="orders">
<id column="detail" property="id" />
<result column="items_id" property="itemsId"/>
<result column="items_num" property="itemsNum"/>
<!--订单详情信息(一对多)-->
<collection property="orderdetailList" ofType="orderdetail">
<id column="detailId" property="itemsId" />
<result column="items_id" property="itemsId" />
<result column="items_num" property="itemsNum"/>
<!--items信息:一对多-->
<association property="items" javaType="items">
<id column="items_id" property="id"/>
<result column="name" property="name"/>
<result column="price" property="price"/>
</association>
</collection>
</collection>
</resultMap>
<select id="findUserAndItems" resultMap="userAndItems">
SELECT
orders.`id`,
orders.`user_id`,
orders.`number`,
user.`username`,
user.`sex`,
orderdetail.`id` detailId,
orderdetail.`items_id`,
orderdetail.`items_num`,
items.`name`,
items.`price`
FROM
orders,
USER,
orderdetail,
items
WHERE orders.`user_id` = user.`id`
AND orders.`id` = orderdetail.`orders_id`
AND orderdetail.`items_id` = items.`id`
</select>
</mapper>
编写测试类:
public class UserMapperTest {
private SqlSessionFactory sqlSessionFactory ;
@Before
public void setUp() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindOrdersAndOrderdetail() {
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> lists = userMapper.findUserAndItems();
sqlSession.close();
}
}
四,对关联查询做出总结
关联查询,也就是多表查询,查询的结果集也不是一个表所对应的javaBean对象所能进行直接映射的,因此,我们再进行关联查询要进行合理的javaBean对象处理和拓展,保证查询出来的结果集都有所对应的javaBean属性与之对应,这样就能保证查询出来的结果正确无误。
在关联查询中我们常用的标签有:association和collection。association标签是一对一关联映射的所需要的标签。colleciton标签是一对多所需要的标签。在mybatis中,可以理解为多对一也是特殊的一对一(如同:多个员工对应一个部门;但是也可以理解为一个员工对应一个部门,只不过有多个员工而已;简单来说:在一个Employee对象中有一个department属性,同时又有多个Employee对象,每个对象中的department对应同一个部门);多对多是特殊的一对多。
在结果集映射中,我们用的结果集映射总共有两种,分别是:resultType和resultMap;那在关联映射关系的时候,我们该如何选择使用哪种结果映射方式呢,其实值需要理解两种映射的不同和映射原理。
1.resutType映射时把查询出来的结果集和对应的javaBean属性进行一一对应。因此,在采用resultType映射,需要映射结果集的javaBean中的所有属性都是与查询结果集进行相互对应的(属性不能嵌套)
2.而使用resultMap结果集映射,则需要先声明resultMap,后使用。先声明resultMao就是制定查询出来的结果集中的列数和javaBean对象中的哪些属性进行关联映射(属性可以嵌套)。