本文以用户User
、订单Order
、角色Role
来进行描述mybatis
中的关联查询
用户和订单之间是一对多的关系,一个用户可以有多个订单。订单和用户之间是多对一的关系,多个订单可以属于同一个用户。用户和角色之间是多对多的关系,一个用户可以有多个角色,多个角色也可以只属于一个用户。
建表语句
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;
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;
注意,用户表和订单表之间使用外键连接的,外键要建在多的一方,因为,如果建在一的一方,那么主键要一个字段要存多个值了,不利于维护和操作,所以,mysql中,外键都是要建在多的一方。
create table `role`(
`id` int (11) not null AUTO_INCREMENT,
`name` varchar(32) not null,
primary key (`id))
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`)
)
实体类
public class User {
private Integer id;
private String username;
private String sex;
private Date birthday;
private String address;
//....(getter、setter)
}
public class Order {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
//getter、setter方法
}
public class Role {
private Integer id;
private String name;
//getter、setter方法
}
一个订单只属于一个用户,从订单的角度看是一对一关系。
一对一映射,有两种方式:
新建OrderUser
的pojo
,继承自Order
(这里继承User
、Order
类都一样,只要这个Pojo
包含User
、Order
的所有属性即可)。
//一对一关联查询的时候,使用的新的实体类,作为返回值类型
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);
}
}
resultMap
用于结果映射,使用这种方式,就是手动的将查询结果进行映射
改造Order
的pojo
,在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>
一个用户,可以有多个订单,在用户的角度上,用户和订单是一对多的关系
改造User
的pojo,一个User
可以有多个Order
,可以在User
中建一个只能装Order
的List
//配置一对多关系映射
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);
}
}
}
一个用户可以有多个角色,一个角色也可以有多个用户,多对多用户,可以拆分为两个一对多,只看用户的话,一个用户可以有多个角色,只看角色的话,一个角色可以有多个用户。或者,你不用在意他是多对多,只需要根据查询的的结果配置相应的结果映射即可。
比如,根据id查询用户的信息以及角色信息
首先,在 User
中添加一个List
集合属性
//配置多对多关系映射
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);
}
结果
如果是查询角色对应的用户和上述过程一样,这里就不叙述了。
另外,保存用户的时候,要手动维护第三张表(可以使用foreach标签进行批量插入),否则用户表和角色表无法关联。
小结
注意说明:
1、保证SQL的可读性,尽量通俗易懂
2、根据实际要求,尽量编写性能更高的SQL语句
3、注意属性名和字段不一致的问题
4、注意一对多和多对一 中:字段和属性对应的问题
如有不足之处,欢迎指正,谢谢!