MyBatis从零开始-MyBatis高级查询

系列博客目录:MyBatis从零开始博客目录

5. MyBatis高级查询

在关系型数据库中,我们经常要处理一对一、一对多的关系。例如:一个学生只能在一个班级,一个班级可以存在很多学生。

数据准备

-- ----------------------------
-- Table structure for `t_class_info`
-- ----------------------------
DROP TABLE IF EXISTS `t_class_info`;
CREATE TABLE `t_class_info` (
  `class_id` int(11) NOT NULL AUTO_INCREMENT,
  `class_name` varchar(255) DEFAULT NULL COMMENT '班级名称',
  PRIMARY KEY (`class_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_class_info
-- ----------------------------
INSERT INTO `t_class_info` VALUES ('1', '一年级');
INSERT INTO `t_class_info` VALUES ('2', '二年级');
INSERT INTO `t_class_info` VALUES ('3', '三年级');
INSERT INTO `t_class_info` VALUES ('4', '四年级');

-- ----------------------------
-- Table structure for `t_student_info`
-- ----------------------------
DROP TABLE IF EXISTS `t_student_info`;
CREATE TABLE `t_student_info` (
  `student_id` int(11) NOT NULL AUTO_INCREMENT,
  `student_name` varchar(255) DEFAULT NULL,
  `class_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`student_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_student_info
-- ----------------------------
INSERT INTO `t_student_info` VALUES ('1', '张三', '1');
INSERT INTO `t_student_info` VALUES ('2', '李四', '2');
INSERT INTO `t_student_info` VALUES ('3', '路人甲', '3');
INSERT INTO `t_student_info` VALUES ('4', '路人乙', '1');

5.1 一对一映射

场景:一个学生只能对应一个班级

5.1.1 使用自动映射处理一对一关系

学生信息类:StudentInfo.java

public class StudentInfo {
    
    
	private Integer studentId;
	private String studentName;
    /**
	 * 所在班级
	 */
	private ClassInfo classInfo;

	// 此处省略其它get、set方法
    // 此处省略无参的构造方法
	// 此处toString()方法
    
    public ClassInfo getClassInfo() {
    
    
		return classInfo;
	}

	public void setClassInfo(ClassInfo classInfo) {
    
    
		this.classInfo = classInfo;
	}
}

班级信息类:ClassInfo.java

public class ClassInfo {
    
    
	private Integer classId;
	private String className;

	// 此处省略其它get、set方法
    // 此处省略无参的构造方法
	// 此处toString()方法
}

接口类:StudentInfoMapper.java

import org.apache.ibatis.annotations.Param;
import com.xiangty.bean.StudentInfo;

public interface StudentInfoMapper {
    
    
	/**
	 * 根据学生id查询学生信息和班级信息
	 * @param studentId 学生id
	 * @return
	 */
	StudentInfo selectStudentAndClassById(@Param("studentId") Integer studentId);
}

StudentInfoMapper.xml

<select id="selectStudentAndClassById" resultType="com.xiangty.bean.StudentInfo">
    select s.student_id, 
            s.student_name, 
            s.class_id, 
            c.class_name 
    from t_student_info s 
    left join t_class_info c
    on c.class_id = s.class_id
    where s.student_id = #{studentId}
</select>

单元测试类StudentInfoTest.java

import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import com.xiangty.bean.StudentInfo;
import com.xiangty.mapper.StudentInfoMapper;

public class StudentInfoTest {
    
    
	@Test
	public void testSelectStudentAndClassById(){
    
    
		SqlSession sqlSession = SqlSessionUtil.getSqlSession();
		try {
    
    
			StudentInfoMapper studentInfoMapper = sqlSession.getMapper(StudentInfoMapper.class);
			Integer studentId = 1;
			StudentInfo studentInfo = studentInfoMapper.selectStudentAndClassById(studentId);
			System.out.println(studentInfo);
		} finally {
    
    
			sqlSession.close();
		}
	}
}


输出结果:
DEBUG [main] - ==>  Preparing: select s.student_id, s.student_name, s.class_id, c.class_name from t_student_info s left join t_class_info c on c.class_id = s.class_id where s.student_id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
TRACE [main] - <==    Columns: student_id, student_name, class_id, class_name
TRACE [main] - <==        Row: 1, 张三, 1, 一年级
DEBUG [main] - <==      Total: 1
StudentInfo [studentId=1, studentName=张三, classInfo=null]

​ 注意:上面的查询的信息中classInfo对象为空。需要在Mapper.xml文件中做如下改造

StudentInfoMapper.xml

<!--  
		c.class_id列后面的"classInfo.classId",这种是复杂的属性映射,可以多层嵌套。
		MyBatis会先查找classInfo属性,如果存在这个属性就创建classInfo对象,然后继续查找classId属性,将c.class_id的值绑定到这个属性上面。
	-->
<select id="selectStudentAndClassById" resultType="com.xiangty.bean.StudentInfo">
    select s.student_id, 
    s.student_name, 
    c.class_id "classInfo.classId", 
    c.class_name "classInfo.className" 
    from t_student_info s 
    left join t_class_info c
    on c.class_id = s.class_id
    where s.student_id = #{studentId}
</select>

单元测试类StudentInfoTest.java

import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import com.xiangty.bean.StudentInfo;
import com.xiangty.mapper.StudentInfoMapper;

public class StudentInfoTest {
    
    
	@Test
	public void testSelectStudentAndClassById(){
    
    
		SqlSession sqlSession = SqlSessionUtil.getSqlSession();
		try {
    
    
			StudentInfoMapper studentInfoMapper = sqlSession.getMapper(StudentInfoMapper.class);
			Integer studentId = 1;
			StudentInfo studentInfo = studentInfoMapper.selectStudentAndClassById(studentId);
			System.out.println(studentInfo);
		} finally {
    
    
			sqlSession.close();
		}
	}
}


输出结果:
DEBUG [main] - ==>  Preparing: select s.student_id, s.student_name, c.class_id "classInfo.classId", c.class_name "classInfo.className" from t_student_info s left join t_class_info c on c.class_id = s.class_id where s.student_id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
TRACE [main] - <==    Columns: student_id, student_name, classInfo.classId, classInfo.className
TRACE [main] - <==        Row: 1, 张三, 1, 一年级
DEBUG [main] - <==      Total: 1
StudentInfo [studentId=1, studentName=张三, classInfo=ClassInfo [classId=1, className=一年级]]
	通过控制台答应的日志看到查询出一条数据,MyBatis将这条数据映射到了班级和学生类中。这种通过一次查询将结果映射到不同对象的方式,称之为关联的嵌套结果映射。

​ 关联的嵌套结果映射需要关联多个表,将所有需要的值一次性查询出来。这种方式的好处是减少数据库查询次数,减轻数据库的压力,缺点是要写复杂SQL。

5.1.2 使用resultMap配置一对一映射

​ 除了使用MyBatis的自动映射来处理一对一嵌套外,还可以在XML映射文件中配置结果映射。使用resultMap进行配置也可以达到5.1.1中的效果,代码如下:

StudentInfoMapper.xml

<!-- 
  association标签用于和一个负载的类型进行关联,即用于一对一的关联配置。
  学生和班级是一对一的,所以在学生的resultMap里面加上association标签配置的班级信息即可。
  property属性表示的是实体类中的属性名称;
  column属性表示的是对应的数据库中字段的名称。
  -->
<resultMap type="com.xiangty.bean.StudentInfo" id="studentMap">
    <id property="studentId" column="student_id"/>
    <result property="studentName" column="student_name"/>
    <association property="classInfo" javaType="com.xiangty.bean.ClassInfo">
        <result property="classId" column="class_id"/>
        <result property="className" column="class_name"/> 
    </association>
</resultMap>
<!-- 注意这个方法使用resultMap配置映射,所以放回值不能用resultType来设置,而是需要使用resultMap属性将其配置为上面的studentMap -->
<select id="selectStudentAndClassById2" resultMap="studentMap">
    select s.student_id, 
    s.student_name, 
    s.class_id, 
    c.class_name
    from t_student_info s 
    left join t_class_info c
    on c.class_id = s.class_id
    where s.student_id = #{studentId}
</select>

单元测试类StudentInfoTest.java

import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import com.xiangty.bean.StudentInfo;
import com.xiangty.mapper.StudentInfoMapper;

public class StudentInfoTest {
    
    
	@Test
	public void testSelectStudentAndClassById2(){
    
    
		SqlSession sqlSession = SqlSessionUtil.getSqlSession();
		try {
    
    
			StudentInfoMapper studentInfoMapper = sqlSession.getMapper(StudentInfoMapper.class);
			Integer studentId = 1;
			StudentInfo studentInfo = studentInfoMapper.selectStudentAndClassById2(studentId);
			System.out.println(studentInfo);
		} finally {
    
    
			sqlSession.close();
		}
	}
}


输出结果:
DEBUG [main] - ==>  Preparing: select s.student_id, s.student_name, s.class_id, c.class_name from t_student_info s left join t_class_info c on c.class_id = s.class_id where s.student_id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
TRACE [main] - <==    Columns: student_id, student_name, class_id, class_name
TRACE [main] - <==        Row: 1, 张三, 1, 一年级
DEBUG [main] - <==      Total: 1
StudentInfo [studentId=1, studentName=张三, classInfo=ClassInfo [classId=1, className=一年级]]

5.2 一对多映射

​ 一对多关系映射的标签collection和association类似,结合的嵌套结果映射就是指通过一次SQL查询将所有的结果查询出来,然后通过配置的结果映射,将数据映射到不同的对象中去。

一对多的场景,一个班级包含多个学生:

ClassInfo.java 新增学生集合studentInfoList

package com.xiangty.bean;
import java.util.List;
/**
 * 班级信息
 * @author xiangty
 */
public class ClassInfo {
    
    
	private Integer classId;
	private String className;
	
	// 学生集合
	List<StudentInfo> studentInfoList;
	
	// 此处省略其它get、set方法
    // 此处省略无参的构造方法
	// 此处toString()方法
}

ClassInfoMapper.java

package com.xiangty.mapper;

import org.apache.ibatis.annotations.Param;

import com.xiangty.bean.ClassInfo;
public interface ClassInfoMapper {
    
    
	
	/**
	 * 根据班级id查询班级和学生信息
	 * @param classId 班级id
	 * @return
	 */
	ClassInfo selectClassInfoAndStudentById(@Param("classId") Integer classId);
	 
}

ClassInfoMapper.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.xiangty.mapper.ClassInfoMapper">
	<!-- 
		collection标签用于和一个负载的类型进行关联,即用于一对多的关联配置。
		班级和学生是一对多的,所以在班级的resultMap里面加上collection标签配置的班级信息即可。
		property属性表示的是实体类中的属性名称;
		column属性表示的是对应的数据库中字段的名称。
	 -->
	<resultMap type="com.xiangty.bean.ClassInfo" id="classMap">
		<id property="classId" column="class_id"/>
		<result property="className" column="class_name"/>
		<collection property="studentInfoList" ofType="com.xiangty.bean.StudentInfo">
			<result property="studentId" column="student_id"/>
			<result property="studentName" column="student_name"/>
		</collection>
	</resultMap>
	<!-- 注意这个方法使用resultMap配置映射,所以放回值不能用resultType来设置,而是需要使用resultMap属性将其配置为上面的studentMap -->
	<select id="selectClassInfoAndStudentById" resultMap="classMap">
		select s.student_id, 
			 s.student_name, 
			 s.class_id, 
			 c.class_name
		from t_class_info c
		left join t_student_info s 
		on s.class_id = c.class_id 
		where c.class_id = #{classId}
	</select>
	
</mapper>

ClassInfoTest.java

package com.xiangty.test;

import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import com.xiangty.bean.ClassInfo;
import com.xiangty.mapper.ClassInfoMapper;

public class ClassInfoTest {
    
    
	
	@Test
	public void selectClassInfoAndStudentById(){
    
    
		SqlSession sqlSession = SqlSessionUtil.getSqlSession();
		try {
    
    
			ClassInfoMapper classInfoMapper = sqlSession.getMapper(ClassInfoMapper.class);
			Integer classId = 1;
			ClassInfo classInfo = classInfoMapper.selectClassInfoAndStudentById(classId);
			System.out.println(classInfo);
		} finally {
    
    
			sqlSession.close();
		}
	}
	
}


运行效果:
DEBUG [main] - ==>  Preparing: select s.student_id, s.student_name, s.class_id, c.class_name from t_class_info c left join t_student_info s on s.class_id = c.class_id where c.class_id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
TRACE [main] - <==    Columns: student_id, student_name, class_id, class_name
TRACE [main] - <==        Row: 1, 张三, 1, 一年级
TRACE [main] - <==        Row: 4, 路人乙, 1, 一年级
DEBUG [main] - <==      Total: 2
ClassInfo [classId=1, className=一年级, studentInfoList=[StudentInfo [studentId=1, studentName=张三, classInfo=null], StudentInfo [studentId=4, studentName=路人乙, classInfo=null]]]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zPA06dON-1614513937815)(C:\Users\xiangty\Desktop\s8.jpg)]

如果文档中有任何问题,可以直接联系我,便于我改正和进步。希望文档对您有所帮助。文档中代码GitHub地址:https://gitee.com/xiangty1/learn-MyBatis/

猜你喜欢

转载自blog.csdn.net/qq_33369215/article/details/114239028