关联关系是面向对象分析、面向对象设计最重要的知识。合理的关联映射将大大简化持久层数据的访问。关联关系大致分为以下三类:

  • 一对一
  • 一对多
  • 多对多

1. 一对一

一对一关系推荐使用唯一主外键关联,即两张表使用外键关联关系,由于是一对一关联,因此还需要给外键列增加unique唯一约束。

-- 身份证表
create table tb_card {
id int primary key auto_increment,
code varchar(18)
};
insert into tb_card (code) values('');
-- 个人表
create table tb_person {
id int primary key auto_increment,
name varchar(18),
sex varchar(18),
age int,
card_id int unique, --card_id 唯一约束,且作为外键
foreign key (card_id) references tb_card (id)
};
insert into tb_person (name,sex,age,card_id) values('jack','男',23,1);

代码中创建Card对象和Person的对象(此处省略get/set方法)

public class Card implements Serializable {

    private Integer id;  // 主键id
private String code; // 身份证编号
···
}
public class Person implements Serializable {

    private Integer id;  // 主键id
private String name; // 姓名
private String sex; // 性别
private Integer age; // 年龄 // 人和身份证是一对一的关系,即一个人只有一个身份证,在Person类中定义card属性,来映射一对一关系
private Card card;
···
}

映射xml文件如下

CardMapper.xml

<!-- namespace指用户自定义的命名空间。 -->
<mapper namespace="org.zang.mapper.CardMapper">
<!-- 根据id查询Card,返回Card对象 -->
<select id="selectCardById" parameterType="int" resultType="org.zang.domain.Card">
SELECT * from tb_card where id = #{id}
</select>
</mapper>

PersonMapper.xml

<mapper namespace="org.zang.mapper.PersonMapper">
<!-- 映射Peson对象的resultMap -->
<resultMap type="org.zang.domain.Person" id="personMapper">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sex" column="sex"/>
<result property="age" column="age"/> <!-- 一对一关联映射:association -->
<association property="card" column="card_id"
select="org.zang.mapper.CardMapper.selectCardById"
javaType="org.zang.domain.Card"/>
</resultMap> <!-- 根据id查询Person,返回resultMap -->
<select id="selectPersonById" parameterType="int"
resultMap="personMapper">
SELECT * from tb_person where id = #{id}
</select>
</mapper>

可以看到,personMapper中使用<association.../>元素来映射一对一的关联关系。

2. 一对多

数据库中一对多关系通常使用主外键关联,外键列应该在多方,即多方维护关系。

-- 班级表 (一)
create table tb_clazz(
id int primary key auto_increment,
code varchar(18),
name varchar(18)
);
insert into tb_clazz(code,name) values('j1601','java就业班'); -- 学生表 (多)
create table tb_student(
id int primary key auto_increment,
name varchar(18),
sex varchar(18),
age int,
clazz_id int, --clazz_id 作为外键参照tb_clazz的主键id
foreign key (clazz_id) references tb_clazz(id)
);
insert into tb_student(name,sex,age,clazz_id) values('jack','男',23,1);
insert into tb_student(name,sex,age,clazz_id) values('rose','女',18,1);
insert into tb_student(name,sex,age,clazz_id) values('tom','男',21,1);
insert into tb_student(name,sex,age,clazz_id) values('alice','女',20,1);

代码中创建Clazz对象和Student的对象(此处省略get/set方法)

public class Clazz implements Serializable {

    private Integer id; // 班级id,主键
private String code; // 班级编号
private String name; // 班级名称 // 班级和学生是一对多的关系,即一个班级可以有多个学生
private List<Student> students;
   ···
}
public class Student implements Serializable {

    private Integer id; // 学生id,主键
private String name; // 姓名
private String sex; // 性别
private Integer age; // 年龄 // 学生和班级是多对一的关系,即一个学生只属于一个班级
private Clazz clazz;
   ···
}

映射文件如下

ClazzMapper.xml

<mapper namespace="org.zang.mapper.ClazzMapper">

    <!-- 映射Clazz对象的resultMap -->
<resultMap type="org.zang.domain.Clazz" id="clazzResultMap">
<id property="id" column="id"/>
<result property="code" column="code"/>
<result property="name" column="name"/> <!-- 一对多关联映射:collection fetchType="lazy"表示懒加载 -->
<collection property="students" javaType="ArrayList" column="id" ofType="org.zang.domain.Student"
select="org.zang.mapper.StudentMapper.selectStudentByClazzId" fetchType="lazy">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sex" column="sex"/>
<result property="age" column="age"/>
</collection>
</resultMap> <!-- 根据id查询班级信息,返回resultMap -->
<select id="selectClazzById" parameterType="int" resultMap="clazzResultMap">
SELECT * FROM tb_clazz WHERE id = #{id}
</select> </mapper>

由于student属性是一个List集合,所有clazzResultMap中使用了<collection.../>元素映射一对多的关联关系。

此外还通过 fetchType="lazy" 来设置懒加载。fatch机制更多的是为了性能考虑,如果查询班级时确认会访问班级的所有学生,则该属性应该设置为eager;否则应该设置为lazy;正常情况下,一对多所关联的集合对象,都应该被设置成lazy。

使用懒加载还应该在mybatis-config.xml中增加如下配置

<settings>
<!-- 要使延迟加载生效必须配置下面两个属性 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

StudentMapper.xml

<mapper namespace="org.zang.mapper.StudentMapper">

<!-- 映射Student对象的resultMap -->
<resultMap type="org.zang.domain.Student" id="studentResultMap">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sex" column="sex"/>
<result property="age" column="age"/>
<!-- 多对一关联映射:association -->
<association property="clazz" javaType="org.zang.domain.Clazz">
<id property="id" column="id"/>
<result property="code" column="code"/>
<result property="name" column="name"/>
</association>
</resultMap> <!-- 根据id查询学生信息,多表连接,返回resultMap -->
<select id="selectStudentById" parameterType="int" resultMap="studentResultMap">
SELECT * FROM tb_clazz c,tb_student s
WHERE c.id = s.clazz_id
AND s.id = #{id}
</select> <!-- 根据班级id查询学生信息,返回resultMap -->
<select id="selectStudentByClazzId" parameterType="int"
resultMap="studentResultMap">
SELECT * FROM tb_student WHERE clazz_id = #{id}
</select> </mapper>

studentResultMap中使用<association.../>元素映射多对一的关联关系。因为<select id="selectStudentById".../>的sql语句是一条多表连接,关联tb_clazz表的同时查询了班级数据,所以<association.../>只是简单的装载数据。

tips:在实际开发中,由于一对多关系通常映射为集合对象,而由于多方的数据量可能很大,所以通常使用懒加载;而多对一只是关联到一个对象,所以通常使用多表连接直接提取出数据。

3. 多对多

在一个购物系统中,一个用户可以有多个订单,这是一对多的关系;一个订单中可以有多种商品,一种商品也可以属于多个不同的订单,订单和商品就是多对多的关系。

对于数据库中多对多的关系建议使用一个中间表来维护关系,中间表中的订单id作为外键参照订单表的id,商品id作为外键参照商品表的id。

-- 用户表
create table tb_user(
id int primary key auto_increment,
username varchar(18),
loginname varchar(18),
password varchar(18),
phone varchar(18),
address varchar(18)
);
insert into tb_user(username,loginname,password,phone,address)
values('杰克','jack','','','广州'); -- 商品表
create table tb_article(
id int primary key auto_increment,
name varchar(18),
price double,
remark varchar(18)
); insert into tb_article(name,price,remark)
values('疯狂java讲义',108.9,'李刚老师经典著作');
insert into tb_article(name,price,remark)
values('疯狂android讲义',99.9,'李刚老师经典著作');
insert into tb_article(name,price,remark)
values('疯狂ios讲义',89.9,'李刚老师经典著作');
insert into tb_article(name,price,remark)
values('springmvc+mybatis企业开发',69.9,'肖文吉老师经典著作'); -- 订单表
create table tb_order(
id int primary key auto_increment,
code varchar(32),
total double,
user_id int,
foreign key (user_id) references tb_user(id)
); insert into tb_order(code,total,user_id)
values('6aa3fa359ff14619b77fab5990940a2d',388.6,1); insert into tb_order(code,total,user_id)
values('6aa3fa359ff14619b77fab5990940b3c',217.8,1); --创建订单表
create table tb_item(
order_id int,
article_id int,
amount int,
primary key(order_id,article_id),
foreign key (order_id) references tb_order(id),
foreign key (article_id) references tb_article(id)
); insert into tb_item(order_id,article_id,amount)
values(1,1,1);
insert into tb_item(order_id,article_id,amount)
values(1,2,1);
insert into tb_item(order_id,article_id,amount)
values(1,3,2); insert into tb_item(order_id,article_id,amount)
values(2,4,2);
insert into tb_item(order_id,article_id,amount)
values(2,1,1);

tb_order表的user_id作为外键参照tb_user表的主键id。tb_item表作为中间表,用来维护tb_article和tb_order的多对多关系,tb_item表的order_id作为外键参照tb_order表的主键id,article_id作为外键参照tb_article表的主键id。

代码中创建User对象、Order对象和Article的对象(此处省略get/set方法)

public class User implements Serializable{

    private Integer id;  // 用户id,主键
private String username; // 用户名
private String loginname; // 登录名
private String password; // 密码
private String phone; // 联系电话
private String address; // 收货地址 // 用户和订单是一对多的关系,即一个用户可以有多个订单
private List<Order> orders;
   ···
}
public class Order implements Serializable {

    private Integer id;  // 订单id,主键
private String code; // 订单编号
private Double total; // 订单总金额 // 订单和用户是多对一的关系,即一个订单只属于一个用户
private User user; // 订单和商品是多对多的关系,即一个订单可以包含多种商品
private List<Article> articles;
  ···
}
public class Article implements Serializable {

    private Integer id;        // 商品id,主键
private String name; // 商品名称
private Double price; // 商品价格
private String remark; // 商品描述 // 商品和订单是多对多的关系,即一种商品可以包含在多个订单中
private List<Order> orders;
   ···
}

映射文件如下

UserMapper.xml

<mapper namespace="org.zang.mapper.UserMapper">

    <resultMap type="org.zang.domain.User" id="userResultMap">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="loginname" column="loginname"/>
<result property="password" column="password"/>
<result property="phone" column="phone"/>
<result property="address" column="address"/>
<!-- 一对多关联映射:collection -->
<collection property="orders" javaType="ArrayList" column="id" ofType="org.zang.domain.User"
select="org.zang.mapper.OrderMapper.selectOrderByUserId" fetchType="lazy">
<id property="id" column="id"/>
<result property="code" column="code"/>
<result property="total" column="total"/>
</collection>
</resultMap> <select id="selectUserById" parameterType="int" resultMap="userResultMap">
SELECT * FROM tb_user WHERE id = #{id}
</select> </mapper>

OrderMapper.xml

<mapper namespace="org.zang.mapper.OrderMapper">

    <resultMap type="org.zang.domain.Order" id="orderResultMap">
<id property="id" column="oid"/>
<result property="code" column="code"/>
<result property="total" column="total"/>
<!-- 多对一关联映射:association -->
<association property="user" javaType="org.zang.domain.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="loginname" column="loginname"/>
<result property="password" column="password"/>
<result property="phone" column="phone"/>
<result property="address" column="address"/>
</association>
<!-- 多对多映射的关键:collection -->
<collection property="articles" javaType="ArrayList" column="oid" ofType="org.zang.domain.Article"
select="org.zang.mapper.ArticleMapper.selectArticleByOrderId" fetchType="lazy">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="price" column="price"/>
<result property="remark" column="remark"/>
</collection>
</resultMap> <!-- 注意,如果查询出来的列同名,例如tb_user表的id和tb_order表的id都是id,同名,需要使用别名区分 -->
<select id="selectOrderById" parameterType="int" resultMap="orderResultMap">
SELECT u.*,o.id AS oid,CODE,total,user_id
FROM tb_user u,tb_order o
WHERE u.id = o.user_id
AND o.id = #{id}
</select> <!-- 根据userid查询订单 -->
<select id="selectOrderByUserId" parameterType="int" resultType="org.zang.domain.Order">
SELECT * FROM tb_order WHERE user_id = #{id}
</select> </mapper>

ArticleMapper.xml

<mapper namespace="org.zang.mapper.ArticleMapper">

  <select id="selectArticleByOrderId" parameterType="int" resultType="org.zang.domain.Article">
SELECT * FROM tb_article WHERE id IN (
SELECT article_id FROM tb_item WHERE order_id = #{id}
)
</select> </mapper>

相关映射的含义上面已解释,关联映射最主要的是能够用好如下两个标签:

  • *  对多关联映射:collection
  • *  对一关联映射:association

此外注意:一对多使用的都是lazy(懒加载)。

mybatis标签之——关联映射的更多相关文章

  1. Mybatis中的关联映射和查询

    一.商品订单数据模型 1.数据表 这里定义了四个表,分别表示用户,商品,订单,和订单详情. 用户表user CREATE TABLE `user` ( `id` int(11) NOT NULL AU ...

  2. Mybatis的关联映射

    实际的开发中,对数据库的操作常常会涉及到多张表,这在面向对象中就涉及到了对象与对象之间的关联关系.针对多表之间的操作,MyBatis提供了关联映射, 通过关联映射就可以很好的处理对象与对象之间的关联关 ...

  3. 转:Mybatis系列之集合映射

    转:Mybatis系列之集合映射 上篇文章我们讲了关联映射,实现了销售与登录用户之间的关联.本文我们接着来讲一讲集合映射,实现销售与客户的多对多关系. 实现销售与客户多对多关系 本文中仍延用<M ...

  4. mybatis 一对一关联映射实例

    在实际项目开发中,经常存在一对一的关系,如一个人对应一张身份证信息,这就是一对一的关系.下面是一个简单的实例: 1.建表过程我就省略了,主要是一张Person表,一张IDCard表,其相关属性见步骤2 ...

  5. Mybatis(四)关联映射

    一. Mybatis关联映射 1 讲数据库中关联关系,主键表与外键表 一对多,多对一,主键表表示一 与外键表表示多 学生与学校的关系,工人与工厂,人员与部门的关系,.... 多        一    ...

  6. SSM框架开发web项目系列(三) MyBatis之resultMap及关联映射

    前言 在上篇MyBatis基础篇中我们独立使用MyBatis构建了一个简单的数据库访问程序,可以实现单表的基本增删改查等操作,通过该实例我们可以初步了解MyBatis操作数据库需要的一些组成部分(配置 ...

  7. MyBatis学习(七)MyBatis关联映射之多对多映射

    对于数据库中的多对多关系建议使用一个中间表来维护关系. 1.创建四张表,分别为用户表,商品表,订单表,中间表. DROP TABLE IF EXISTS `t_user`; CREATE TABLE ...

  8. MyBatis的核心配置、动态sql、关联映射(快速总结)

    MyBatis的核心对象和配置 #1. SqlSessionFactory对象: 单个数据库映射关系经过编译的内存镜像: 作用:创建SQLSession对象. //读取配置文件 InputSteam ...

  9. mybatis之关联映射

    ###mybatis使用之一对一关联映射 1)分析并画ER图.(特别是一对一.一对多.多对多的情况) 2)启动终端数据库,并建库建表,在表中插入值和字段,并查看结果.(后期把navicat用上) 3) ...

随机推荐

  1. Entityframework:“System.Data.Entity.Internal.AppConfig”的类型初始值设定项引发异常。

    <configSections> <!-- For more information on Entity Framework configuration, visit http:// ...

  2. 端口复用技术简单了解;重用端口;socket复用端口

    端口复用相关点 多个应用复用端口,只有最后一个绑定的socket可以接受数据,所有socket都可以发送数据 使用端口复用技术时,所有的socket都开启端口复用,才可以实现端口复用 黑客技术,使用标 ...

  3. 从volatile说到i++的线程安全问题

    简介 volatile关键字保证了在多线程环境下,被修饰的变量在别修改后会马上同步到主存,这样该线程对这个变量的修改就是对所有其他线程可见的,其他线程能够马上读到这个修改后值. Thread的本地内存 ...

  4. 定义和使用EL函数

    EL为表达式语言,在EL中,允许定义和使用函数.下面将介绍如何定义和使用EL的函数. 1. 定义和使用函数 函数的定义和使用分为以下3个步骤: (1)编写一个Java类,并在该类中编写公用的静态方法, ...

  5. IIS HTTP 错误 404.17 - Not Found 解决方法

    错误提示如下图: 出现这种情况的原因通常是因为先安装了Framework,后安装的IIS: 运行cmd,输入:   C:\Windows\Microsoft.NET\Framework\V4.0.30 ...

  6. 附3 springboot源码解析 - 构建SpringApplication

    package com.microservice.framework; import org.springframework.boot.SpringApplication; import org.sp ...

  7. graphic rendering pipeline

    整理下管线 此时一定要有这张图 注意表中的数据流向 强调几个细节 之前对次序理解有点乱 rasterizer之前 管线里是只有逐顶点信息的 IA里面会setup primitive  通过Primit ...

  8. SQL Server中的database checkpoint

    基于性能方面的考虑, 数据库引擎会在内存(buffer cache)中执行数据库数据页(pages)的修改, 不会再每次做完修改之后都把修改了的page写回到磁盘上. 更准确的说, 数据库引擎定期在每 ...

  9. MD5 SHA1 哈希 签名 碰撞 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  10. layer和3D仿射变换

    1.视图的显示基于图层,通过控制图层同样能控制显示效果,获取当前的视图的layer,并为其增加圆角边框. //设置layer边框的宽度为2 view.layer.borderWidth=; //如果需 ...