MyBatis(二)——关联查询

文章目录

    • 1. 搭建环境
    • 2. 一对一
      • 2.1 使用ResultType
      • 2.2 使用ResultMap
    • 3. 一对多
    • 4. 多对多

1. 搭建环境

本文以用户User、订单Order、角色Role来进行描述mybatis中的关联查询

用户和订单之间是一对多的关系,一个用户可以有多个订单。订单和用户之间是多对一的关系,多个订单可以属于同一个用户。用户和角色之间是多对多的关系,一个用户可以有多个角色,多个角色也可以只属于一个用户。

Order——User
MyBatis(二)——关联查询_第1张图片
User——Role
MyBatis(二)——关联查询_第2张图片

建表语句

  • 创建User表
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `birthday` date DEFAULT NULL COMMENT '生日',
  `sex` char(1) DEFAULT NULL COMMENT '性别',
  `address` varchar(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8;

MyBatis(二)——关联查询_第3张图片

  • 创建Order表
CREATE TABLE `order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL COMMENT '下单用户id',
  `number` varchar(32) NOT NULL COMMENT '订单号',
  `createtime` datetime NOT NULL COMMENT '创建订单时间',
  `note` varchar(100) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`),
  KEY `FK_orders_1` (`user_id`),
  CONSTRAINT `FK_order_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

MyBatis(二)——关联查询_第4张图片

注意,用户表和订单表之间使用外键连接的,外键要建在多的一方,因为,如果建在一的一方,那么主键要一个字段要存多个值了,不利于维护和操作,所以,mysql中,外键都是要建在多的一方。

  • 创建Role表
create table `role`(
`id` int (11) not null AUTO_INCREMENT,
`name` varchar(32) not null,
primary key (`id))

MyBatis(二)——关联查询_第5张图片
(数据可能不太恰当,只要能说明问题就行【捂脸】)

User表和Role表是多对多关系,相当于是两个多对一,所以,建立一张辅助表user_role

create table `role_user`(
`id` int(10) not null auto_increment,
`u_id` int(10) not null,
`r_id` int(10) not null,
PRIMARY key(`id`),
CONSTRAINT `user_id` FOREIGN KEY(`u_id`) REFERENCES `user`(`id`),
CONSTRAINT `role_id` FOREIGN key(`r_id`) REFERENCES `role`(`id`)
)

MyBatis(二)——关联查询_第6张图片

实体类

  • User
public class User {
	private Integer id;
	private String username;
	private String sex;
	private Date birthday;
	private String address;
	//....(getter、setter)
}
  • Order
public class Order {
	private Integer id;

	private Integer userId;

	private String number;

	private Date createtime;

	private String note;
	//getter、setter方法
}
  • Role
public class Role {

	private Integer id;
	private String name;
	//getter、setter方法
}

2. 一对一

一个订单只属于一个用户,从订单的角度看是一对一关系。

一对一映射,有两种方式:

2.1 使用ResultType

新建OrderUserpojo,继承自Order(这里继承UserOrder类都一样,只要这个Pojo包含UserOrder的所有属性即可)。

//一对一关联查询的时候,使用的新的实体类,作为返回值类型
public class OrderUser extends Order {
	private Integer id;
	private String username;
	private String sex;
	private Date birthday;
	private String address;
	//getter、setter方法
}	

修改order的映射文件,新增查询方法getOrderUser

<!-- 1.使用resultType,建一个组合的pojo -->
<select id="queryOrderUser" resultType="orderuser">
	select 
		o.id,
		o.user_id userId,
		o.number,
		o.createtime,
		o.note,
		u.username,
		u.address
	from `order` o left join `user` u on o.user_id = u.id; 
</select>

测试

@Test //测试使用resultType的一对一关联查询
//查询订单的用户信息
public void testQueryOrderUser() {
	SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
	SqlSession sqlSession = sqlSessionFactory.openSession();
	OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
	List<OrderUser> list = orderMapper.queryOrderUser();
	for (OrderUser orderUser : list) {
		System.out.println(orderUser);
	}
}

结果
MyBatis(二)——关联查询_第7张图片

2.2 使用ResultMap

resultMap用于结果映射,使用这种方式,就是手动的将查询结果进行映射

改造Orderpojo,在Order类中添加一个User属性

//添加User属性
private User user;
//并生成响应的getter、setter方法

修改Order的映射文件

<!-- id:<resultMap>标签的唯一标识符
	 type:映射的pojo对象 -->
<resultMap type="order" id="orderUserMap">
		
	 <id property="id" column="id"/>
	 <!--result标签用于数据库中的字段和javaBean的绑定
	 		property:javaBean中的属性名
	 		column:数据库中字段名
	 -->
	 <result property="userId" column="user_id"/>
	 <result property="number" column="number"/>
	 <result property="createtime" column="createtime"/>
	 <result property="note" column="note"/>
	
	<!-- association:配置一对一关联映射
		 property:pojo中的一个对象属性,order里面的User属性名
		 javaType:pojo关联的pojo对象类型
	 -->
	
	<association property="user" javaType="user">
		<!-- id标签用于绑定主键
			 column:"表的主键字段,或者可以为查询语句中的别名字段"
			 property:"映射pojo对象的主键属性" 
		-->
		<id property="id" column="user_id"/>
		<result property="username" column="username"/>
		<result property="address" column="address"/>
	</association>
</resultMap>

MyBatis(二)——关联查询_第8张图片

3. 一对多

一个用户,可以有多个订单,在用户的角度上,用户和订单是一对多的关系

改造User的pojo,一个User可以有多个Order,可以在User中建一个只能装OrderList

//配置一对多关系映射
private List<Order> orders;
//生成响应的getter、setter方法

修改User的映射文件

<resultMap type="user" id="user_order_map">
	<id column="id" property="id"/>
	<result column="username" property="username"/>
	<result column="birthday" property="birthday"/>
	<result column="sex" property="sex"/>
	<result column="address" property="address"/>
	
	<!-- 一对多关联映射配置,注意是oftype,
		 property:pojo的集合属性名
		 ofType:集合中的pojo对象类型
	-->
	
	<collection property="orders" ofType="order">
		
		<!-- id标签用于绑定主键
	  column:"表的主键字段,或者可以为查询语句中的别名字段"
	  property:"映射pojo对象的主键属性" 
		-->
		
		<id property="id" column="oid"/>
		<result property="userId" column="user_id"/>
		<result property="number" column="number"/>
		<result property="createtime" column="createtime"/>
		<result property="note" column="note"/>
	</collection>
</resultMap>

测试

@Test //测试一对多关联查询
public void testSelectUserOrderResultMap(){
	SqlSessionFactory sessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
	SqlSession sqlSession = sessionFactory.openSession(true);
	UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
	List<User> list = userMapper.selectUserOrderResultMap();
	for (User user : list) {
		System.out.println(user);
		for (Order order : user.getOrders()) {
			System.out.println("     订单有:" + order);
		}
		
	}
}

结果
在这里插入图片描述

4. 多对多

一个用户可以有多个角色,一个角色也可以有多个用户,多对多用户,可以拆分为两个一对多,只看用户的话,一个用户可以有多个角色,只看角色的话,一个角色可以有多个用户。或者,你不用在意他是多对多,只需要根据查询的的结果配置相应的结果映射即可。

比如,根据id查询用户的信息以及角色信息

首先,在 User 中添加一个List roles集合属性

//配置多对多关系映射
private List<Role> roles;
//生成响应的getter、setter方法

修改User的映射文件

<!--多对多关联查询 -->
<resultMap id="UserRole" type="user">
	<id column="id" property="id"/>
	<result column="username" property="username"/>
	<result column="birthday" property="birthday"/>
	<result column="sex" property="sex"/>
	<result column="address" property="address"/>
	
	<collection property="roles" ofType="role">
		<id property="r_id" column="r_id"></id>
		<result property="name" column="name"></result>
	</collection>
</resultMap>

<!--根据的id查询一个用户的信息以及角色信息-->
<select id="selectUserRoleById" resultMap="UserRole" resultType="user">
	select u.id,u.username,u.birthday,u.sex,u.address,r.name from user u left join role_user
	ru on u.id=ru.u_id left join role r on r.r_id=ru.r_id where u.id=#{id}
</select>

测试

@Test //测试多对多
public void testSelectUserRoleById() {
	SqlSessionFactory sessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
	SqlSession sqlSession = sessionFactory.openSession(true);
	UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
	User user = userMapper.selectUserRoleById(2);
	System.out.println(user);
}

结果
MyBatis(二)——关联查询_第9张图片
如果是查询角色对应的用户和上述过程一样,这里就不叙述了。

另外,保存用户的时候,要手动维护第三张表(可以使用foreach标签进行批量插入),否则用户表和角色表无法关联。

小结

  • 关联-association
  • 集合-collection
  • 所以association是用于一对一和多对一,而collection是用于一对多的关系
  • JavaType和ofType都是用来指定对象类型的
  • JavaType是用来指定pojo中属性的类型
  • ofType指定的是映射到list集合属性中pojo的类型。

注意说明:

1、保证SQL的可读性,尽量通俗易懂

2、根据实际要求,尽量编写性能更高的SQL语句

3、注意属性名和字段不一致的问题

4、注意一对多和多对一 中:字段和属性对应的问题

如有不足之处,欢迎指正,谢谢!

你可能感兴趣的:(mybatis)