关联关系
对学习课程的总结:
当查询内容涉及到具有关联关系的多个表时,就需要使用关联查询。根据表与表间的关联关系的不同,关联查询分为四种:
- 一对一关联查询
- 一对多关联查询
- 多对一关联查询
- 多对多关联查询
一、一对多关联查询
一对多关联查询是指,在查询一方对象的时候,同时将其所关联的多方对象也都查询出来。
建立实体类:
minister部长、大臣
country国家
public class Minister {
private Integer mid;
private String mname;
//getter()/setter()
}
public class Country {
private Integer cid;
private String cname;
//关联属性
private Set<Minister> ministers;
//getter()/setter()
}
方式一:通过多表连接的方式
dao层接口:
Country selectCountryById(int id);
mapper配置
<mapper namespace="com.chen.dao.ICountryDao">
<resultMap id="countryMapper" type="Country">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
<collection property="ministers" ofType="Minister">
<id column="mid" property="mid"/>
<result column="mname" property="mname"/>
</collection>
</resultMap>
<select id="selectCountryById" resultMap="countryMapper">
SELECT cid, cname, mid, mname
FROM country, minister
WHERE countryId = cid AND cid = #{id}
</select>
</mapper>
测试
@Test
public void testSelectCountryById(){
SqlSession sqlSession = null;
try{
sqlSession = MyBatisUtils.getSqlSession();
ICountryDao mapper = sqlSession.getMapper(ICountryDao.class);
Country country = mapper.selectCountryById(2);
System.out.println(country);
}finally{
if(sqlSession != null){
sqlSession.close();
}
}
}
方式二:通过多表单独方式实现
mapper配置
<select id="selectMinisterByCountry" resultType="Minister">
SELECT mid, mname FROM minister WHERE countryId = #{column}
</select>
<resultMap id="countryMapper" type="Country">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
<collection property="ministers" ofType="Minister" select="selectMinisterByCountry" column="cid"/>
</resultMap>
<select id="selectCountryById" resultMap="countryMapper">
SELECT cid, cname FROM country WHERE cid = #{cid}
</select>
dao层接口与测试同上
二、多对一的关联查询
查询多方对象的时候,同时将其所关联的一方对象也查询出来
由于在查询多方对象时也是一个一个查询,所以多对一关联查询,其实就是一对一关联查询,即一对一关联查询的实现方式与多对一的实现方式是相同的
方式一:多表连接查询
dao层接口
Minister selectMinisterById(int mid);
mapper配置
<resultMap id="ministerMapper" type="Minister">
<id column="mid" property="mid"/>
<result column="mname" property="mname"/>
<association property="country" javaType="Country">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
</association>
</resultMap>
<select id="selectMinisterById" resultMap="ministerMapper">
SELECT mid, mname, cid, cname
FROM minister, country
WHERE countryId = cid AND mid = #{mid}
</select>
测试
public class Many_2_One {
private IMinisterDao mapper;
private SqlSession sqlSession;
@Before
public void before() {
sqlSession = MyBatisUtils.getSqlSession();
mapper = sqlSession.getMapper(IMinisterDao.class);
}
@After
public void after() {
if (sqlSession != null) {
sqlSession.close();
}
}
@Test
public void test() {
Minister minister = mapper.selectMinisterById(2);
System.out.println(minister);
}
}
方式二: 多表单独方式查询
mapper配置
<select id="selectCountryByMinister" resultType="Country">
SELECT cid, cname FROM country WHERE cid = #{xxx}
</select>
<resultMap id="ministerMapper" type="Minister">
<id column="mid" property="mid"/>
<result column="mname" property="mname"/>
<association property="country" javaType="Country" select="selectCountryByMinister" column="countryId">
</association>
</resultMap>
<select id="selectMinisterById" resultMap="ministerMapper">
SELECT mid, mname, countryId FROM minister WHERE mid = #{mid}
</select>
三、自关联查询
自关联查询,自己既是一方,又是多方,是1:n或n:1的变型。
例如,对于新闻栏目NewsColumn,可以充当一方,即父栏目,也可以充当多方,即子栏目,而反应到DB表中,只有一张表,这张表中具有一个外键,用于表示该栏目的父栏目。一级栏目没有父栏目,所有可以将其外键值设为0,而子栏目则具有外键值。
为了便于理解,将自关联分为两种情况来讲解,一种是当作1:n讲解,即当前类作为一方,其包含多方的集合域属性。一种是当作n:1讲解,即当前类作为多方,其包含一方的域属性。
下面以新闻栏目为例进行讲解。由于column是DBMS中的关键字,为了避免误解,将新闻栏目实体类定义为NewsLabel。
数据库表
其中pid为parent id,即其父id
| id | name | pid |
|---|---|---|
| 1 | 娱乐新闻 | 0 |
| 2 | 体育新闻 | 0 |
| 3 | NBA | 2 |
| 4 | CBA | 2 |
| 5 | 火箭 | 3 |
| 6 | 湖人 | 3 |
| 7 | 北京金隅 | 4 |
| 8 | 浙江广厦 | 4 |
| 9 | 青岛双星 | 4 |
| 10 | 港台明星 | 1 |
| 11 | 内地影视 | 1 |
以一对多方式处理
(1)查询指定栏目的所有子孙栏目
实体类
//新闻栏目:当前的新闻栏目被看作是一方,即父栏目
public class NewsLabel {
private Integer id;
private String name; //栏目名称
private Set<NewsLabel> children;
//此处省略getter()/setter()/toString()
}
dao接口
List<NewsLabel> selectChildrenByParent(int pid);
mapper配置
<mapper namespace="com.chen.dao.INewsLabelDao">
<!-- 递归调用 -->
<resultMap id="newsLabelMapper" type="NewsLabel">
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="children" ofType="NewsLabel" select="selectChildrenByParent" column="id"/>
</resultMap>
<select id="selectChildrenByParent" resultMap="newsLabelMapper">
SELECT id, name FROM newslabel WHERE pid = #{pid}
</select>
</mapper>
测试
public class NewsLabelTest {
private INewsLabelDao mapper;
SqlSession sqlSession;
@Before
public void before() {
sqlSession = MyBatisUtils.getSqlSession();
mapper = sqlSession.getMapper(INewsLabelDao.class);
}
@After
public void after() {
if (sqlSession != null) {
sqlSession.close();
}
}
@Test
public void testNewsLabel() {
List<NewsLabel> children = mapper.selectChildrenByParent(2);
for (NewsLabel newslabel : children) {
System.out.println(newslabel);
}
}
}
测试结果
NewsLabel{id=3, name='NBA', children=[NewsLabel{id=5, name='火箭', children=[]}, NewsLabel{id=6, name='湖人', children=[]}]}
NewsLabel{id=4, name='CBA', children=[NewsLabel{id=8, name='浙江广厦', children=[]}, NewsLabel{id=9, name='青岛双星', children=[]}, NewsLabel{id=7, name='北京金隅', children=[]}]}
(2)查询指定栏目及其所有子孙栏目
dao接口
NewsLabel selectNewsLabelById(int id);
mapper配置
<!-- 递归调用,查询子栏目 -->
<select id="selectNewsLabelByPid" resultMap="newsLabelMapper2">
SELECT id, name FROM newslabel WHERE pid = #{column}
</select>
<resultMap id="newsLabelMapper2" type="NewsLabel">
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="children" ofType="NewsLabel" select="selectNewsLabelByPid" column="id"/>
</resultMap>
<!-- 根据id查询栏目,只查了一次 -->
<select id="selectNewsLabelById" resultMap="newsLabelMapper2">
SELECT id, name FROM newslabel WHERE id = #{id}
</select>
测试
@Test
public void testSelectNewsLabelById() {
NewsLabel newsLabel = mapper.selectNewsLabelById(2);
System.out.println(newsLabel);
}
结果
NewsLabel{id=2, name='体育新闻',
children=[
NewsLabel{id=4, name='CBA', children=[NewsLabel{id=9, name='青岛双星', children=[]}, NewsLabel{id=7, name='北京金隅', children=[]}, NewsLabel{id=8, name='浙江广厦', children=[]}]},
NewsLabel{id=3, name='NBA', children=[NewsLabel{id=6, name='湖人', children=[]}, NewsLabel{id=5, name='火箭', children=[]}]}]}
以多对一方式处理
以多对一方式处理,即多方可以看到一方。该处理方式的应用场景,例如在网页上显示当前页面的站内位置。
需求:查询当前栏目及其所有父辈栏目
实体类
//新闻栏目:当前的新闻栏目被看作是多方,即子栏目
public class NewsLabel2 {
private Integer id;
private String name; //栏目名称
private NewsLabel2 parent;
//此处省略getter()/setter()/toString()
}
dao接口
NewsLabel2 selectNewsLabelById2(int id);
mapper配置
<resultMap id="newLabelMapper3" type="NewsLabel2">
<id column="id" property="id"/>
<result column="name" property="name"/>
<association property="parent" javaType="NewsLabel2" select="selectNewsLabelById2" column="pid"/>
</resultMap>
<select id="selectNewsLabelById2" resultMap="newLabelMapper3">
SELECT id, name, pid FROM newslabel WHERE id = #{id}
</select>
测试
@Test
public void testSelectNewsLabelById2() {
//查询id为3的栏目,及其所有父栏目
NewsLabel2 newsLabel2 = mapper.selectNewsLabelById2(3);
System.out.println(newsLabel2);
}
结果
NewsLabel2{id=3, name='NBA', parent=NewsLabel2{id=2, name='体育新闻', parent=null}}
实际开发中NewsLabel中自关联实体类应如下定义,上面的方式,是为了更好的理解自关联,所以分开进行讲解
public class NewsLabel {
private Integer id;
private String name; //栏目名称
private NewsLabel parent; //父栏目
private Set<NewsLabel> children; //子栏目
//此处省略getter()/setter()/toString()
}
多对多关联查询
什么是多对多关联关系?一个学生可以选择多门课程,而一门课程可由多个学生选。这就是典型的多对多关系。所以,所谓多对多关系,其实就是由两个互反的一对多关系组成。
一般情况下,多对多关系都会通过一个中间表来建立,例如选课表
注意:students2和course是一方,中间表middle是多方!!!
数据库表
students2:
| sid | sname |
|---|---|
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
course:
| cid | cname |
|---|---|
| 1 | JavaSE |
| 2 | JavaEE |
| 3 | Android |
中间表:
实体类
在定义双向关联(双方均可看到对方的关联关系)的实体的toString()方法时,只让一方的toString()方法中可以输出对方,不要让双方均可输出对方,否则将会出现输出时的递归现象,程序报错
public class Student2 {
private Integer sid;
private String sname;
private Set<Course> courses;
//省略getter()/setter()/toString()
}
public class Course {
private Integer cid;
private String cname;
private Set<Student2> student2s;
//省略getter()/setter()/toString()
}
dao接口
Student2 selectStudentById(int sid);
mapper配置
<mapper namespace="com.chen.dao.IStudent2Dao">
<resultMap id="studentMapper" type="Student2">
<id column="sid" property="sid"/>
<result column="sname" property="sname"/>
<collection property="courses" ofType="Course">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
</collection>
</resultMap>
<select id="selectStudentById" resultMap="studentMapper">
SELECT sid, sname, cid, cname FROM student2, middle, course
WHERE sid = studentId AND cid = courseId and sid=#{sid}
</select>
</mapper>
测试
public class Many2ManyTest {
private IStudent2Dao mapper
;
SqlSession sqlSession;
@Before
public void before() {
sqlSession = MyBatisUtils.getSqlSession();
mapper = sqlSession.getMapper(IStudent2Dao.class);
}
@After
public void after() {
if (sqlSession != null) {
sqlSession.close();
}
}
@Test
public void testNewsLabel() {
Student2 student2 = mapper.selectStudentById(1);
System.out.println(student2);
}
}
结果
Student2{sid=1, sname='张三',
courses=[Course{cid=2, cname='JavaEE'}, Course{cid=1, cname='JavaSE'}]}
本文深入解析了数据库查询中的一对一、一对多、多对一、多对多及自关联查询的实现方法,包括多表连接和多表单独查询两种方式,并通过具体案例详细介绍了每种查询的Mapper配置和测试代码。

5587

被折叠的 条评论
为什么被折叠?



