2022尚硅谷SSM框架跟学二MyBatis基础二
- 6.MyBatis的各种查询功能
- 6.1查询一个实体类对象
- 6.2查询一个list集合
- 6.3查询单个数据
- MyBatis别名对应文档
- 6.4查询一条数据为map集合
- 6.5查询多条数据为map集合
- (1)方式一
- (2)方式二
- 7.特殊SQL的执行
- 7.1模糊查询
- 7.2批量删除
- 7.3动态设置表名
- 7.4添加功能获取自增的主键
- 8自定义映射resultMap
- 8.1resultMap处理字段和属性的映射关系
- 方法一:字段名起别名,别名是属性名
- 方法二:设置一个全局配置信息
- 方法三:用自定义映射resultMap实现
- 总结:
- 8.2多对一映射处理
- 8.2.1、级联方式处理映射关系
- 8.2.2使用association处理映射关系
- 8.2.3分步查询
- (1)查询员工信息
- (2)根据员工所对应的部门id查询部门信息
- (3)分布查询的优势
- 8.3一对多映射处理
- 8.3.1collection
- 8.3.2分步查询
- (1)查询部门信息
- (2)根据部门id查询部门中的所有员工
6.MyBatis的各种查询功能
6.1查询一个实体类对象
按照功能创建一个接口SelectMapper
配置Mapper的配置文件
创建测试类
在SelectMapper.java中添加方法
/**
* @param
* @return com.atguigu.mybatis.pojo.User
* @description //根据id获取User对象
* @param: id
* @date 2023/1/3 19:10
* @author wty
**/
User getUserById(@Param("id") Integer id);
在SelectMapper.xml中添加语句
<!-- User getUserById(Integer id); -->
<select id="getUserById" resultType="User">
select *
from t_user
where id = #{id}
</select>
在SelectMapperTest.java测试类中添加方法
@Test
public void getUserByIdTest() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
User user = mapper.getUserById(6);
System.out.println(user);
sqlSession.close();
}
查询结果
原本要查询一行多列的结果集,但是sql写错了查询成了多行多列,这样会出现怎么样的结果呢,一起看下
报错
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 6
提醒我们了,Mapper接口的返回值如果是User,那么查询结果是一行多列,sql不能写错。
6.2查询一个list集合
当查询的数据为多条时,不能使用实体类作为返回值,否则会抛出异常TooManyResultsException;
但是若查询的数据只有一条,可以使用实体类或集合作为返回值
在SelectMapper.java中添加方法
/**
* @param
* @return java.util.List<com.atguigu.mybatis.pojo.User>
* @description //查询所有用户信息
* @date 2023/1/3 19:15
* @author wty
**/
List<User> getAllUser();
在SelectMapper.xml中添加语句
<!-- List<User> getAllUser(); -->
<select id="getAllUser" resultType="User">
select *
from t_user
</select>
在SelectMapperTest.java测试类中添加方法
@Test
public void checkLoginByParamTest() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.checkLoginByParam("Linda", "234");
System.out.println(user);
sqlSession.close();
}
查询结果
如果把SelectMapper.xml中的查询语句更改成查询一行多列,放到这里可以吗,我们一起来看一下。
发现查询结果没有问题
6.3查询单个数据
单行单列数据,比如数量、平均数,或者某个具体条件查询的某个字段值
在SelectMapper.java中添加方法
/**
* @param
* @return java.lang.Integer
* @description //获取用户的总数量
* @date 2023/1/3 19:42
* @author wty
**/
Integer getCount();
在SelectMapper.xml中添加语句
<!-- Integer getCount(); -->
<select id="getCount" resultType="java.lang.Integer">
select count(*)
from t_user
</select>
在SelectMapperTest.java测试类中添加方法
@Test
public void getCount() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
Integer count = mapper.getCount();
System.out.println("查询的记录数:" + count);
sqlSession.close();
}
查询结果
除了以上写法外,还有另一种写法resultType=“int”
在SelectMapper.xml中添加语句
<!-- Integer getCount(); -->
<select id="getCount" resultType="int">
select count(*)
from t_user
</select>
依然可以执行成功
除了以上写法外,还有另一种写法resultType=“Integer”
在SelectMapper.xml中添加语句
<!-- Integer getCount(); -->
<select id="getCount" resultType="Integer">
select count(*)
from t_user
</select>
也可以成功
不区分大小写integer
<!-- Integer getCount(); -->
<select id="getCount" resultType="integer">
select count(*)
from t_user
</select>
发现也可以成功
INT大写
<!-- Integer getCount(); -->
<select id="getCount" resultType="INT">
select count(*)
from t_user
</select>
依旧可以成功
得出结论:
1.resultType后面的内容不区分大小写
2.resultType提前设置好了别名
MyBatis别名对应文档
MyBatis提前设置好的别名在官方文档中查询如下:
Alias别名 | Mapped Type(Mapper接口返回类型) |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
6.4查询一条数据为map集合
查询的时候,不是字段名就对应实体类,而是起了别名,那么就需要map集合了。
在SelectMapper.java中添加方法
/**
* @param
* @return java.util.Map<java.lang.String, java.lang.Object>
* @description //通过id查询User信息转换成别名
* @param: id
* @date 2023/1/3 22:48
* @author wty
**/
Map<String, Object> getUserByidToMap(@Param("id") Integer id);
在SelectMapper.xml中添加语句
<!-- Map<String, Object> getUserByidToMap(@Param("id") Integer id); -->
<select id="getUserByidToMap" resultType="map">
select *
from t_user
where id = #{id}
</select>
在SelectMapperTest.java测试类中添加方法
@Test
public void getUserByidToMapTest() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
Map<String, Object> map = mapper.getUserByidToMap(6);
// {password=456, gender=男, id=6, age=24, email=tony@126.com, username=tony}
System.out.println(map);
sqlSession.close();
}
查询结果
注意如果有字段为null那么map集合是不会放进去值的。
例如:
Lisa的年龄和邮箱为null
查询map结果:
6.5查询多条数据为map集合
(1)方式一
将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,此时可以将这些map放在一个list集合中获取。
在SelectMapper.java中添加方法
/**
* @param
* @return java.util.Map<java.lang.String, java.lang.Object>
* @description //查询所有用户信息
* @date 2023/1/3 23:02
* @author wty
**/
Map<String, Object> getAllUserToMap();
在SelectMapper.xml中添加语句
<!-- Map<String, Object> getAllUserToMap(); -->
<select id="getAllUserToMap" resultType="map">
select *
from t_user
</select>
在SelectMapperTest.java测试类中添加方法
@Test
public void getAllUserToMap() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
Map<String, Object> map = mapper.getAllUserToMap();
System.out.println(map);
sqlSession.close();
}
执行后发现报错
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 7
因为mapper接口中规定的返回值是一个Map<String,Object>集合,但是实际返回值是多个Map集合。
解决方案,用List集合嵌套Map集合
更改SelectMapper.java中的方法
/**
* @param
* @return java.util.Map<java.lang.String, java.lang.Object>
* @description //查询所有用户信息
* @date 2023/1/3 23:02
* @author wty
**/
List<Map<String, Object>> getAllUserToMap();
修改SelectMapperTest.java测试类
@Test
public void getAllUserToMap() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
List<Map<String, Object>> list = mapper.getAllUserToMap();
System.out.println(list);
sqlSession.close();
}
而SelectMapper.xml无需修改
查询结果
(2)方式二
将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,并且最终要以一个map的方式返回数据,此时需要通过@MapKey注解设置map集合的键,值是每条数据所对应的map集合。
在SelectMapper.java中修改方法
/**
* @param
* @return java.util.Map<java.lang.String, java.lang.Object>
* @description //查询所有用户信息
* @date 2023/1/3 23:02
* @author wty
**/
//List<Map<String, Object>> getAllUserToMap();
@MapKey("id")
Map<String, Object> getAllUserToMap();
在SelectMapperTest.java测试类中修改方法
@Test
public void getAllUserToMap() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
//List<Map<String, Object>> list = mapper.getAllUserToMap();
Map<String, Object> map = mapper.getAllUserToMap();
//list.forEach(System.out::println);
Set<Map.Entry<String, Object>> entries = map.entrySet();
for (Map.Entry<String, Object> entry : entries) {
System.out.println(entry);
}
/**
* 1={password=123, gender=男, id=1, age=23, email=hsp@126.com, username=hsp}
* 2={password=123456, gender=男, id=2, age=23, email=hsp@126.com, username=admin}
* 3={password=123456, gender=男, id=3, age=23, email=hsp@126.com, username=admin}
* 4={password=123456, gender=男, id=4, age=23, email=hsp@126.com, username=admin}
* 5={password=111, gender=男, id=5, username=Lisa}
* 6={password=456, gender=男, id=6, age=24, email=tony@126.com, username=tony}
* 7={password=234, gender=女, id=7, age=21, email=Linda@126.com, username=Linda}
*/
sqlSession.close();
}
查询结果:
7.特殊SQL的执行
防止出现sql注入,一般都使用#{}占位符的方式,但是如下几个特殊的sql一般使用${}的形式。
新建Mapper接口 SpecialSqlMapper
新建配置文件SpecialSqlMapper.xml
配置namespace
新建测试类SpecialSqlMapperTest.java
7.1模糊查询
在SpecialSqlMapper.java中添加方法
/**
* @param
* @return java.util.List<com.atguigu.mybatis.pojo.User>
* @description //根据mh字段(用户名)查询用户信息
* @param: mh
* @date 2023/1/3 23:41
* @author wty
**/
List<User> getUserByLike(@Param("mh") String mh);
在SpecialSqlMapper.xml中添加sql语句
<!-- List<User> getUserByLike(@Param("mh") String mh); -->
<select id="getUserByLike" resultType="User">
select *
from t_user
where username like '%#{mh}%'
</select>
在SpecialSqlMapperTest.java添加测试方法
@Test
public void getUserByLikeTest() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
SpecialSqlMapper mapper = sqlSession.getMapper(SpecialSqlMapper.class);
List<User> list = mapper.getUserByLike("a");
list.forEach(System.out::println);
sqlSession.close();
}
发现执行结果:报错
DEBUG 01-03 23:45:52,147 ==> Preparing: select * from t_user where username like '%?%' (BaseJdbcLogger.java:137)
Caused by: java.sql.SQLException: Parameter index out of range (1 > number of parameters, which is 0).
还原一下报错原因
public void testJdbc() {
try {
Class.forName("");
Connection connection = DriverManager.getConnection("", "", "");
String sql = "select * from t_user where username like '%?%'";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "a");
} catch (Exception e) {
e.printStackTrace();
}
}
可以看到错误原因是单引号中的?会看成问号,而不是占位符,而实际有a作为占位符的变量,这样就导致了a超出了占位符的个数。
解决方案1
在SpecialSqlMapper.xml中更改为${}
<!-- List<User> getUserByLike(@Param("mh") String mh); -->
<select id="getUserByLike" resultType="User">
select *
from t_user
where username like '%${mh}%'
</select>
查询结果
解决方案2
用mysql提供的concat函数拼接
<!-- List<User> getUserByLike(@Param("mh") String mh); -->
<select id="getUserByLike" resultType="User">
select *
from t_user
where username like concat('%', #{mh}, '%')
</select>
查询正确
解决方案3
双引号方式"%“#{}”%"
<!-- List<User> getUserByLike(@Param("mh") String mh); -->
<select id="getUserByLike" resultType="User">
select *
from t_user
where username like "%"#{mh}"%"
</select>
执行结果正确
7.2批量删除
在SpecialSqlMapper.java中添加方法
/**
* @param
* @return int
* @description //根据多个id删除多条记录
* @param: ids
* @date 2023/1/4 16:29
* @author wty
**/
int deleteMoreUser(@Param("ids") String ids);
在SpecialSqlMapper.xml中添加sql语句
<!-- int deleteMoreUser(@Param("ids") String ids); -->
<delete id="deleteMoreUser">
delete
from t_user
where id in (#{ids})
</delete>
在SpecialSqlMapperTest.java添加测试方法
@Test
public void deleteMoreUser() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
SpecialSqlMapper mapper = sqlSession.getMapper(SpecialSqlMapper.class);
int i = mapper.deleteMoreUser("8,9,10,11");
System.out.println("删除了:" + i + "条记录");
sqlSession.close();
}
发现报错
这里必须要用${}的形式,那么需要修改
修改SpecialSqlMapper.xml中的sql语句
<!-- int deleteMoreUser(@Param("ids") String ids); -->
<delete id="deleteMoreUser">
delete
from t_user
where id in (${ids})
</delete>
查询结果
7.3动态设置表名
在SpecialSqlMapper.java中添加方法
/**
* @param
* @return java.util.List<com.atguigu.mybatis.pojo.User>
* @description //动态设置表名查询用户的信息
* @param: tableName
* @date 2023/1/4 16:46
* @author wty
**/
List<User> getUserList(@Param("tableName") String tableName);
在SpecialSqlMapper.xml中添加sql语句
<!-- List<User> getUserList(@Param("tableName") String tableName);-->
<select id="getUserList" resultType="User">
select *
from #{tableName}
</select>
在SpecialSqlMapperTest.java添加测试方法
@Test
public void getUserList() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
SpecialSqlMapper mapper = sqlSession.getMapper(SpecialSqlMapper.class);
List<User> list = mapper.getUserList("t_user");
list.forEach(System.out::println);
sqlSession.close();
}
发现报错
表名两侧有单引号,是不对的,想到了用${}来写
修改SpecialSqlMapper.xml中的ql语句
<select id="getUserList" resultType="User">
select *
from ${tableName}
</select>
查询结果
7.4添加功能获取自增的主键
使用场景:
场景模拟:
t_clazz(clazz_id,clazz_name)
t_student(student_id,student_name,clazz_id)
1、添加班级信息
2、获取新添加的班级的id
3、为班级分配学生,即将某学的班级id修改为新添加的班级的id
获取自增主键不是MyBatis特有的,其底层是JDBC的,可以看下示例代码
public void getautoincreamidTest() {
try {
Class.forName("");
Connection connection = DriverManager.getConnection("", "", "");
String sql = "insert into t_user values(?,?)";
// 这里需要配置第2个参数,可以直接写1
PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
preparedStatement.setString(1, "a");
preparedStatement.setString(2, "b");
preparedStatement.executeUpdate();
// resultSet是单行单列的
ResultSet resultSet = preparedStatement.getGeneratedKeys();
resultSet.next();
// 自动递增的主键,因为是单行单列,所以获取的时候是getInt(1)
int id = resultSet.getInt(1);
} catch (Exception e) {
e.printStackTrace();
}
}
在SpecialSqlMapper.java中添加方法
/**
* @param
* @return int
* @description //添加一条用户信息,并且获取自增主键
* @param: user
* @date 2023/1/4 17:16
* @author wty
**/
int insertUser(User user);
在SpecialSqlMapper.xml中添加sql语句
useGeneratedKeys:设置使用自增的主键。
keyProperty:因为增删改有统一的返回值是受影响的行数,因此只能将获取的自增的主键放在传输的参数user对象的某个属性中。
<!-- int insertUser(User user); -->
<!-- useGeneratedKeys是否显示自增主键 keyProperty是用于返回自增主键的字段 -->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into t_user
values (null, #{username}, #{password}, #{age}, #{gender}, #{email})
</insert>
在SpecialSqlMapperTest.java添加测试方法
@Test
public void insertUser() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
SpecialSqlMapper mapper = sqlSession.getMapper(SpecialSqlMapper.class);
User user = new User(null, "Lucy", "123", 21, "女", "Lucy@qq.com");
System.out.println("插入前:" + user);
int i = mapper.insertUser(user);
System.out.println("插入成功:" + i + "条数据");
System.out.println("插入后:" + user);
sqlSession.close();
}
运行测试类,结果如下:
获取到id是自增主键
8自定义映射resultMap
8.1resultMap处理字段和属性的映射关系
若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射
创建新的Module
点击下一步
Name:mybatis-resultMap
GroupId:com.atguigu.mybatis
拷贝上一个pom.xml的内容
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>SSM</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu.mybatis</groupId>
<artifactId>mybatis-resultMap</artifactId>
<!-- 打包方式 -->
<packaging>jar</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.20</version>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</project>
拷贝上一个工程的jdbc.properties和log4j.xml
用模板创建mybatis-config.xml
从上一个工程拷贝工具类SqlSessionUtil.java
创建pojo和mapper包,并在核心配置文件mybatis-config.xml中配置包名
创建mapper的配置文件的文件夹
在SQL YOG中新创建2张表
创建t_emp表
CREATE TABLE `t_emp` (
`emp_id` INT(11) NOT NULL AUTO_INCREMENT,
`emp_name` VARCHAR(20) DEFAULT NULL,
`age` INT(11) DEFAULT NULL,
`gender` CHAR(1) DEFAULT NULL,
`dept_id` INT(11) DEFAULT NULL,
PRIMARY KEY (`emp_id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
创建t_dept表
CREATE TABLE `t_dept` (
`dept_id` INT(11) NOT NULL AUTO_INCREMENT,
`dept_name` VARCHAR(20) DEFAULT NULL,
PRIMARY KEY (`dept_id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
添加数据
写pojo实体类 Emp.java
package com.atguigu.mybatis.pojo;
/**
* @ClassName: Emp
* @Description:
* @Author: wty
* @Date: 2023/1/4
*/
public class Emp {
private Integer empId;
private String empName;
private Integer age;
private String gender;
private Integer deptId;
public Emp() {
}
public Emp(Integer empId, String empName, Integer age, String gender, Integer deptId) {
this.empId = empId;
this.empName = empName;
this.age = age;
this.gender = gender;
this.deptId = deptId;
}
public Integer getEmpId() {
return empId;
}
public void setEmpId(Integer empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
@Override
public String toString() {
return "Emp{" +
"empId=" + empId +
", empName='" + empName + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", deptId=" + deptId +
'}';
}
}
写pojo实体类 Dept.java
package com.atguigu.mybatis.pojo;
/**
* @ClassName: Dept
* @Description:
* @Author: wty
* @Date: 2023/1/4
*/
public class Dept {
private Integer deptId;
private String deptName;
public Dept() {
}
public Dept(Integer deptId, String deptName) {
this.deptId = deptId;
this.deptName = deptName;
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
@Override
public String toString() {
return "Dept{" +
"deptId=" + deptId +
", deptName='" + deptName + '\'' +
'}';
}
}
创建mapper接口
用模板创建mapper的核心配置文件,并且配置接口的全类名
在EmpMapper.java中添加方法
/**
* @param
* @return com.atguigu.mybatis.pojo.Emp
* @description //根据员工ID查询员工信息
* @param: empId
* @date 2023/1/4 21:19
* @author wty
**/
Emp getEmpByEmpId(@Param("empId") Integer empId);
在EmpMapper.xml中添加sql
<!-- Emp getEmpByEmpId(@Param("empId") Integer empId);-->
<select id="getEmpByEmpId" resultType="Emp">
select *
from t_emp
where emp_id = #{empId}
</select>
创建测试类 com.atguigu.mybatis.test.ResultMapTest
测试类ResultMapTest.java添加方法
@Test
public void getEmpByEmpId() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp id = mapper.getEmpByEmpId(3);
System.out.println("id = " + id);
sqlSession.close();
}
查询结果
查询不出来的话,需要对应字段名和实体类的属性名
修改EmpMapper.xml
方法一:字段名起别名,别名是属性名
<select id="getEmpByEmpId" resultType="Emp">
select emp_id empId, emp_name empName, age, gender, dept_id deptId
from t_emp
where emp_id = #{empId}
</select>
查询结果
方法二:设置一个全局配置信息
找到MyBatis的官方pdf
拷贝pdf的方法
mybatis-config.xml中添加
<!-- mapUnderscoreToCamelCase将下划线映射为驼峰 -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
EmpMapper.xml中保持不变,最初的状态
<select id="getEmpByEmpId" resultType="Emp">
select *
from t_emp
where emp_id = #{empId}
</select>
直接跑测试类,查看结果
方法三:用自定义映射resultMap实现
更改EmpMapper.xml文件,选择使用resultMap的方式
resultMap:设置自定义映射
属性:
id:表示自定义映射的唯一标识
type:查询的数据要映射的实体类的类型
子标签:
id:设置主键的映射关系
result:设置普通字段的映射关系
association:设置多对一的映射关系
collection:设置一对多的映射关系
属性:
property:设置映射关系中实体类中的属性名
column:设置映射关系中表中的字段名
<!-- 方式3:使用resultMap
resultMap使用resultMap标签的信息
-->
<resultMap id="empResultMap" type="Emp">
<!-- id 设置数据库主键和属性名的映射关系 -->
<id column="emp_id" property="empId"></id>
<!-- result 设置数据库非主键字段和属性名的映射关系 -->
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
<result column="dept_id" property="deptId"></result>
</resultMap>
<select id="getEmpByEmpId" resultMap="empResultMap">
select *
from t_emp
where emp_id = #{empId}
</select>
执行测试类,查看结果
总结:
若字段名和实体类中的属性名不一致,但是字段名符合数据库的规则(使用_),实体类中的属性名符合Java的规则(使用驼峰)。
此时也可通过以下两种方式处理字段名和实体类中的属性的映射关系
a>.可以通过为字段起别名的方式,保证和实体类中的属性名保持一致
b>.可以在MyBatis的核心配置文件中设置一个全局配置信息mapUnderscoreToCamelCase,可以在查询表中数据时,自动将_类型的字段名转换为驼峰例如:字段名user_name,设置了mapUnderscoreToCamelCase,此时字段名就会转换为userName
8.2多对一映射处理
场景模拟:
查询员工信息以及员工所对应的部门信息
针对Emp实体类进行修改,分析deptId字段,一个员工只有一个部门是1对1,那么是用对象返回。
package com.atguigu.mybatis.pojo;
/**
* @ClassName: Emp
* @Description:
* @Author: wty
* @Date: 2023/1/4
*/
public class Emp {
private Integer empId;
private String empName;
private Integer age;
private String gender;
/**
* 一个员工对应一个部门,所以是1:1
*/
private Dept dept;
public Emp() {
}
public Emp(Integer empId, String empName, Integer age, String gender, Dept dept) {
this.empId = empId;
this.empName = empName;
this.age = age;
this.gender = gender;
this.dept = dept;
}
public Integer getEmpId() {
return empId;
}
public void setEmpId(Integer empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"empId=" + empId +
", empName='" + empName + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", dept=" + dept +
'}';
}
}
EmpMapper.java中加入方法
/**
* @param
* @return com.atguigu.mybatis.pojo.Emp
* @description //根据员工ID获取员工和部门信息
* @param: empId
* @date 2023/1/4 22:28
* @author wty
**/
Emp getEmpAndDeptByEmpid(@Param("empId") Integer empId);
EmpMapper.xml中写入sql语句
<!-- Emp getEmpAndDeptByEmpid(@Param("empId") Integer empId);-->
<select id="getEmpAndDeptByEmpid" resultType="Emp">
SELECT a.*, b.`dept_name`
FROM t_emp a
LEFT JOIN
t_dept b ON a.dept_id = b.dept_id
WHERE a.emp_id = #{empId}
</select>
测试类ResultMapTest.java中加入方法
@Test
public void getEmpAndDeptByEmpid() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByEmpid(3);
System.out.println(emp);
sqlSession.close();
}
查看结果:
这种方式,Emp类中dept对象是不可能赋值的,所以这种方式是错误的。
解决方式如下3种:
8.2.1、级联方式处理映射关系
在EmpMapper.xml中添加sql
<!-- 处理多对一的映射关系 -->
<!-- 方法1:级联 -->
<resultMap id="empAndDeptResultMap" type="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
<result column="dept_id" property="dept.deptId"></result>
<result column="dept_name" property="dept.deptName"></result>
</resultMap>
<select id="getEmpAndDeptByEmpid" resultMap="empAndDeptResultMap">
SELECT a.*, b.`dept_name`
FROM t_emp a
LEFT JOIN
t_dept b ON a.dept_id = b.dept_id
WHERE a.emp_id = #{empId}
</select>
测试类ResultMapTest.java中执行测试方法
8.2.2使用association处理映射关系
在EmpMapper.xml中添加sql
<!-- 方法2:使用association处理映射关系 -->
<resultMap id="empAndDeptResultMap" type="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
<result column="dept_id" property="dept.deptId"></result>
<result column="dept_name" property="dept.deptName"></result>
<!-- association 处理多对一的映射关系,也可以处理一对一,主要处理实体类的属性
property 设置实体类中dept对象的映射关系
javaType 设置实体类dept的类型Dept
-->
<association property="dept" javaType="Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
</association>
</resultMap>
<select id="getEmpAndDeptByEmpid" resultMap="empAndDeptResultMap">
SELECT a.*, b.`dept_name`
FROM t_emp a
LEFT JOIN
t_dept b ON a.dept_id = b.dept_id
WHERE a.emp_id = #{empId}
</select>
测试类ResultMapTest.java中执行方法,查看结果
8.2.3分步查询
(1)查询员工信息
在EmpMapper.java中添加方法
/**
* @param
* @return com.atguigu.mybatis.pojo.Emp
* @description //分步查询:先根据员工ID查询员工信息
* @param: empId
* @date 2023/1/4 23:10
* @author wty
**/
Emp getEmpAndDeptByStepOne(@Param("empId") Integer empId);
在EmpMapper.xml中添加sql
<!-- 方法3:分步查询-->
<resultMap id="getEmpAndDeptByStepOnResultMap" type="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
<!-- association 处理多对一的映射关系,也可以处理一对一,主要处理实体类的属性
property 设置实体类中dept对象的映射关系
select 设置分布查询中dept对象的属性从另外哪个sql获取,这里取值是mapper接口中方法的全类名
column 多表查询中外键字段的字段名
-->
<association property="dept" select="com.atguigu.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="dept_id">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
</association>
</resultMap>
<!-- Emp getEmpAndDeptByStepOne(@Param("empId") Integer empId); -->
<select id="getEmpAndDeptByStepOne" resultMap="getEmpAndDeptByStepOnResultMap">
select *
from t_emp
where emp_id = #{empId}
</select>
(2)根据员工所对应的部门id查询部门信息
特别注意,这里要在DeptMapper.java中添加方法
/**
* @param
* @return com.atguigu.mybatis.pojo.Dept
* @description //分步查询2:根据员工所在部门的ID查询部门信息
* @param: deptId
* @date 2023/1/4 23:18
* @author wty
**/
Dept getEmpAndDeptByStepTwo(@Param("deptId") Integer deptId);
在DeptMapper.xml中添加sql
<!-- Dept getEmpAndDeptByStepTwo(@Param("deptId") Integer deptId); -->
<select id="getEmpAndDeptByStepTwo" resultType="Dept">
select *
from t_dept
where dept_id = #{deptId}
</select>
测试类ResultMapTest.java中添加方法
@Test
public void getEmpAndDeptByStepOne() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByStepOne(3);
System.out.println(emp);
sqlSession.close();
}
查询结果
(3)分布查询的优势
延迟加载(懒加载)
如果当前只查询员工信息,而不需要部门信息,那么就不会查询部门信息,即:不会进行第二步的查询,可以减少内存的消耗。
如果要开启延迟加载,需要开启配置信息
查看官方文档
lazyLoadingEnabled:延迟加载的全局开关。当设置为true时,所有关联对象都会延迟加载。
aggressiveLazyLoading:当设置为true时,任何方法的调用都会加载该对象的所有属性。设置成false的时候每个属性会按需加载。比如,如果设置成true那么无论查询什么信息,2步查询都会查,如果设置成false配合lazyLoadingEnabled设置true可以实现按需加载,即如果查询员工姓名,则不会执行部门查询的sql语句。
在mybatis-config.xml增加
<!-- 开启延迟加载(懒加载) -->
<setting name="lazyLoadingEnabled" value="true"/>
修改ResultMapTest.java,只查询员工的名称
@Test
public void getEmpAndDeptByStepOne() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByStepOne(3);
System.out.println(emp.getEmpName());
sqlSession.close();
}
查看运行结果
反之,如果我们查询了部门名称,看下结果
@Test
public void getEmpAndDeptByStepOne() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByStepOne(3);
System.out.println(emp.getDept().getDeptName());
sqlSession.close();
}
设置另外一个参数,因为默认是false可以省略
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 延迟加载由2个参数决定 lazyLoadingEnabled设置true
aggressiveLazyLoading设置false
-->
<!-- 开启延迟加载(懒加载) -->
<setting name="lazyLoadingEnabled" value="true"/>
<!--aggressiveLazyLoading 设置false按需加载 -->
<!--aggressiveLazyLoading 设置true全部加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
注意,这样配置的延迟加载是全局变量,MyBatis中所有的分布查询都会遵循设置,那么如果某个Mapper查询不想遵从的话,我们该怎么办呢。
修改EmpMapper.xml
<!-- association 处理多对一的映射关系,也可以处理一对一,主要处理实体类的属性
property 设置实体类中dept对象的映射关系
select 设置分布查询中dept对象的属性从另外哪个sql获取,这里取值是mapper接口中方法的全类名
column 多表查询中外键字段的字段名
fetchType lazy延迟加载 eager立即加载
-->
<association property="dept" select="com.atguigu.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="dept_id" fetchType="eager">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
</association>
可以看到fetchType如果设置了eager,当前类就遵从了全部加载
再测试一下加载员工姓名
@Test
public void getEmpAndDeptByStepOne() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByStepOne(3);
System.out.println(emp.getEmpName());
sqlSession.close();
}
查看结果
8.3一对多映射处理
Dept.java中添加属性
package com.atguigu.mybatis.pojo;
import java.util.List;
/**
* @ClassName: Dept
* @Description:
* @Author: wty
* @Date: 2023/1/4
*/
public class Dept {
private Integer deptId;
private String deptName;
private List<Emp> emps;
public Dept() {
}
public Dept(Integer deptId, String deptName) {
this.deptId = deptId;
this.deptName = deptName;
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public List<Emp> getEmps() {
return emps;
}
public void setEmps(List<Emp> emps) {
this.emps = emps;
}
@Override
public String toString() {
return "Dept{" +
"deptId=" + deptId +
", deptName='" + deptName + '\'' +
", emps=" + emps +
'}';
}
}
8.3.1collection
在DeptMapper.java中添加方法
/**
* @param
* @return com.atguigu.mybatis.pojo.Dept
* @description //查询部门以及部门中的员工信息
* @param: deptId
* @date 2023/1/6 21:12
* @author wty
**/
Dept getDeptAndEmpByDeptId(@Param("deptId") Integer deptId);
在DeptMapper.xml中添加sql语句
<!-- 处理一对多的映射关系: -->
<!-- 方法1:collection -->
<!-- Dept getDeptAndEmpByDeptId(@Param("deptId") Integer deptId); -->
<resultMap id="getDeptAndEmpByDeptIdResultMap" type="Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
<!--collection处理一(多)对多的映射关系 集合类型 -->
<!-- ofType设置集合中的类型,比如这里Dept实体类中有属性emp,那么该类型来自于实体类Emp,则ofTyoe是Emp -->
<collection property="emps" ofType="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
</collection>
</resultMap>
<select id="getDeptAndEmpByDeptId" resultMap="getDeptAndEmpByDeptIdResultMap">
select *
from t_dept a
left join t_emp b on a.dept_id = b.dept_id
where a.dept_id = #{deptId}
</select>
在ResultMapTest.java添加方法
@Test
public void getDeptAndEmpByDeptId() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = mapper.getDeptAndEmpByDeptId(3);
System.out.println(dept);
sqlSession.close();
}
查询结果
8.3.2分步查询
(1)查询部门信息
在DeptMapper.java中添加方法作为分步查询的第1步
/**
* @param
* @return com.atguigu.mybatis.pojo.Dept
* @description // 分步查询第一步:部门信息以及员工员工信息
* @param: deptId
* @date 2023/1/6 21:39
* @author wty
**/
Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId);
在DeptMapper.xml中添加方法
<!-- 方法2:分步查询 -->
<resultMap id="getDeptAndEmpByStepOneResultMap" type="Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
<collection property="emps" select="com.atguigu.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
fetchType="lazy" column="dept_id">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
</collection>
</resultMap>
<!-- Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId); -->
<select id="getDeptAndEmpByStepOne" resultMap="getDeptAndEmpByStepOneResultMap">
select *
from t_dept
where dept_id = #{deptId}
</select>
(2)根据部门id查询部门中的所有员工
在EmpMapper.java中添加方法作为分步查询的第2步
/**
* @param
* @return java.util.List<com.atguigu.mybatis.pojo.Emp>
* @description //分步查询第二步:部门信息以及员工员工信息
* @param: deptId
* @date 2023/1/6 22:40
* @author wty
**/
List<Emp> getDeptAndEmpByStepTwo(@Param("deptId") Integer deptId);
注意接口中方法的全类名要配置进select标签中
在EmpMapper.xml中添加sql语句
<!-- Dept getDeptAndEmpByStepTwo(@Param("deptId") Integer deptId); -->
<select id="getDeptAndEmpByStepTwo" resultType="Emp">
select *
from t_emp
where dept_id = #{deptId}
</select>
最后修改测试类
@Test
public void getDeptAndEmpByStepOneTesy() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = mapper.getDeptAndEmpByStepOne(1);
System.out.println(dept);
sqlSession.close();
}
查询结果
只查询部门信息,查看延迟加载
修改ResultMapTest.java
@Test
public void getDeptAndEmpByStepOneTesy() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = mapper.getDeptAndEmpByStepOne(1);
System.out.println(dept.getDeptName());
sqlSession.close();
}
查看结果
更改配置,设置为全部加载
修改DeptMapper.xml
<collection property="emps" select="com.atguigu.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
fetchType="eager" column="dept_id">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
</collection>
继续运行测试类,查看结果
分步查询的优点:可以实现延迟加载
但是必须在核心配置文件中设置全局配置信息:
lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。
aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载。
此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载, fetchType=“lazy(延迟加载)|eager(立即加载)”