6 mybatis 关联关系查询

本文深入解析了数据库查询中的一对一、一对多、多对一、多对多及自关联查询的实现方法,包括多表连接和多表单独查询两种方式,并通过具体案例详细介绍了每种查询的Mapper配置和测试代码。
关联关系

对学习课程的总结:

当查询内容涉及到具有关联关系的多个表时,就需要使用关联查询。根据表与表间的关联关系的不同,关联查询分为四种:

  1. 一对一关联查询
  2. 一对多关联查询
  3. 多对一关联查询
  4. 多对多关联查询

一、一对多关联查询

一对多关联查询是指,在查询一方对象的时候,同时将其所关联的多方对象也都查询出来。
建立实体类:
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

idnamepid
1娱乐新闻0
2体育新闻0
3NBA2
4CBA2
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:

sidsname
1张三
2李四
3王五

course:

cidcname
1JavaSE
2JavaEE
3Android

中间表:

实体类

在定义双向关联(双方均可看到对方的关联关系)的实体的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'}]}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值