MyBatis关联查询

8 篇文章 0 订阅

关联查询

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

(1)一对一关联查询
(2)一对多关联查询
(3)多对一关联查询
(4)多对多关联查询

日常工作中最常见的关联关系是一对多、多对一与多对多,一对一关联查询的解决方案与多对一解决方案是相同的。

一对多关联查询

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

下面以国家 Country 与部长 Minister 间的一对多关系进行演示。

(1) 定义实体

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

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

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

}

(2) 定义数据库表

{% asset_img ass01.jpg %}

{% asset_img ass02.jpg %}

(3) 定义 Dao 层接口

public interface ICountryDao {
	Country selectCountryById(int cid);

}

(4) 定义工具类

public class MyBatisUtils {
	private static SqlSessionFactory sqlSessionFactory;

	public static SqlSession getSqlSession() {
		try {
			InputStream is = Resources.getResourceAsStream("mybatis.xml");
			if (sqlSessionFactory == null) {
				sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
			}
			return sqlSessionFactory.openSession();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
}

(5) 定义映射文件

<?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="com.huang.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,minister
		where countryId=cid and cid=#{xxx} 
	</select>
	
</mapper>

(6) 定义主配置文件

<?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>
    
	<!-- 注册DB连接四要素属性文件 -->
	<properties resource="jdbc.properties" />
	
	<!-- 定义类型别名 -->
	<typeAliases>
		<!-- 将指定包中所有类的简单类名当作其别名 -->
		<package name="com.huang.beans" />
	</typeAliases>

	<!-- 配置运行环境 -->
	<environments default="testEM">
		<environment id="testEM">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.user}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>

	<!-- 注册映射文件 -->
	<mappers>
		<mapper resource="com/huang/dao/mapper.xml" />
	</mappers>

</configuration>

(7) 定义测试类

public class MyTest {
	private ICountryDao dao;
	private SqlSession sqlSession;

	@Before
	public void before() {
		sqlSession = MyBatisUtils.getSqlSession();
		dao = sqlSession.getMapper(ICountryDao.class);
	}
	
	@After
	public void after() {
		if(sqlSession != null) {
			sqlSession.close();
		}
	}
	
	
	@Test
	public void test01() {
		Country country = dao.selectCountryById(2);
		System.out.println(country);
	}
	
}

(8) 运行结果

{% asset_img ass03.jpg %}

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

  • property: 指定关联属性, 即 Country 类中的集合属性
  • ofType:集合属性的泛型类型
多表单独查询方式

多表连接查询方式是将多张表进行连接,连为一张表后进行查询。其查询的本质是一张表。

{% asset_img ass05.jpg %}

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

多对一关联查询

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

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

下面以部长 Minister 与国家 Country 间的多对一关系进行演示。

1. 多表连接查询方式

(1) 修改实体

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

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

}

(2) 修改数据库

{% asset_img ass06.jpg %}

(3) 修改 Dao 层接口

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

(4) 修改映射文件

	<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=#{xxx}
	</select>

(5) 修改测试类

public class MyTest {
	private IMinisterDao dao;
	private SqlSession sqlSession;

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

(6) 运行结果

{% asset_img ass07.jpg %}

2. 多表单独查询方式

(1) 修改映射文件

{% asset_img ass08.jpg %}

(2) 运行结果

{% asset_img ass09.jpg %}

自关联查询

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

为了便于理解,将自关联分为两种情况来讲解。一种是当作 1:n 讲解,即当前类作为一方,其包含多方的集合域属性。一种是当作 n:1 讲解,即当前类作为多方,其包含一方的域属性。下面以新闻栏目为例:

1. 以一对多方式处理

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

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

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

(1) 自关联的 DB 表

{% asset_img ass10.jpg %}

(2) 定义实体类

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

}

(3) 定义 Dao 接口

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

(4) 定义 mapper 映射

<mapper namespace="com.huang.dao.INewsLabelDao">
	<!-- 
	<select id="" resultMap="newslabelMapper">
		select id,name from newslabel where pid=#{ooo}
	</select>
	 -->
	<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=#{xxx}
	</select>
	
</mapper>

(5) 定义测试类

public class MyTest {
	private INewsLabelDao dao;
	private SqlSession sqlSession;

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

(6) 运行结果

{% asset_img ass11.jpg %}

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

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

(1) 修改 Dao 接口

public interface INewsLabelDao {
	NewsLabel selectNewsLabelById(int id);
}

(2) 修改 mapper 映射

<select id="selectNewslabelByParent" resultMap="newslabelMapper">
		select id,name 
		from newslabel 
		where pid=#{ooo}
	</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=#{xxx}
	</select>
	
</mapper>

(3) 修改测试类

	@Test
	public void test01() {
		NewsLabel newsLabel = dao.selectNewsLabelById(3);
		System.out.println(newsLabel);
		
	}

(4)运行结果

{% asset_img ass12.jpg %}

注意 A 和 B 查询的区别

2. 以多对一方式处理

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

(1) 定义实体类

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

}

(2) 定义 Dao 接口

public interface INewsLabelDao {
	NewsLabel selectNewsLabelById(int id);
}

(3) 定义 mapper 映射

	<resultMap type="NewsLabel" id="newslabelMapper">
		<id column="id" property="id"/>
		<result column="name" property="name"/>
		<collection property="parent" 
					javaType="NewsLabel"
					select="selectNewsLabelById"
					column="pid"/>
	</resultMap>
	
	<select id="selectNewsLabelById" resultMap="newslabelMapper">
		select id,name,pid 
		from newslabel 
		where id=#{xxx}
	</select>

(4) 定义测试类

	@Test
	public void test01() {
		NewsLabel newsLabel = dao.selectNewsLabelById(9);
		System.out.println(newsLabel);
		
	}

(5) 运行结果

{% asset_img ass13.jpg %}

多对多关联查询

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

(1) 定义实体

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

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

}

(2) 定义数据库表

{% asset_img ass14.jpg %}

{% asset_img ass15.jpg %}

{% asset_img ass16.jpg %}

(3) 定义 Dao 层接口

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

(4) 定义工具类

public class MyBatisUtils {
	private static SqlSessionFactory sqlSessionFactory;

	public static SqlSession getSqlSession() {
		try {
			InputStream is = Resources.getResourceAsStream("mybatis.xml");
			if (sqlSessionFactory == null) {
				sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
			}
			return sqlSessionFactory.openSession();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
}

(5) 定义 mapper 映射

	<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=#{xxx}
	</select>

(5) 定义测试类

	@Test
	public void test01() {
		Student student = dao.selectStudentById(1);
		System.out.println(student);
		
	}

(6) 运行结果

{% asset_img ass17.jpg %}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值