MyBatis--关联查询

MyBatis--关联查询

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

  • 一对一关联查询
  • 一对多关联查询
  • 多对一关联查询
  • 多对多关联查询

一、一对多关联查询

一对多关联查询是指,在查询一对象的时候,同时将其所关联的多放对象也都查询出来。

例:国家Country与部长Minister间的一对多关系

1.定义实体

若定义的是双向关联,即双方的属性中均有对方的对象作为作用域属性出现,那么它们在定义个各自的toString()方法时需要注意,只让某一方可以输出另一方即可,不要让双方的toString()方法均可输出对方。这样会形成递归调用,程序出错。

Country:

public class Country {
    
    private Integer cid;
    private String cname;
    //关联属性
    private Set<Minister> ministers;
}

Minister:

public class Minister {
    private Integer mid;
    private String mname;
}

2.定义数据库表

country.PNG
minister.PNG

3.定义dao接口

public interface ICountyrDao {  
    Country selectCountryById(int cid);
}

4.测试类:

public class MyTest {
    
    private ICountryDao dao;
    private SqlSession session;
            
    @Before
    public void setUp(){
        session = MyBatisUtils.getSqlSession();
        dao = session.getMapper(ICountryDao.class);
    }
    
    @After
    public void tearDown(){
        if(session!=null){
            session.close();
        }
    }
    
    @Test
    public void test01(){           
        Country country = dao.selectCountryById(2);
        System.out.println(country);
    }   
}

5.映射文件

方式一:多表连接查询方式:

<mapper namespace="com.hcx.dao.ICountryDao">

    <resultMap type="Country" id="countryMapper">
        <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,minster
        where countryId=cid and cid=#{cid}
    </select>

</mapper>

注意,此时即使字段名与属性名相同,在<resultMap/>中也要写出他们的映射关系。因为框架是依据<resultMap/>封装对象的。

在映射文件中使用<collection/>标签体现出两个实体对象间的关联关系。其两个属性的意义为:

  • property:指定关联属性,即Country类中的集合属性
  • ofType:集合属性的泛型类型

方式二:多表单独查询方式:

多表连接查询方式是将多张表进行连接,连为一张表后进行查询。其查询的本质是一张表。而多表单独查询方式是多张表各自查询各自的相关内容,需要多张表的联合数据,则将主表的查询结果联合其他表的查询结果,封装为一个对象。

多个查询时可以跨越多个映射文件的,即是可以跨越多个namespace的。在使用其它namespace的查询时,添加上其所在的namespace即可。

<mapper namespace="com.hcx.dao.ICountryDao">

    <select id="selectMinisterByCountry" resultType="Minister">
        select mid,mname from minister where countryId=#{cid}
    </select>
    
    <resultMap type="Country" id="countryMapper">
        <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>

</mapper>
一对多单独查询.PNG

手动sql语句过程:

select cid,cname from country where cid=2
1.PNG
select mid,mname from minister where countryId=2
2.PNG

关联属性<collection/>的数据来自于另一个查询<selectMinisterByCountry/>。而该查询<selectMinisterByCountry>的动态参数countryId=#{cid}的值来自于查询<selectCountryById/>的查询结果字段cid。

二、多对一关联查询

这里的多对一关联查询是指,在查询多方对象的时候,同时将其所关联的一方对象也查询出来。

由于查询多方对象时也是一个一个查询,所以多对一关联查询,其实就是一对一关联查询。即一对一关联查询的实现方式与多对一的实现方式是相同的。

例:部长Minister与国家Country间的多对一关系

1.实体类:

Minister:

public class Minister {
    private Integer mid;
    private String mname;
    //关联属性
    private Country country;
}

Country:

public class Country {      
    private Integer cid;
    private String cname;       
}

2.dao接口:

public interface IMinisterDao { 
    Minister selectMinisterById(int mid);
}

3.测试类:

public class MyTest {
    
    private IMinisterDao dao;
    private SqlSession session;
    
    
    @Before
    public void before(){
        session = MyBatisUtils.getSqlSession();
        dao = session.getMapper(IMinisterDao.class);
    }
    
    @After
    public void tearDown(){
        if(session!=null){
            session.close();
        }
    }
    
    @Test
    public void test01(){           
        Minister minister = dao.selectMinisterById(2);
        System.out.println(minister);
    }

}

映射文件:

方式一:多表连接查询方式:

<mapper namespace="com.hcx.dao.IMinisterDao">

    <resultMap type="Minister" id="ministerMapper">
        <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>
    
</mapper>

注意,在映射文件中使用<association/>标签体现出两个实体对象间的关联关系。

  • property:指定关联属性,即Minister类中的country属性
  • javaType:关联属性的类型

方式二:多表单独查询方式:

<mapper namespace="com.hcx.dao.IMinisterDao">
    
    <select id="selectCountryById" resultType="Country">
        select cid,cname from country where cid=#{cid}
    </select>
    
    <resultMap type="Minister" id="ministerMapper">
        <id column="mid" property="mid"/>
        <result column="mname" property="mname"/>
        <association property="country"
                     javaType="Country"
                     select="selectCountryById"
                     column="countryId"/>
    </resultMap>
    
    <select id="selectMinisterById" resultMap="ministerMapper">
        select mid,mname,countryId from minister where mid=#{mid}
    </select>
</mapper>
多对一单独查询.PNG

手动写sql过程:

select mid,mname,countryId from minister where mid=2
11.PNG
select cid,cname from country where cid=1
22.PNG

三、自关联查询

所谓自关联查询是指,自己即充当一方,又充当多方,是1:n或n:1的变型。例如,对于新闻栏目NewsLabel,可以充当一方,即父栏目,也可以充当多方,即子栏目。而反映到DB表中,只有一张表,这张表中具有一个外键,用于表示该栏目的父栏目。一级栏目没有父栏目,所以可以将其外键值设为0,而子栏目则具有外键值。

将自关联分为两种情况。一种是当作1:n,即当前类作为一方,其包含多方的集合域属性。一种是当作n:1,即当前类作为多方,其包含一方的域属性。

1.自关联的DB表

自关联DB表.PNG

手动查询:

自关联手动1.PNG
自关联手动2.PNG
自关联手动3.PNG

2.以一对多方式处理

以一对多方式处理,即一方可以看到多方。该处理方式的应用场景比较多。例如页面点击父栏目,显示出其子栏目。将鼠标定位在窗口中的某菜单项上会显示其所有子菜单项等。

查询指定栏目的所有子孙栏目

根据指定的id,仅查询出其所有子栏目。包括其所有辈分的孙子栏目。即给出的插叙id实际为父栏目id

a.实体类

//新闻栏目:当前的新闻栏目被看作是一方,即父栏目
public class NewsLabel {
    private Integer id;
    private String name; //栏目名称
    private Set<NewsLabel> children;
}

b.dao接口

public interface INewsLabelDao {    
    List<NewsLabel> selectChildrenByParent(int pid);
}

c.mapper映射

通过select语句的递归调用实现查询所有下级栏目的功能。查询结果的集合数据<collection/>来自于递归调用的selectChildrenByParentId查询。与第一次进行该查询不同的是,第一次的pid动态参数值来自于调用方法传递来的实参,而<collection/>中查询语句的pid动态参数值来自于上一次的查询结果的id值

<mapper namespace="com.hcx.dao.INewsLabelDao">
    
    <resultMap type="NewsLabel" id="newslabelMapper">
        <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>

d.测试类

public class MyTest {
    
    private INewsLabelDao dao;
    private SqlSession session;
            
    @Before
    public void before(){
        session = MyBatisUtils.getSqlSession();
        dao = session.getMapper(INewsLabelDao.class);
    }
    
    @After
    public void tearDown(){
        if(session!=null){
            session.close();
        }
    }
    
    @Test
    public void test01(){       
        List<NewsLabel> children = dao.selectChildrenByParent(1);
        for (NewsLabel newsLabel : children) {
            System.out.println(newsLabel);
        }
    }   
}

查询指定栏目及其所有子孙栏目

查询结果既要包含指定id的当前栏目,还包含其所有辈分的孙子栏目。即给出的id实际为当前要查询的栏目的id。

a.dao接口:

public interface INewsLabelDao {    
    //List<NewsLabel> selectChildrenByParent(int pid);
    NewsLabel selectNewsLabelById(int id);
}

b.mapper映射文件

<mapper namespace="com.hcx.dao.INewsLabelDao">

    <select id="selectNewslabelByParent" resultMap="newslabelMapper">
        select id,name from newslabel where pid=#{id}
    </select>
    
    <resultMap type="NewsLabel" id="newslabelMapper">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <collection property="children"
                    ofType="NewsLabel"
                    select="selectNewslabelByParent"
                    column="id"/>
    </resultMap>
    
    <select id="selectNewslabelById" resultMap="newsLabelMapper">
        select id,name from newslabel where id=#{id}
    </select>
        
</mapper>

c.测试类

@Test
public void test02(){
    NewsLabel newslabel = dao.selectNewsLabelById(1);
    System.out.println(newslabel);
}

3.以多对一方式处理

多对一方式处理,即多方可以看到一方。该处理方式的应用场景,例如在网页上显示当前页面的站内位置

a.实体类

//新闻栏目:当前的新闻栏目被看作是多方,即子栏目
public class NewsLabel {
    private Integer id;
    private String name; //栏目名称
    private NewsLabel parent;
}

b.dao接口

public interface INewsLabelDao {    
    //List<NewsLabel> selectChildrenByParent(int pid);
    NewsLabel selectNewsLabelById(int id);
}

c.mapper映射:

<mapper namespace="com.hcx.dao.INewsLabelDao">
    
    <!-- <select id="selectNewsLabelById" resultMap="newslabelMapper">
        select id,name,pid from newslabel where id=#{pid}
    </select> -->
    
    <resultMap type="NewsLabel" id="newslabelMapper">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <association property="parent"
                     javaType="NewsLabel"
                     select="selectNewsLabelById"
                     column="pid"/>
    </resultMap>
    
    <select id="selectNewsLabelById" resultMap="newslabelMapper">
        select id,name,pid from newslabel where id=#{id}
    </select>
        
</mapper>

d.测试类

@Test
public void test02(){
    NewsLabel newslabel = dao.selectNewsLabelById(1);
    System.out.println(newslabel);
}

四、多对多关联查询

多对多关联关系,例如一个学生可以选多门课程,而一门课程可以由多个学生选择。多对多关系,其实是由两个互反的一对多关系组成。一般情况下,多对多关系都会通过一个中间表来建立,例如选课表。

1.定义实体:

在定义双向关联(双方均可看到对方的关联关系)的实体的toString方法时,只让一方的toString方法中输出对方,不要让双方均可输出对方。否则将会出现输出时的递归现象。

Student:

public class Student {
    
    private Integer sid;
    private String sname;
    private Set<Course> courses;
}

Course:

public class Course {
    
    private Integer cid;
    private String cname;
    private Set<Student> students;
}

2.数据库表:

多对多表.PNG

3.dao接口

public interface IStudentDao {
    Student selectStudentById(int sid);
}

4.mapper映射

多对多关联关系也是通过映射文件<resultMap/>的<collection/>体现的。但是,需要注意的是SQL语句中是对三张表的连接查询。

<mapper namespace="com.hcx.dao.IStudentDao">
    
    <resultMap type="Student" id="studentMapper">
        <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 student,middle,course
        where sid=studentId and cid=courseId and sid=#{sid}
    </select>
        
</mapper>

5.测试类:

public class MyTest {
    
    private IStudentDao dao;
    private SqlSession session;
    
    
    @Before
    public void before(){
        session = MyBatisUtils.getSqlSession();
        dao = session.getMapper(IStudentDao.class);
    }
    
    @After
    public void tearDown(){
        if(session!=null){
            session.close();
        }
    }
    
    @Test
    public void test01(){
        Student student = dao.selectStudentById(1);
        System.out.println(student);
    }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,273评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,349评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,709评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,520评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,515评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,158评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,755评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,660评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,203评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,287评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,427评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,122评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,801评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,272评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,393评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,808评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,440评论 2 359

推荐阅读更多精彩内容