9.1 项目环境
9.1.1 项目目录
9.2 关联关系分类
三种:1对1,1对多,多对多
9.2.1 一对一关联关系
人和身份证的关系。一个人只能有一张身份证,一张身份证只能属于一个人。双向【一对一】关联关系。
9.2.2 一对多关联关系
举一个例子:用户和订单的关系。
一个用户可以有多个订单,从用户到订单是【一对多】关联关系。
一个订单只能属于一个人,从订单到人是【一对一】的关联关系。
9.2.3 多对多关联关系
举一个例子:业务系统中,用户和角色的关系。
一个用户可以有多种角色:小明有可能是:经理,员工,总经理
一种角色可以有多个用户:经理这个角色,有多个人:kobe , Curry
在实际项目中,多对多关系通过中间表,看成两个一对多关联关系。
9.3 SQL数据表结构
-- drop table if exists user3;
-- 创建用户基本表
create table user(
id int primary key auto_increment, -- 主键自增长
username varchar(20) not null,
birthday date,
sex char(1) default '男',
address varchar(50)
);
insert into user3 values (null, '侯大利','1990-10-24','男','江州');
insert into user3 values (null, '王大青','1992-11-12','女','秦阳');
insert into user3 values (null, '朱琳','1983-05-20','男','江州');
insert into user3 values (null, '田大甜','1993-03-22','女','阳州');
-- 查询数据
select * from user;
-- 用户信息表
create table user_info(
id int primary key, -- 既是主键,又是外键
height double, -- 身高厘米
weight double, -- 体重公斤
married tinyint, -- 是否结婚
foreign key(id) references user3(id)
);
-- 插入用户数据
insert into user_info values(1,185,90,1),(2,170,60,0);
select * from user_info;
9.4 一对一关联
查询1号用户和他的扩展信息
select * from user u inner join user_info i on u.id = i.id where u.id=1
9.4.1 用户实体类
- UserInfo类(创建一对一的关系)
package cn.guardwhy.domain;
/**
* 用户拓展信息类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo {
private Integer id; // 主键
private Double height; // 身高
private Double weight; // 体重
private Boolean married; // 是否结婚
private User user; // 用户的基本信息
}
- User类(创建一对一的关系)
package cn.guardwhy.domain;
import java.sql.Date;
/**
* 用户类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private UserInfo userInfo; // 对应的用户扩展信息
}
9.4.2 sqlMapConfig.xml
<?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 resource="db.properties">
<property name="jdbc.username" value="root"/>
<property name="jdbc.password" value="root"/>
</properties>
<!--定义实体类别名-->
<typeAliases>
<package name="cn.guardwhy.domain"/>
</typeAliases>
<!-- 一个核心配置文件,可以配置多个运行环境,default默认使用哪个运行环境 -->
<environments default="default">
<!-- 其中的一个运行环境,通过id来进行标识-->
<environment id="default">
<!--事务管理器 -->
<transactionManager type="JDBC"/>
<!--数据源 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--映射器-->
<mappers>
<package name="cn.guardwhy.dao"/>
</mappers>
</configuration>
9.4.3 用户接口
package cn.guardwhy.dao;
import cn.guardwhy.domain.User;
public interface UserMapper {
/**
* 通过uid查找用户和拓展信息
*/
User findUserAndInfo(int uid);
}
9.4.4 用户Mapper.xml
association标签:实现一对一关系
association标签的属性 | 说明 |
---|---|
property | 一对一对应的另一方实体类的属性名,如:userInfo |
resultMap | 指定另一方映射配置的id,如:用户扩展信息的映射 |
-
定义User的映射**userMap**,包含主键和所有的属性,无论属性名与列名是否相同。
-
定义用户信息的映射**userInfoMap**,包含主键和所有的属性,无论属性名与列名是否相同。
-
定义映射userAndInfoMap,extends继承于userMap,同时必须指定type属性。
-
使用association定义一对一关联映射,指定:property、resultMap属性,将resultMap指定为userInfoMap。
-
使用表连接查询:通过用户id查询用户和对应的用户扩展信息,查询结果映射为userAndInfoMap。
<?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="cn.guardwhy.dao.UserMapper">
<!--1.创建User的映射-->
<resultMap id="userMap" type="user">
<!--映射主键-->
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
</resultMap>
<!--2.创建UserInfo的映射-->
<resultMap id="userInfoMap" type="userInfo">
<!--映射主键-->
<id property="id" column="id"/>
<result property="height" column="height"/>
<result property="weight" column="weight"/>
<result property="married" column="married"/>
</resultMap>
<!--3.创建一对一的关联extends,将上面的映射关系复制过来-->
<resultMap id="userAndInfoMap" type="user" extends="userMap">
<!--一对一关联-->
<association property="userInfo" resultMap="userInfoMap"/>
</resultMap>
<!--4.使用上面的一对一的映射-->
<select id="findUserAndInfo" resultMap="userAndInfoMap" parameterType="int">
select * from user3 u inner join user_info i on u.id = i.id where u.id = #{id}
</select>
</mapper>
9.4.5 测试代码
查询输出用户和扩展信息
package cn.guardwhy.test;
import cn.guardwhy.dao.UserMapper;
import cn.guardwhy.domain.User;
import cn.guardwhy.utils.SessionFactoryUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class TestUserMapper {
private static SqlSessionFactory factory;
private SqlSession session;
private UserMapper userMapper;
// 1.创建会话对象,自动提交事务
@Before
public void begin(){
session = SessionFactoryUtils.getSession();
userMapper = session.getMapper(UserMapper.class);
}
// 2.测试代码
@Test
public void testFindUserAndInfo(){
User user = userMapper.findUserAndInfo(1);
System.out.println("用户信息:" + user);
System.out.println("扩展信息:" + user.getUserInfo());
}
// 3.关闭会话
@After
public void end(){
// 手动提交
session.commit();
session.close();
}
}
9.4.6 执行结果
9.5 一对多关联
9.5.1 订单模型
9.5.2 SQL数据表结构
-- 创建订单表
create table order_form(
oid int primary key auto_increment , -- 主键
user_id int not null, -- 用户id,外键
number varchar(20), -- 订单编号
create_time datetime, -- 下单时间
note varchar(100), -- 备注
foreign key(user_id) references user(id) -- 外键约束,关联主表的主键
);
-- 添加订单数据
insert into order_form values(null, 1,'10001001', now(), '请提前安装'),(null, 1,'10001002', now(), '包邮'),(null, 1,'10001003', now(), '选择红色');
insert into order_form values(null, 2,'10001004', now(), '购买多件'),(null, 2,'10001005', now(), '安全带');
-- 查询订单表
select * from order_form;
9.5.3 数据表关系
9.5.4 用户实体类
- OrderForm(创建一对多的关系)
package cn.guardwhy.domain;
import java.sql.Timestamp;
/**
* 订单实体类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderForm {
private Integer oid; // 主键
private Integer userId; // 外键
private String number; // 订单号
private Timestamp createTime; // 下单时间
private String note; // 备注信息
private User user; // 用户信息
}
- User(创建一对多的关系)
package cn.guardwhy.domain;
import java.sql.Date;
import java.util.List;
/**
* 用户类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private UserInfo userInfo; // 对应的用户扩展信息
private List<OrderForm> orders; // 对应所有的订单信息
}
9.5.5 用户接口
/**
* 查询全部用户数据并且查询用户的所有订单数据
*/
User findUserAndOrders(int uid);
9.5.6 用户Mapper.xml
collection标签:用来配置1对多的关联映射
collection的属性 | 说明 |
---|---|
property | 多方属性的名字,如:orders |
javaType | 多方的属性类型,可以省略。如:list |
ofType | 集合中每个元素的类型,如:orderForm |
resultMap | 多方的映射,如:订单映射 orderMap |
步骤
- 定义订单的映射orderMap
- 配置用户到订单的一对多关联关系userOrdersMap,继承于userMap
- collection:配置一对多关联关系,指定property,ofType,resultMap为orderMap
- 查询某个用户,并且查询关联的多个订单信息,结果为userOrdersMap
<?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="cn.guardwhy.dao.UserMapper">
<!--创建User的映射-->
<resultMap id="userMap" type="user">
<!--映射主键-->
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
</resultMap>
<!--1.2创建UserInfo的映射-->
<resultMap id="userInfoMap" type="userInfo">
<!--映射主键-->
<id property="id" column="id"/>
<result property="height" column="height"/>
<result property="weight" column="weight"/>
<result property="married" column="married"/>
</resultMap>
<!--1.3 创建一对一的关联extends,将上面的映射关系复制过来-->
<resultMap id="userAndInfoMap" type="user" extends="userMap">
<!--一对一关联-->
<association property="userInfo" resultMap="userInfoMap"/>
</resultMap>
<!--1.4 使用上面的一对一的映射-->
<select id="findUserAndInfo" resultMap="userAndInfoMap" parameterType="int">
select * from user u inner join user_info i on u.id = i.id where u.id = #{id}
</select>
<!--创建订单的映射-->
<resultMap id="orderMap" type="orderForm">
<id property="oid" column="oid"/>
<result property="userId" column="user_id"/>
<result property="number" column="number"/>
<result property="createTime" column="create_time"/>
<result property="note" column="note"/>
</resultMap>
<!--配置一对多的映射-->
<resultMap id="userOrdersMap" type="user" extends="userMap">
<!--
property: 多方的属性名
javaType:多方的属性类型
ofType: 集合中每个元素的类型
resultMap:多方映射
-->
<collection property="orders" javaType="list" ofType="orderForm" resultMap="orderMap"/>
</resultMap>
<!--一对多的关联查询-->
<select id="findUserAndOrders" resultMap="userOrdersMap" parameterType="int">
select * from user u inner join order_form o on u.id = o.user_id where u.id=#{id}
</select>
</mapper>
9.5.7 测试代码
// 3.查询1号用户的所有订单
@Test
public void testFindUserAndOrders(){
User user = userMapper.findUserAndOrders(1);
System.out.println("用户信息:" + user);
List<OrderForm> orders = user.getOrders();
System.out.println("用户订单信息如下:" );
for (OrderForm order : orders){
System.out.println(order);
}
}
9.5.8 执行结果
9.6 多对多关联
多对多关联关系,可以通过中间表看成两个双向的一对多关联关系。
9.6.1 关联模型
9.6.2 SQL数据表结构
-- 创建角色表
create table role(
role_id int primary key auto_increment comment '角色id(主键)',
role_name varchar(32) not null comment '角色名称',
role_detail varchar(100) default null comment '角色描述'
);
-- 插入角色记录
insert into role(role_name, role_detail) values('局长', '公安局的管理者');
insert into role(role_name, role_detail) values('普通刑警', '刑侦破案');
insert into role(role_name, role_detail) values('法医', '协助刑警破案');
insert into role(role_name, role_detail) values('支队长', '刑警管理者');
select * from role;
-- 创建用户的角色中间表
create table user_role(
user_id int not null comment '用户id',
role_id int not null comment '角色id',
primary key(user_id, role_id), -- 复合主键
foreign key(user_id) references user(id),
foreign key(role_id) references role(role_id)
);
insert into user_role(user_id, role_id) values(1,1);
insert into user_role(user_id, role_id) values(2,2);
insert into user_role(user_id, role_id) values(6,2);
insert into user_role(user_id, role_id) values(1,3);
insert into user_role(user_id, role_id) values(2,1);
insert into user_role(user_id, role_id) values(2,4);
-- 查询用户角色中间表
select * from user_role;
-- 查询1号用户有哪些角色
select *from user u inner join user_role ur on u.id = ur.user_id inner join
role r on r.role_id = ur.role_id where u.id = 1;
9.6.3 角色实体类
Role(角色实体类)
package cn.guardwhy.domain;
import java.util.List;
/**
角色实体类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Role {
private Integer roleId;
private String roleName;
private String roleDetail;
private List<User> users; //对应多个用户
}
User(创建多对多的关系)
package cn.guardwhy.domain;
import java.sql.Date;
import java.util.List;
/**
* 用户类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private UserInfo userInfo; // 对应的用户扩展信息
private List<OrderForm> orders; // 对应所有的订单信息
private List<Role> roles; // 对应多个角色
}
9.6.4 用户接口类
/**
* 通过uid查找用户和他的所有角色
*/
User findRolesByUserId(int uid);
9.6.5 用户Mapper.xml
- 定义User的映射配置userMap(已经配置)
- 定义角色的映射roleMap,配置主键和所有的属性
- 定义一个用户对象对应多个角色userRolesMap,继承于userMap
- 使用collection关联映射,指定property,javaType,ofType,resultMap为roleMap
- 定义查询findUserAndInfo,映射结果是:userRolesMap
<?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="cn.guardwhy.dao.UserMapper">
<!--创建User的映射-->
<resultMap id="userMap" type="user">
<!--映射主键-->
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
</resultMap>
<!--3.1 配置角色的映射-->
<resultMap id="roleMap" type="role">
<id property="roleId" column="role_id"/>
<id property="roleName" column="role_name"/>
<id property="roleDetail" column="role_Detail"/>
</resultMap>
<!--3.2 配置多对多的映射-->
<resultMap id="userRolesMap" type="user" extends="userMap">
<!--
property: 多方的属性名
javaType:多方的属性类型
ofType: 集合中每个元素的类型
resultMap:多方映射
-->
<collection property="roles" javaType="list" ofType="role" resultMap="roleMap"/>
</resultMap>
<!--3.3 查询某个用户对应的角色-->
<select id="findRolesByUserId" resultMap="userRolesMap" parameterType="int">
select *from user u inner join user_role ur on u.id = ur.user_id
inner join role r on r.role_id = ur.role_id where u.id = #{id};
</select>
</mapper>
9.6.6 测试代码
// 多对多关联查询
@Test
public void testFindRolesByUserId(){
User user = userMapper.findRolesByUserId(1);
System.out.println("用户信息:" + user);
List<Role> roles = user.getRoles();
System.out.println("用户角色如下: ");
for(Role role : roles){
System.out.println(role);
}
}
9.6.7 执行结果