Mybatis中如何实现一对一,一对多的关联查询?

Posted 崇令

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis中如何实现一对一,一对多的关联查询?相关的知识,希望对你有一定的参考价值。

MyBatis实现一对一、一对多关联查询一般有两种方式:

方式一:sqlMapper配置文件

一对一:在resultMap标签中使用 association 标签

一对多:在resultMap 标签中使用collection 标签

方式二:注解

一对一:在@Results 注解中的@Result注解中使用@One注解

一对多:在@Results 注解中的@Result 注解中使用@Many注解

接下来,我们一起来看看这两种方式分别是怎么用代码实现的?

这里需要用到学生student和班级class两张表,如下图所示:

 

 一、一对一查询(sqlMapper配置文件

1. 需求

一个学生相对应一个专属班级,所以学生Student班级Banji是一对一的关系;

查询需求:查询一个学生,与此同时查询出该学生所属的班级信息(用sqlMapper的方式)

2. 创建Student和Banji实体类

Student.java

public class Student 
	private int sid;
	private String sname;
	private Date birthday;
	private String ssex;
	private int classid;
	// 外部属性
	private Banji bj;

    // 省略无参、有参、get、set、toString方法

Banji.java 

public class Banji 
	private int classid;
	private String classname;

    //  省略无参、有参、get、set、toString方法

3. 创建StudentMapper接口

public interface StudentMapper 

	//定义方法
	public List<Student> findAllStudent();	

4. 配置 StudentMapper.xml

PS:查询的返回值类型使用的resultMap,和上面resultMap标签中的id名必须保持一致

<?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.ape.mapper.StudentMapper">
	<!-- 手动映射
		column:数据表的字段名
		property:java中实体类的属性
	-->
	<resultMap type="Student" id="Student_Class_Map">
		<result column="sid"  property="sid"/>
		<result column="sname"  property="sname"/>
		<result column="birthday"  property="birthday"/>
		<result column="ssex"  property="ssex"/>
		<result column="classid"  property="classid"/>
		<association property="bj">
			<result column="classid"  property="classid"/>
			<result column="classname"  property="classname"/>
		</association>		
	</resultMap>
	
	<!--一对一查询-->
	<select id="findAllStudent" resultMap="Student_Class_Map">
	 	select * from student
	 	inner join class on student.classid=class.classid
	</select>

</mapper>

5. 测试 Test

public class Test01 
	public static void main(String[] args) 
	
		SqlSession sqlSession = DaoUtil.getSqlSession();
		StudentMapper sm = sqlSession.getMapper(StudentMapper.class);
		
		//一对一查询
		List<Student> slist = sm.findAllStudent();
		for (Student s:slist) 
			System.out.println(s);
		

		//释放资源
		DaoUtil.closeSource(sqlSession);

输出结果为:

  

二、一对一查询(注解

1. 需求

一个学生相对应一个专属班级,所以学生Student班级Banji是一对一的关系;

查询需求:查询一个学生,与此同时查询出该学生所属的班级信息(用注解的方式)

2. 创建Student和Banji实体类

Student.java

public class Student 
	private int sid;
	private String sname;
	private Date birthday;
	private String ssex;
	private int classid;
	// 外部属性
	private Banji bj;

    // 省略无参、有参、get、set、toString方法

Banji.java 

public class Banji 
	private int classid;
	private String classname;

    //  省略无参、有参、get、set、toString方法

3. 创建StudentMapper接口

public interface StudentMapper 

//学生对班级一对一,做映射
@Results(
	@Result(column = "classid",property = "classid"),
	@Result(column = "classid",property = "bj",
		one=@One(select ="com.ape.mapper.BanjiMapper.findBanjiByClassid" ))	
)
	

@Select("select * from student")
public List<Student> findAllStudent();

4. 创建BanjiMapper接口

public interface BanjiMapper 

	//学生对班级,找班级对象
	@Select("select * from class where classid=#v")
	public Banji findBanjiByClassid(int classid);

5. 测试 Test

public class Test01 
	public static void main(String[] args) 
	
		SqlSession sqlSession = DaoUtil.getSqlSession();
		StudentMapper sm = sqlSession.getMapper(StudentMapper.class);
		
		List<Student> slist = sm.findAllStudent();
		for(Student s:slist) 
			System.out.println(s);
		
        
        DaoUtil.closeSource(sqlSession);
	

输出结果为: 

 

三、一对多查询(sqlMapper配置文件

1. 需求

一个班级对应多个学生,所以班级Banji学生Student是一对多的关系;

查询需求:查询一个班级,与此同时查询出该班级所有的学生信息(用sqlMapper的方式)

2. 创建Banji和Student实体类

Banji.java 

public class Banji 
	private int classid;
	private String classname;
    //外部属性
	private List<Student> slist;

    //  省略无参、有参、get、set、toString方法

Student.java

public class Student 
	private int sid;
	private String sname;
	private Date birthday;
	private String ssex;
	private int classid;

    // 省略无参、有参、get、set、toString方法

3. 创建BanjiMapper接口

public interface BanjiMapper 

	//班级对学生,一对多
	public List<Banji> findAllBanji();

4. 配置BanjiMapper.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">
 
<mapper namespace="com.ape.mapper.BanjiMapper">

	<resultMap type="Banji" id="Class_Student_Map">
		<result column="classid"  property="classid"/>
		<result column="classname"  property="classname"/>
		<collection property="slist" ofType="Student">
			<result column="sid"  property="sid"/>
			<result column="sname"  property="sname"/>
			<result column="birthday"  property="birthday"/>
			<result column="ssex"  property="ssex"/>
			<result column="classid"  property="classid"/>
		</collection>
	</resultMap>

	<select id="findAllBanji" resultMap="Class_Student_Map">
		select * from class
		left join student on class.classid=student.classid
		order by class.classid
	</select>

</mapper>

5. 测试 Test

public class Test01 
	public static void main(String[] args) 
	
		SqlSession sqlSession = DaoUtil.getSqlSession();
        BanjiMapper bm = sqlSession.getMapper(BanjiMapper.class);

		List<Banji> blist = bm.findAllBanji();
		for (Banji b:blist) 
			System.out.println("班级编号"+b.getClassid()+","+"班级名称"+b.getClassname());
			for(Student s:b.getSlist()) 
				System.out.println(s);
			
			System.out.println("====================================");
		

		//释放资源
		DaoUtil.closeSource(sqlSession);
    

输出结果为:

 

四、一对多查询(注解

1. 需求

一个班级对应多个学生,所以班级Banji学生Student是一对多的关系;

查询需求:查询一个班级,与此同时查询出该班级所有的学生信息(用注解的方式)

2. 创建Banji和Student实体类

Banji.java 

public class Banji 
	private int classid;
	private String classname;
    //外部属性
	private List<Student> slist;

    //  省略无参、有参、get、set、toString方法

Student.java

public class Student 
	private int sid;
	private String sname;
	private Date birthday;
	private String ssex;
	private int classid;

    // 省略无参、有参、get、set、toString方法

3. 创建BanjiMapper接口

public interface BanjiMapper 

    //班级对学生一对多,做映射
	@Results(
		@Result(column = "classid",property = "classid"),
		@Result(column = "classid",property = "slist",
				many=@Many(select="com.ape.mapper.StudentMapper.findStudentByClassid"))		
	)

		
	@Select("select * from class")
	public List<Banji> findAllBanji();

4. 创建StudentMapper接口

public interface StudentMapper 
    
    //班级对学生一对多,找学生对象
	@Select("select * from student where classid=#v")
	public Student findStudentByClassid(int classid);

5. 测试 Test

public class Test01 
	public static void main(String[] args) 
	
		SqlSession sqlSession = DaoUtil.getSqlSession();
        BanjiMapper bm = sqlSession.getMapper(BanjiMapper.class);

		List<Banji> blist = bm.findAllBanji();
		for (Banji b:blist) 
			System.out.println("班级编号"+b.getClassid()+","+"班级名称"+b.getClassname());
			for(Student s:b.getSlist()) 
				System.out.println(s);
			
			System.out.println("====================================");
		

		//释放资源
		DaoUtil.closeSource(sqlSession);
    

输出结果为: 

其实,日常这样的例子也不少:

一对一:

一个人对应一张身份证

一张身份证对应一个人

一个课程对应一个老师

一个员工对应一个部门

……

一对多:

一个用户可以有多个订单

一个用户可以有账户

一个学生可以学多个课程

一个球队有多个球员

……

以上就是Mybatis的多表关联查询(一对一、一对多)的全部内容。

看完如果对你有帮助,点赞支持一下呀!
 

 

 

mybatis笔试题2

1、Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别。

答:能,Mybatis不仅可以执行一对一、一对多的关联查询,还可以执行多对一,多对多的关联查询,多对一查询,其实就是一对一查询,只需要把selectOne()修改为selectList()即可;多对多查询,其实就是一对多查询,只需要把selectOne()修改为selectList()即可。

关联对象查询,有两种实现方式,一种是单独发送一个sql去查询关联对象,赋给主对象,然后返回主对象。另一种是使用嵌套查询,嵌套查询的含义为使用join查询,一部分列是A对象的属性值,另外一部分列是关联对象B的属性值,好处是只发一个sql查询,就可以把主对象和其关联对象查出来。

那么问题来了,join查询出来100条记录,如何确定主对象是5个,而不是100个?其去重复的原理是<resultMap>标签内的<id>子标签,指定了唯一确定一条记录的id列,Mybatis根据<id>列值来完成100条记录的去重复功能,<id>可以有多个,代表了联合主键的语意。

同样主对象的关联对象,也是根据这个原理去重复的,尽管一般情况下,只有主对象会有重复记录,关联对象一般不会重复。

举例:下面join查询出来6条记录,一、二列是Teacher对象列,第三列为Student对象列,Mybatis去重复处理后,结果为1个老师6个学生,而不是6个老师6个学生。

       t_id    t_name           s_id

|          1 | teacher      |      38 |

|          1 | teacher      |      39 |

|          1 | teacher      |      40 |

|          1 | teacher      |      41 |

|          1 | teacher      |      42 |

|          1 | teacher      |      43 |

2、Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

答:Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。

它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。

当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。

3、Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?

答:不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复;毕竟namespace不是必须的,只是最佳实践而已。

原因就是namespace+id是作为Map<String, MappedStatement>的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同。

4、Mybatis中如何执行批处理?

答:使用BatchExecutor完成批处理。

5、Mybatis都有哪些Executor执行器?它们之间的区别是什么?

答:Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。

SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。

ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。

BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。

作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。

6、Mybatis中如何指定使用哪一种Executor执行器?

答:在Mybatis配置文件中,可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数。

7.接口绑定有几种实现方式,分别是怎么实现的?

接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上

@Select@Update等注解里面包含Sql语句来绑定,另外一种就是通过xml里面写SQL来绑定,

在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名.

8.什么情况下用注解绑定,什么情况下用xml绑定

当Sql语句比较简单时候,用注解绑定,

当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多

9.MyBatis实现一对一有几种方式?具体怎么操作的

有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次,

通过在resultMap里面配置association节点配置一对一的类就可以完成;

嵌套查询是先查一个表,根据这个表里面

的结果的外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置

10、Mybatis是否可以映射Enum枚举类?

答:Mybatis可以映射枚举类,不单可以映射枚举类,Mybatis可以映射任何对象到表的一列上。映射方式为自定义一个TypeHandler,实现TypeHandler的setParameter()和getResult()接口方法。TypeHandler有两个作用,一是完成从javaType至jdbcType的转换,二是完成jdbcType至javaType的转换,体现为setParameter()和getResult()两个方法,分别代表设置sql问号占位符参数和获取列查询结果。

11、Mybatis映射文件中,如果A标签通过include引用了B标签的内容,请问,B标签能否定义在A标签的后面,还是说必须定义在A标签的前面?

答:虽然Mybatis解析Xml映射文件是按照顺序解析的,但是,被引用的B标签依然可以定义在任何地方,Mybatis都可以正确识别。

原理是,Mybatis解析A标签,发现A标签引用了B标签,但是B标签尚未解析到,尚不存在,此时,Mybatis会将A标签标记为未解析状态,然后继续解析余下的标签,包含B标签,待所有标签解析完毕,Mybatis会重新解析那些被标记为未解析的标签,此时再解析A标签时,B标签已经存在,A标签也就可以正常解析完成了。

12、简述Mybatis的Xml映射文件和Mybatis内部数据结构之间的映射关系?

答:Mybatis将所有Xml配置信息都封装到All-In-One重量级对象Configuration内部。在Xml映射文件中,<parameterMap>标签会被解析为ParameterMap对象,其每个子元素会被解析为ParameterMapping对象。<resultMap>标签会被解析为ResultMap对象,其每个子元素会被解析为ResultMapping对象。每一个<select>、<insert>、<update>、<delete>标签均会被解析为MappedStatement对象,标签内的sql会被解析为BoundSql对象。

13、为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?

答:Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。


本站代码下载方法:


以上是关于Mybatis中如何实现一对一,一对多的关联查询?的主要内容,如果未能解决你的问题,请参考以下文章

mybatis笔试题2

MyBatis高级篇 - 关联查询(一对多)

Mybatis的嵌套关联查询案例实战

第九节:mybatis关联查询之一对多查询

MyBatis总结之高级映射一对多查询

MyBatis学习09高级映射之一对多查询