MyBatis篇5- 关联查询:association查询、collection查询

2,026 阅读7分钟

关联查询

association查询

实现一对一或多对一关联查询,就是主表与关联表的数据是一对一或多对一,例如员工为主表,关联部门表查询,1个员工在一个部门或者多个员工在1个部门。对应java类,1个部门类,1个员工类,员工类中有一个部门属性。

collection查询

就是主表与关联表的数据是一对多或多对多,例如部门为主表,员工关联表查询,一个部门有1个员工或者1个员工有多个员工。对应java类,1个部门类,1个员工类,部门类中有一个员工集合的属性。

下面的测试案例使用xml+接口的方式对数据库进行查询,分为八个文件:

主配置文件,

关联表Dept的实体类,关联表Dept的接口,关联表Dept的xml映射器,

主表Emp的实体类,主表Emp的接口,主表Emp的xml映射器,

调用程序

测试案例

主配置文件

<?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>
        	<!-- 配置封装类的别名集,注意,配置别名要放在主配置文件开头 -->
    	<typeAliases>
    		<typeAlias type="com.test.day40.Emp" alias="Emp"/>
        <typeAlias type="com.test.day40.Dept" alias="Dept"/>
	</typeAliases>
 
        <!-- 数据源环境配置 -->
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver" />
				<property name="url" value="jdbc:mysql://localhost:3306/sys?useUnicode=true&amp;characterEncoding=UTF-8" />
				<property name="username" value="root" />
				<property name="password" value="root" />
			</dataSource>
		</environment>
	</environments>
	
		<!-- 主配置文件引入映射器 -->
	<mappers>
        <mapper resource="com/test/day40/EmpMapper.xml" />
        <mapper resource="com/test/day40/DeptMapper.xml" />
	</mappers>
</configuration>

主表Emp的xml映射器

<?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">
    
<!-- 接口与xml映射器混合使用:mapper标签的namespace属性值是接口的全名(包名.接口)-->
<mapper namespace="com.test.day40.EmpMapper">

    	<!-- 
    	resultMap是配置emp类的属性与emp表的字段的对应关系
    	它可以不全都配置,只配置你需要的部分
    	比如说类有10个属性,值需要使用其中的5个属性与表的字段去对应,你就配置5个
	 -->
	<resultMap id="empMap" type="Emp">
		<!-- 配置emp类的属性与emp表的字段的对应关系 -->
		<id property="id" column="id"/>
		<result property="eName" column="e_name"/>
		<result property="sex" column="sex"/>
		<result property="eNo" column="e_no"/>
		<result property="dId" column="d_id"/>
	</resultMap>

	<!-- 
    	resultMap可以配置多个,但是id不能重复
    	为了防止重复的配置,可以继承resultMap继承其他的resultMap
	 -->
	<resultMap id="empAndDeptMap" type="Emp" extends="empMap">
		<!-- 
    		在子的resultMap中重新配置父的属性映射,
    		子的映射可以覆盖父的映射,但是只是在子的resultMap生效,不影响父的resultMap
    		在当前来说,就是empAndDeptMap中的id属性映射eid字段
    		但是empMap中,依然还是id属性映射id字段
		 -->
		<id property="id" column="eid"/>
		<!-- 
    		association标签实现一对一或多对一关联查询,
    		就是主表与关联表的数据是一对一或多对一,
    		例如员工为主表,关联部门表查询,1个员工在一个部门或者多个员工在1个部门。
    		对应java类,1个部门类,1个员工类,员工类中有一个部门属性。
    		
            以SQL语句为例(一对一或多对一):SELECT * FROM emp e LEFT JOIN dept d ON e.d_id = d.id
    		property="dept",emp类的dept属性名
    		column="id",根据关联查询条件ON e.d_id = d.id,dept表与emp的关联字段是dept表的id字段
    		javaType="Dept",emp类的dept属性的类型是Dept类
    		resultMap="com.test.day40.DeptMapper.deptMap",emp类的dept属性对应的resultMap,
            如果这个resultMap不在当前的xml配置文件中,值就要写成resultMap所在的配置文件的namespace.resultMap的id
    		例如:id是deptMap的resultMap在namespace是com.test.day40.DeptMapper的xml配置文件中,resultMap="com.test.day40.DeptMapper.deptMap"
		 -->
		<association property="dept" column="id" javaType="Dept" resultMap="com.test.day40.DeptMapper.deptMap"/>
	</resultMap>

	<!-- 
    	在关联查询中,哪个是主表,就把这个查询写在哪个的xml配置文件中
    	如果在查询个过程中,查询的字段出现相同的字段名,我们要给其中一个加别名,保证没有重名的字段
    	加了别名的字段,要在resultMap中修改属性与字段的映射
	 -->
	<select id="select" resultMap="empAndDeptMap">
		SELECT 
		e.id eid,e.e_name,e.sex,e.e_no,e.d_id,d.id,d.d_name,d.d_desc,d.d_no
		 FROM emp e LEFT JOIN dept d ON e.d_id = d.id
	</select>

</mapper>

主表Emp的接口

package com.test.day40;

import java.util.List;

//接口和映射器混合使用:方法名与xml映射器中的id值对应
public interface EmpMapper {
    	public List<Emp> select();
}

主表Emp的测试实体类

package com.test.day40;

import java.io.Serializable;

public class Emp implements Serializable{
    	private static final long serialVersionUID = 1L;
	
    	public Integer id;
	public String eName;
	public String sex;
	public String eNo;
	public Integer dId;
	public Dept dept;
	
	public Dept getDept() {
		return dept;
	}
	public void setDept(Dept dept) {
		this.dept = dept;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String geteName() {
		return eName;
	}
	public void seteName(String eName) {
		this.eName = eName;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public String geteNo() {
		return eNo;
	}
	public void seteNo(String eNo) {
		this.eNo = eNo;
	}
	public Integer getdId() {
		return dId;
	}
	public void setdId(Integer dId) {
		this.dId = dId;
	}
	
	@Override
	public String toString() {
		return "emp[id=" + id + ",eName=" + eName + ",sex=" + sex + ",eNo=" + eNo + ",dId" 
				+ dId + ",dept=" + dept + "]";
	}
}

关联表Dept的xml映射器

<?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">

<!-- 接口与xml映射器混合使用:mapper标签的namespace属性值是接口的全名(包名.接口)-->
<mapper namespace="com.test.day40.DeptMapper">

    	<resultMap id="deptMap" type="Dept">
		<!-- 配置Dept类的属性与Dept表的字段的对应关系 -->
		<id property="id" column="id"/>
		<result property="dName" column="d_name"/>
		<result property="dDesc" column="d_desc"/>
		<result property="dNo" column="d_no"/>
	</resultMap>

	<resultMap id="deptAndEmpMap" type="Dept" extends="deptMap">
		<!-- 覆盖父的resultMap关于id的映射配置 -->
		<id property="id" column="did"/>
		<!-- 
		基于collection查询(一对多),
		就是主表与关联表的数据是一对多或多对多,例如部门为主表,员工关联表查询,
		一个部门有1个员工或者1个员工有多个员工。
		
        以SQL语句为例(一对多):SELECT * FROM dept d LEFT JOIN emp e ON e.d_id = d.id
		collection标签,是用来配置实体类的集合型的属性
		property="emps",实体类的集合型的属性的属性名
		ofType="Emp",实体类的集合型的属性的泛型
		resultMap="com.test.day40.EmpMapper.empMap",实体类的集合型的属性的泛型对应resultMap
		值就要写成resultMap所在的配置文件的namespace.resultMap的id
		 -->
		<collection property="emps" ofType="Emp" resultMap="com.test.day40.EmpMapper.empMap"/>
	</resultMap>

	<select id="select" resultMap="deptAndEmpMap">
		SELECT 
		e.id,e.e_name,e.sex,e.e_no,e.d_id,d.id did,d.d_name,d.d_desc,d.d_no
		 FROM dept d LEFT JOIN emp e ON e.d_id = d.id
	</select>
</mapper>

关联表Dept的接口

package com.test.day40;

import java.util.List;

//接口和映射器混合使用:方法名与xml映射器中的id值对应
public interface DeptMapper {
    	public List<Dept> select();
}

关联表Dept的测试实体类

package com.test.day40;

import java.io.Serializable;
import java.util.List;

public class Dept implements Serializable{
	
    	private static final long serialVersionUID = 1L;
	
	public Integer id;
	public String dName;
	public String dNo;
	public String dDesc;
	public List<Emp> emps;
	
	public List<Emp> getEmps() {
		return emps;
	}
	public void setEmps(List<Emp> emps) {
		this.emps = emps;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getdName() {
		return dName;
	}
	public void setdName(String dName) {
		this.dName = dName;
	}
	public String getdNo() {
		return dNo;
	}
	public void setdNo(String dNo) {
		this.dNo = dNo;
	}
	public String getdDesc() {
		return dDesc;
	}
	public void setdDesc(String dDesc) {
		this.dDesc = dDesc;
	}
	
	@Override
	public String toString() {
		return "[id=" + id + ",dName=" + dName + ",dNo=" + dNo + ",dDesc=" + dDesc + ",emps=" + emps +"]";
	}
}

调用程序:接口和映射器混合使用

public class XmlAndInMapperTest {
    	public static void main(String[] args) {
    		Reader reader = null;
    		SqlSessionFactory sqlSessionFactory = null;
    		SqlSession session = null;
    		try{
    			//加载主配置文件,主配置文件中有数据库连接相关信息,还有映射器的相关信息
    			//参数是主配置文件的路径,我们的主配置文件是在跟路径下,所以直接写文件名就可以
			reader = Resources.getResourceAsReader("myBatis-config.xml");
			
			//创建SqlSessionFactory工厂
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
			
			//得到SqlSession对象,这个对象是操作映射器的对象
			session = sqlSessionFactory.openSession();
            
                        //操作接口与xml混合映射器:getMapper(接口的class).调用的方法		
//			List<Emp> list = session.getMapper(EmpMapper.class).select();//测试association查询
			
			List<Dept> list = session.getMapper(DeptMapper.class).select();//测试collection查询
			System.out.println(list);	
   
			//提交session
			session.commit();
		}catch(Exception e){
			//如果发生异常回滚session
			session.rollback();
		}finally{
			//关闭资源
			try {
				reader.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			session.close();
		}
	}
}