Mybatis上手教程–select语句的resultMap相关配置
一为什么要用resultMap
场景1:当我们的数据库表中的字段名为last_name,email,而对应的Java bean实体类中的属性名为lastName,mail时,此时,我们的查询SQL若是直接写成类似select * from tbl_employee时,最终Java代码执行的结果,看到类似于:lastName=null, gender=’男’, mail=’null’的情况。当然,解决该类问题的方式,我们可以通过在Mybatis主配置文件中,通过settings标签来配置驼峰命名转换规则来处理last_name和lastName的映射关系,比如:
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
或者,我们也可以在SQL mapper文件的SQL语句中,给对应的列取别名:
select last_name as lastName,email as mail from tbl_employee
这2种思路都可以解决响应的问题。
但是,我们也可以通过在具体的SQL mapper文件中,通过定义resultMap标签来完成。
场景2:如果我们的Javabean实体类Employee里有一个属性,指向另外一个实体类Department。用于表示该员工所属的部门信息的话。那么,此时,如果我们想要查询的时候,不但要查询员工的信息,还想要连带查询部门名称的话。我们就得依赖resultMap标签了。当然,这种情况下,我们的SQL语句,直接写一个关联的join查询就行,比较简单。
当然,我们得知道在select语句中resultMap和resultType二者是互斥的。即只能选用其中一个,二者不可兼用。
二用resultMap来解决别名映射的问题
1 EmployeeMapper接口文件,EmployeeMapper.java
Employee getEmployeeByIdViaResultMap(Integer id);
2 SQL mapper文件,EmployeeMapper.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.knockatdatabase.dao.EmployeeMapper">
<resultMap id="myEmployee" type="com.knockatdatabase.Employee">
<id column="id" property="id"></id>
<result column="email" property="mail"></result>
<result column="gender" property="gender"></result>
<result column="last_name" property="lastName"/>
</resultMap>
<select id="getEmployeeByIdViaResultMap" resultMap="myEmployee">
select * from tbl_employee where id=#{id}
</select>
</mapper>
注意:此时的SQL mapper文件中,多了一个标签resultMap,关于它的解释和说明如下:
- id=”myEmployee”,是关于该resultMap的标识符,将来引用的时候,就是用的这个名称;
- type=”com.knockatdatabase.Employee”,表示的是要对这个具体的Javabean的实体类做结果字段的映射;
- <id column=”id” property=”id”></id>,表示的是要映射数据库表里的主键和javabean实体类的属性映射;
- 剩下的其它字段则用result标签来映射;
- 其中的配置,column表示数据库表中的字段,property则对应着javabean实体类的属性;
然后,在我们的SQL中,选用的是上面配置的那个resultMap,而不再使用resultType了。
3 测试类Java代码,EmployeeTest.java
@Test
public void testGetEmployeeByIdViaResultMap() {
String resource = "mybatis-config.xml";
SqlSession sqlSession = null;
try{
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmployeeByIdViaResultMap(9);
System.out.println(employee);
} catch (IOException e) {
e.printStackTrace();
}finally {
sqlSession.close();
}
}
//结果:
Employee{id=9, lastName='Huang', gender='男', mail='hensi163@163.com'}
三 用resultMap来解决实体类引用另一个实体类的问题
实体类员工表里有一个属性,指向另外一个实体类Department,而且数据库里员工表上有个外键约束指向主表部门表的主键字段。
现在,想要实现查询员工信息的时候,连带员工所在部门名称一起查询出来。
1添加tbl_dept表,并给tbl_employee添加外键约束,添加测试数据
[mysql@ppasdev ~]$ mysql -umybatis_project -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2634
Server version: 5.6.32-log MySQL Community Server (GPL)
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mybatis_project |
| test |
+--------------------+
3 rows in set (0.16 sec)
mysql> use mybatis_project;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+---------------------------+
| Tables_in_mybatis_project |
+---------------------------+
| tbl_employee |
+---------------------------+
1 row in set (0.00 sec)
mysql> create table tbl_dept(id int primary key auto_increment,dept_name varchar(20));
Query OK, 0 rows affected (1.88 sec)
mysql> desc tbl_dept;
+-----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| dept_name | varchar(20) | YES | | NULL | |
+-----------+-------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)
mysql> alter table tbl_employee add column did int, add constraint fk_emp_dept foreign key(did) references tbl_dept(id) on delete cascade;
Query OK, 8 rows affected (0.89 sec)
Records: 8 Duplicates: 0 Warnings: 0
mysql> desc tbl_employee;
+-----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| last_name | varchar(50) | YES | | NULL | |
| gender | char(1) | YES | | NULL | |
| email | varchar(30) | YES | | NULL | |
| did | int(11) | YES | MUL | NULL | |
+-----------+-------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
mysql> insert into tbl_dept values(1,'市场部');
Query OK, 1 row affected (0.04 sec)
mysql> insert into tbl_dept values(2,'财务部');
Query OK, 1 row affected (0.04 sec)
mysql> update tbl_employee set did=1 where id=1;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update tbl_employee set did=2 where id>1;
Query OK, 7 rows affected (0.09 sec)
Rows matched: 7 Changed: 7 Warnings: 0
mysql> select * from tbl_employee;
+----+-----------+--------+------------------------------+------+
| id | last_name | gender | email | did |
+----+-----------+--------+------------------------------+------+
| 1 | 黄伟 | 男 | huangwei@knockatdatabase.com | 1 |
| 2 | Huang | ? | hensi163@163.com | 2 |
| 4 | Huang | 男 | hensi163@163.com | 2 |
| 5 | Huang | 男 | hensi163@163.com | 2 |
| 6 | Huang | 男 | hensi163@163.com | 2 |
| 7 | Huang | 男 | hensi163@163.com | 2 |
| 8 | Huang | 男 | hensi163@163.com | 2 |
| 9 | Huang | 男 | hensi163@163.com | 2 |
+----+-----------+--------+------------------------------+------+
8 rows in set (0.03 sec)
mysql> select * from tbl_dept;
+----+-----------+
| id | dept_name |
+----+-----------+
| 1 | 市场部 |
| 2 | 财务部 |
+----+-----------+
2 rows in set (0.00 sec)
mysql>
2 构建Department实体类,并修改Employee实体类
Department实体类:
package com.knockatdatabase;
/**
* @Author:asher
* @Date:2021/10/10 11:16
* @Description:com.knockatdatabase
* @Version:1.0
*/
public class Department {
private int id;
private String deptName;
public Department() {
}
public Department(int id, String deptName) {
this.id = id;
this.deptName = deptName;
}
@Override
public String toString() {
return "Department{" +
"id=" + id +
", deptName='" + deptName + '\'' +
'}';
}
}
employee实体类:
package com.knockatdatabase;
/**
* @Author:asher
* @Date:2021/9/28 12:21
* @Description:com.knockatdatabase
* @Version:1.0
*/
public class Employee {
private Integer id;
private String lastName;
private String gender;
private String mail;
private Department department;
public Employee() {
}
public Employee(Integer id, String lastName, String gender, String mail, Department department) {
this.id = id;
this.lastName = lastName;
this.gender = gender;
this.mail = mail;
this.department = department;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", lastName='" + lastName + '\'' +
", gender='" + gender + '\'' +
", mail='" + mail + '\'' +
", department=" + department +
'}';
}
}
在之前的Employee实体类的基础上,添加一个私有成员变量,其类型是Department类型的,然后添加该字段的getter/setter,以及toString(),构造方法。
3 EmployeeMapper.java接口添加获取员工和部门信息的新方法
Employee getEmployeeAndDepartmentById(Integer id);
4 SQL mapper文件,编写新的resultMap
<resultMap id="myEmpAndDept" type="com.knockatdatabase.Employee">
<id column="id" property="id"></id>
<result column="last_name" property="lastName"></result>
<result column="gender" property="gender"></result>
<result column="email" property="mail"></result>
<result column="dept_name" property="department.deptName"></result>
</resultMap>
<select id="getEmployeeAndDepartmentById" resultMap="myEmpAndDept">
select emp.id,emp.gender,emp.email mail,emp.last_name,dept.dept_name
from tbl_employee emp,tbl_dept dept
where emp.did=dept.id and emp.id=#{id}
</select>
这里要务必注意,前面的resultMap是一个新构建出来的,其中 <result column=”dept_name” property=”department.deptName”></result> 表示关联查询的SQL结果中的dept_name字段,对应Employee实体类的Department类型的成员变量department的deptName。是一种吉联的属性关联方法。
5 测试类代码
@Test
public void testGetEmployeeAndDepartmentById() {
String resource = "mybatis-config.xml";
SqlSession sqlSession = null;
try{
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employeeAndDepartmentById = mapper.getEmployeeAndDepartmentById(1);
System.out.println("employee info: " + employeeAndDepartmentById);
Department department = employeeAndDepartmentById.getDepartment();
System.out.println("department info:" + department);
} catch (IOException e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
//结果
employee info: Employee{id=1, lastName='黄伟', gender='男', mail='null', department=Department{id=0, deptName='市场部'}}
department info:Department{id=0, deptName='市场部'}
注意:该案例中,为什么输出的结果中mail=’null’???深入分析,会发现,SQL语句中,使用了列别名的形式,”select emp.id,emp.gender,emp.email mail,emp.last_name,dept.dept_name”,已经将email取别名为mail了,而在resultMap中,我们又再次的将<result column=”email” property=”mail”></result>,也就意味着要把SQL语句中的email转为mail,可是此时SQL语句中,已经没有了email列了,于是Mybatis就将NULL再次赋值给mail列了。所以,最后输出的结果成了mail=’null’。也就说说,我们多此一举的画蛇添足了。
四小结
- resultMap和resultType只能二选一,不能同时使用;
- resultMap可以解决字段别名映射的问题,即库表字段名和实体类属性名不匹配的情形;
- resultMap可以解决实体类中的属性引用另外一个实体类的情形,如员工类里有一个字段指向部门表的实体类,适用场景是查询员工信息时,连带其部门信息一起查询;