一、环境搭建

项目结构

在这里插入图片描述

数据库

1.学生表com_student

在这里插入图片描述

2.课程表com_course

在这里插入图片描述

3.班级表com_class

在这里插入图片描述

4.档案表com_archives

在这里插入图片描述

关系说明:

  • 学生表(com_student)和学生档案表(com_archives)的关系是一对一,即一名学生只有一个档案,一个档案只能代表一名学生。
  • 学生表(com_student)和学生班级表(com_class)的关系是一对多,即一名学生只有一个班级,一个班级有众多学生。
  • 学生表(com_student)和学生课程表(com_course)的关系是多对多,即一名学生有多门课程,一门课程有多名学生选修。

无论是哪一种查询,真正的关注点只有映射配置文件一个(即:StudentDao.xml)。

二、MyBatis级联查询(一对一)

1.association、级联属性查询

说明:档案类(Archives.java)是学生类(Student.java)的一个属性;查询结束返回的是一个对象。
使用场景:查询出某一个学生的所有信息并且显示出该学生的档案信息。

  • Student.java(实体类)
package com.xx.Bean;

public class Student {

	private int id;
	
	private String stuName;
	
	private int stuAge;
	
	//课程表的外键
	private int stuClassId;
	
	//Archives类是Student类的一个属性(级联查询)
	private Archives arch;

	public Archives getArch() {
		return arch;
	}

	public void setArch(Archives arch) {
		this.arch = arch;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getStuName() {
		return stuName;
	}

	public void setStuName(String stuName) {
		this.stuName = stuName;
	}

	public int getStuAge() {
		return stuAge;
	}

	public void setStuAge(int stuAge) {
		this.stuAge = stuAge;
	}

	public int getStuClassId() {
		return stuClassId;
	}

	public void setStuClassId(int stuClassId) {
		this.stuClassId = stuClassId;
	}


	public Student(int id, String stuName, int stuAge, int stuClassId, Archives arch) {
		super();
		this.id = id;
		this.stuName = stuName;
		this.stuAge = stuAge;
		this.stuClassId = stuClassId;
		this.arch = arch;
	}

	@Override
	public String toString() {
		return "Student [id=" + id + ", stuName=" + stuName + ", stuAge=" + stuAge + ", stuClassId=" + stuClassId
				+ ", arch=" + arch + "]";
	}

	public Student() {
		super();
		// TODO Auto-generated constructor stub
	}
}

  • Archives.java(实体类)
package com.xx.Bean;

public class Archives {

	private int id;
	
	private String archName;
	
	private String archNum;
	
	//学生表的外键
	private int stuId;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getArchName() {
		return archName;
	}

	public void setArchName(String archName) {
		this.archName = archName;
	}

	public String getArchNum() {
		return archNum;
	}

	public void setArchNum(String archNum) {
		this.archNum = archNum;
	}

	public int getStuId() {
		return stuId;
	}

	public void setStuId(int stuId) {
		this.stuId = stuId;
	}

	@Override
	public String toString() {
		return "Archives [id=" + id + ", archName=" + archName + ", archNum=" + archNum + ", stuId=" + stuId + "]";
	}

	public Archives(int id, String archName, String archNum, int stuId) {
		super();
		this.id = id;
		this.archName = archName;
		this.archNum = archNum;
		this.stuId = stuId;
	}

	public Archives() {
		super();
		// TODO Auto-generated constructor stub
	}
}

  • StudentDao.java(接口)
package com.xx.Dao;

import java.util.List;

import com.xx.Bean.Student;

public interface StudentDao {
	
	//传递一个学生id,返回一条Student数据
	public Student getStudent(int id);
	
}

  • mybatis-config.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>											
	<!-- resource是jdbc配置文件 -->
  	<properties resource="sql.properties"></properties>
  	<!-- 
  		mapUnderscoreToCamelCase:是否开启自动驼峰命名规则(camel case)映射
  	 -->
  	<settings>
  		<setting name="mapUnderscoreToCamelCase" value="true"/>
  	</settings>
  	<environments default="development">
	   <environment id="development">
	      <transactionManager type="JDBC"/>
	      <dataSource type="POOLED">
	        <property name="driver" value="${sql_driver}"/>
	        <property name="url" value="${sql_url}"/>
	        <property name="username" value="${sql_username}"/>
	        <property name="password" value="${sql_password}"/>
	      </dataSource>
	    </environment>
	  </environments>
  <mappers>
    <package name="com.xx.Dao"/> 
  </mappers>
</configuration>
  • StudentDao.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.xx.Dao.StudentDao">
 	<select id="getStudent" resultMap="getStudentForAssoc">
 		select 
 		stu.*,
 		arch.id aid,
 		arch.arch_name,
 		arch.arch_num,
 		arch.stu_id 
 		from 
 		com_student stu 
 		left join 
 		com_archives arch 
 		on 
 		stu.id = arch.stu_id 
 		where 
 		stu.id=#{id}
 	</select> 
 	
 	<!-- 方法一:了解 -->
 	<resultMap type="com.xx.Bean.Student" id="getStudentForSimple">
 		<id property="id" column="id"/>
 		<result property="stuName" column="stu_name"/>
 		<result property="stuAge" column="stu_age"/>
 		<result property="stuClassId" column="stu_class_id"/>
 		<result property="arch.id" column="aid"/>
 		<result property="arch.archName" column="arch_name"/>
 		<result property="arch.archNum" column="arch_num"/>
 		<result property="arch.stuId" column="stu_id"/>
 	</resultMap>
 	
 	<!-- 方法二:使用此种方法!!! -->
 	<resultMap type="com.xx.Bean.Student" id="getStudentForAssoc">
 		 <!-- 
  			property:类的属性,
  			column:查询出来的字段名。
  			通过resultMap将MyBaits查询出来的数据和实体类的字段名一一对应。
  		 -->
 		<id property="id" column="id"/>
 		<result property="stuName" column="stu_name"/>
 		<result property="stuAge" column="stu_age"/>
 		<result property="stuClassId" column="stu_class_id"/>
 		<!-- 
  			association表示将两个对象联合起来,使MyBatis能够知道谁是谁的对象,谁该怎样封装谁!!!
  			property:表示实体类(Student)下边的哪一个属性。
  			javaType:表示实体类(Student)下边的arch属性要封装com.xx.Bean.Archives类。
  		 -->
 		<association property="arch" javaType="com.xx.Bean.Archives">
 			<result property="id" column="aid"/>
	 		<result property="archName" column="arch_name"/>
	 		<result property="archNum" column="arch_num"/>
	 		<result property="stuId" column="stu_id"/>
 		</association>
 	</resultMap>
  </mapper>

执行顺序:

  • MyBatis首先执行select语句,从数据库中查询出如下结果:
    在这里插入图片描述
  • 然后MyBatis准备返回数据,默认是按照resultType返回;但是此处找到了resultMap自定义的规则,于是对照resultMap中的唯一标志getStudentForAssoc名去找对应的规则。
  • 找到对应resultMap规则后,按照规则提示一个对一个的封装!!!
  • 总结:自定义规则的时候,只需记住property代表要封装的实体类属性,column代表数据库中已经查出来的数据字段,我们要将数据字段封装进实体类属性当中!
  • sql.properties(数据库连接池)
sql_username=root//自己数据库链接名
sql_password=root//登录数据库用户密码
sql_driver=com.mysql.jdbc.Driver
sql_url=jdbc:mysql://localhost:3306/mybatistest//要使用的数据库
  • test.java(JUnit测试)
package com.xx.JUnit;
import static org.junit.Assert.*;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import com.xx.Bean.Student;
import com.xx.Dao.StudentDao;

public class test {

private String resource;
	
	private InputStream inputStream;
	
	private SqlSessionFactory sqlSessionFactory;
	
	private SqlSession session;
	
	private Student stu;

	@Before
	public void initMybatis() throws IOException {
		resource = "mybatis-config.xml";
		inputStream = Resources.getResourceAsStream(resource);
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}
	
	@Test
	public void getStudent() {
		session = sqlSessionFactory.openSession();
		StudentDao dao = session.getMapper(StudentDao.class);
		System.out.println(dao.getStudent(1));
		//关闭和数据库的链接
		session.close();
	}
}
//双击getStudent选中,右击->run As->JUnit Test运行。

运行结果:
在这里插入图片描述

2.association分步查询

使用场景同“集合查询”一样,不同的是,使用集合级联查询的方式书写sql语句比较复杂。分步查询符合个人所能想到的最简单的思维逻辑; 即:我先按照id查询出这个学生的详细来,然后在学生的详细信息中找到该学生的档案编号,再根据这个档案表好去查询学生的详细档案信息。
  • StudentDao.java(学生接口类)
//新加分步查询方法
public Student spiltGetStudent(int id);
  • ArchivesDao.java(档案接口类)
//按照id查询档案信息
//首先执行的是查询学生信息的mapper映射语句,然后想办法将查询出来的id传递到此处。
public Archives getArchivesByid(int id); 
  • ArchivesDao.xml(按照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.xx.Dao.ArchivesDao">
  	<!-- 查询档案,返回的是一个档案实体类 -->
  	<select id="getArchivesByid" resultType="com.xx.Bean.Archives">
  		select * from com_archives where id=#{id}
  	</select>	
  </mapper>
  • StudentDao.xml(按照id查询学生)
	<!-- 按照id查询出学生信息,然后执行自定义的resultMap规则 -->
 	<select id="spiltGetStudent" resultMap="spiltGetStudentForAssc">
 		select * from com_student where id=#{id}
 	</select>
 	<!--  -->
 	<resultMap type="com.xx.Bean.Student" id="spiltGetStudentForAssc">
 		<id property="id" column="id"/>
 		<result property="stuName" column="stu_name"/>
 		<result property="stuAge" column="stu_age"/>
 		<result property="stuClassId" column="stu_class_id"/>
 		<!-- 
			逻辑:我查询出学生的信息了,接下来应该告诉mybais怎样根据当前信息去查询档案数据库。
			select:指定一个查询sql的唯一标识(就是告诉MyBatis接下来去使用select指定的这个查询),其余的封装操作由MyBatis完成。
			column:要传入的条件(此处是id,和select指定的查询语句对应)。
			注意:select尽量对应全类名。
		 -->
 		<association 
 			property="arch"
 			select="com.xx.Dao.ArchivesDao.getArchivesByid"
 			column="id">
 		</association>
 	</resultMap>
  • JUnit
@Test
public void splitGetClassStudent() {
	//分步查询
	session = sqlSessionFactory.openSession();
	StudentDao dao = session.getMapper(StudentDao.class);
	System.out.println(dao.spiltGetStudent(3));
	session.close();
}

在这里插入图片描述

  • 查询结果如图片
  • 说明:cong图中的日志打印可以看到,数据库被请求查询了多次,最终得出了结果。
  • 问题:若我只是想根据学生id得出该学生的档案编号这一条数据,但使用分步查询每次都要请求多次数据库,十分浪费资源,此时就用到按需查询。
    在这里插入图片描述
    在这里插入图片描述

3.按需查询

在mybatis-config.xml中加入两条设置:
<!-- 
  		mapUnderscoreToCamelCase:是否开启自动驼峰命名规则(camel case)映射。
  		lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。
  		aggressiveLazyLoading:开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载
  	 -->
  	<settings>
  		<setting name="mapUnderscoreToCamelCase" value="true"/>
  		<setting name="lazyLoadingEnabled" value="true"/>			//加这里
  		<setting name="aggressiveLazyLoading" value="false"/>		//加这里
  	</settings>

再次执行如下测试时,输出结果发生改变
在这里插入图片描述
结果:
在这里插入图片描述
只请求了一次数据库便得出结果。
按需加载就是需要的时候去查询,延迟加载就是不着急加载(不着急查询)。

  • 补充:在assoication标签中有一个fetchType属性,该属性有两个属性:lazy和eager。前者为延迟加载,后者是立即加载。
  • 该属性中的eager可以用来关闭全局配置文件定义的延迟加载(只是关闭当前查询的延迟加载)。
  • 即当写fetchType=eager时,MyBatis对数据库的请求是多次请求,由延迟加载变为立即加载(立即请求数据库)。结果如下图二所示:
    在这里插入图片描述
    在这里插入图片描述

三、MyBatis级联查询(一对多)

1.集合查询

说明:班级类(Class.java)中有一个是学生类(Student.java)的列表属性,保存了这个班级中的所有学生,查询结束返回的是一个集合。
使用场景:查询某一个班级的具体信息并且返回该班级下的所有学生信息。

  • Class.java(实体类)
package com.xx.Bean;

import java.util.List;

public class Class {

	private int id;
	
	private String className;
	
	private String classNum;
	
	//列表属性,用来保存本班级下面的所有的学生。
	private List<Student> students;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getClassName() {
		return className;
	}

	public void setClassName(String className) {
		this.className = className;
	}

	public String getClassNum() {
		return classNum;
	}

	public void setClassNum(String classNum) {
		this.classNum = classNum;
	}

	//可以通过此方法来单独取得所有的学生的属性,不取班级属性。
	public List<Student> getStudents() {
		return students;
	}

	public void setStudents(List<Student> students) {
		this.students = students;
	}

	@Override
	public String toString() {
		return "Class [id=" + id + ", className=" + className + ", classNum=" + classNum + ", students=" + students
				+ "]";
	}

	public Class(int id, String className, String classNum, List<Student> students) {
		super();
		this.id = id;
		this.className = className;
		this.classNum = classNum;
		this.students = students;
	}

	public Class() {
		super();
		// TODO Auto-generated constructor stub
	}
	
}

  • ClassDao.java(接口)
    在ClassDao.java接口类中写入如下方法。
package com.xx.Dao;

import java.util.List;

public interface ClassDao {

	//传入班级的id,返回本班级的基本信息以及本班级下面的所有的学生的信息->collection
	public List<Class> getClassStudent(int id);
	//传入班级的id,返回本班级的基本信息以及本班级下面的所有的学生的信息->分步查询。
	public List<Class> splitGetClassStudent(int id);
	
}

  • ClasstDao.xml(映射配置文件)
    在ClassDao.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">
  <!-- namespace是指要处理哪一个接口类下的方法,所以必须要改为ClassDao -->
  <mapper namespace="com.xx.Dao.ClassDao">
  	<!-- 先去数据库中执行,成功执行之后再去写resultMap -->
  	<select id="getClassStudent" resultMap="classStudents">
 		select 
 		c.*,
 		stu.id sid,
 		stu.stu_name,
 		stu.stu_age,
 		stu.stu_class_id 
 		from 
 		com_class c 
 		left join 
 		com_student stu 
 		on 
 		c.id = stu.stu_class_id 
 		where 
 		c.id=#{id};
 	</select>
 	
 	<!-- type是对班级类定义的封装规则,该规则的唯一标识是classStudents -->
 	<resultMap type="com.xx.Bean.Class" id="classStudents">
 		<!-- 
 			collection:定义集合元素的封装
 				property:指定实体类的哪一个属性是集合属性,MyBatis会将查询出来的集合封装到这里。
 				javaType:指定对象的类型,和ofType区分。
 				ofType:指定集合里面元素的类型。
 		 -->
 		 <id property="id" column="id"/>
 		 <result property="className" column="class_name"/>
 		 <result property="classNum" column="class_num"/>
 		 <collection property="students" ofType="com.xx.Bean.Student">
 		 	<result property="id" column="sid"/>
 		 	<result property="stuName" column="stu_name"/>
 		 	<result property="stuAge" column="stu_age"/>
 		 	<result property="stuClassId" column="stu_class_id"/>
 		 </collection>
 	</resultMap>
  </mapper>
  • JUnit测试
@Test
public void getClassStudent() {
	session = sqlSessionFactory.openSession();
	StudentDao dao = session.getMapper(StudentDao.class);
	List<Class> list =  dao.getClassStudent(2);
	System.out.println(list);
	session.close();
}

打印结果如下:
在这里插入图片描述

2.collection分步查询

本例在association分步查询中已经开启过按需加载和延迟加载。

  • ClassDao.java
//此处出现过一个错误,当返回值是Class的时候,一直报错。即public Class 。。。
//原因估计是因为Class是一个关键字,下次起名不能这么随意。
public List<Class> splitGetClassStudent(int id);
  • StudentDao.java
//使用collection的分步查询
//根据传递过来的班级ID查询出该班级所有的学生信息
public List<Student> getStudentListByClassId(int id);
  • ClassDao.xml
	<select id="splitGetClassStudent" resultMap="splitGetClassStudentMap">
 		select * from com_class where id=#{id}
 	</select>
 	
 	<resultMap type="com.xx.Bean.Class" id="splitGetClassStudentMap">
 		<id property="id" column="id"/>
 		<result property="className" column="class_name"/>
 		<result property="classNum" column="class_num"/>
 		<collection 
 			property="students"
 			select="com.xx.Dao.StudentDao.getStudentListByClassId"
 			column="id">
 		</collection>
 	</resultMap>
  • StudentDao.xml
<!-- resultType是集合里面元素的类型 -->
<!-- 根据传递过来的班级ID查询出该班级所有的学生信息 -->
 <select id="getStudentListByClassId" resultType="com.xx.Bean.Student">
 	select * from com_student where stu_class_id=#{id}
 </select>
  • JUnit
@Test
public void getStudentListById() {
	//使用collection的分步查询方法
	session = sqlSessionFactory.openSession();
	ClassDao dao = session.getMapper(ClassDao.class);
	System.out.println("------------------------------------");
	System.out.println(dao.splitGetClassStudent(2));
	session.close();
}

打印结果:
在这里插入图片描述

四、MyBatis级联查询(多对多)

数据库方面加入一个中间表,实体类方面使用两个一对多查询就好。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐