MyBatis from scratch-MyBatis advanced query

Series blog directory: MyBatis blog directory from scratch

5. MyBatis advanced query

In relational databases, we often have to deal with one-to-one and one-to-many relationships. For example: a student can only be in one class, and a class can have many students.

data preparation

-- ----------------------------
-- 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 One-to-one mapping

Scenario: A student can only correspond to one class

5.1.1 Use automatic mapping to handle one-to-one relationships

Student information class: 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;
	}
}

Class information class: ClassInfo.java

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

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

Interface class: 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>

Unit test class 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]

​ Note: The classInfo object in the above query information is empty. Need to make the following transformation in the Mapper.xml file

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>

Unit test class 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将这条数据映射到了班级和学生类中。这种通过一次查询将结果映射到不同对象的方式,称之为关联的嵌套结果映射。

​ The associated nested result mapping requires multiple tables to be associated, and all the required values ​​are queried at once. The advantage of this method is to reduce the number of database queries and reduce the pressure on the database. The disadvantage is to write complex SQL.

5.1.2 Use resultMap to configure one-to-one mapping

​ In addition to using MyBatis's automatic mapping to handle one-to-one nesting, you can also configure the result mapping in the XML mapping file. Configuration using resultMap can also achieve the effect in 5.1.1, the code is as follows:

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>

Unit test class 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 One-to-many mapping

​ The label collection of one-to-many relationship mapping is similar to association. The combined nested result mapping refers to querying all the results through one SQL query, and then mapping the data to different objects through the configured result mapping.

One-to-many scenario, a class contains multiple students:

ClassInfo.java adds 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]]]

[External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (img-zPA06dON-1614513937815)(C:\Users\xiangty\Desktop\s8.jpg)]

If there are any problems in the document, you can contact me directly so that I can correct and make progress. Hope the documentation is helpful to you. GitHub address of the code in the document: https://gitee.com/xiangty1/learn-MyBatis/

Guess you like

Origin blog.csdn.net/qq_33369215/article/details/114239028