前言
上一篇文章中荔枝梳理了有关MyBatis的相关环境配置和核心配置文件及其模板的设置和生成,而在这篇文章中荔枝会着重梳理MyBatis多种查询情况、特殊SQL执行以及两表联查时映射关系的处理。希望对需要的小伙伴有帮助~~~
文章目录
2.2.3 association分步查询解决多对一映射问题
一、多种查询情况
在MyBatis中,对于数据库的查询来说,如果查询出来的数据只有一条,可以通过实体类对象或者集合(list、map)来接收,如果查询的数据有多条,则一定不能通过实体类来接收,否则会抛出异常TooManyResultException。
1.1 查询单条数据
实体类对象:
User getUserById(@Param("id") Integer id);
list集合:
List<User> getAllUser(@Param("id") Integer id);
map集合
Map<String,Object> getUserByIdToMap(@Param("id") Integer id);
1.2 查询多条数据
list集合:
List<User> getAllUser(@Param("id") Integer id);
map集合:
List<Map<String,Object>> getAllUserToMap(@Param("id") Integer id);
或者借助@MapKey注解
@MapKey("id") //把查询到的数据的某一个字段作为key,查询到的所有数据作为值value
Map<String,Object> getAllUserToMap2(@Param("id") Integer id);
1.3 特殊SQL的执行
在MyBatis中,大多数的查询都可以使用#{}的格式来获取参数,但是有一些特殊SQL的执行则不能直接采用#{}的格式。
1.3.1 模糊查询
采用${}格式
<mapper namespace="com.crj.mapper.SQLMapper">
<!-- List<User> getUserByLike(@Param("username") String username);-->
<select id="getUserByLike" resultType="User">
select * from t_user where username like '%${username}%'
</select>
</mapper>
采用concat拼接并采用#{}
<mapper namespace="com.crj.mapper.SQLMapper">
<!-- List<User> getUserByLike(@Param("username") String username);-->
<select id="getUserByLike" resultType="User">
select * from t_user where username like concat('%',#{username},'%')
</select>
</mapper>
采用" "拼接SQL
<mapper namespace="com.crj.mapper.SQLMapper">
<!-- List<User> getUserByLike(@Param("username") String username);-->
<select id="getUserByLike" resultType="User">
select * from t_user where username like "%"#{username}"%"
</select>
</mapper>
1.3.2 批量删除
在执行批量删除的时候为什么不能使用#{}?这是因为#{}会自动加上单引号从而导致SQL异常无法实现批量删除的功能。
<!-- int deleteMore(@Param("ids") String ids);-->
<delete id="deleteMore">
delete from t_user where id in (${ids})
</delete>
1.3.3 动态设置表名
在数据库执行完水平分表之后,MyBatis在执行数据操作的时候就需要动态设置表名从而实现分表查询。这里因为表名是不能加单引号的,所以这里还是采用${}的形式来实现动态分表查询。
<!-- List<User> getUserByTableName(@Param("tableName") String tableName);-->
<select id="getUserByTableName" resultType="User">
select * from ${tableName} where id=1
</select>
1.3.4 获取自增主键
插入数据时使用自增主键需要设置insert标签的两个属性:
- useGeneratedKeys:设置当前标签中的SQL使用了自增主键
- keyProperty:将自增的主键的值赋值给传输到映射文件中参数的某个属性
<!-- void insertUser(User user);-->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into t_user values (null,#{username},#{password})
</insert>
二、自定义映射
在之前我们使用resultType要求字段名和属性名一致,使用的是一种默认的自动创建的映射关系。但是当字段名和属性名不一致的时候或者处理一对多和多对一的映射关系的时候,我们需要自定义映射关系resultMap。
2.1 解决字段名和属性名不一致的三种方案
- 为字段起别名,保持与属性名一致
<select id="getAllEmp" resultType="Emp">
select eid,emp_name empName from t_emp
</select>
- 在核心配置文件中借助setting标签中的mapUnderscoreToCameCase
<!--设置mybatis的全局设置-->
<settings>
<!-- mapUnderscoreToCameCase:将下划线映射到驼峰命名,默认是false不支持-->
<setting name="mapUnderscoreToCameCase" value="true"/>
</settings>
通过将mapUnderscoreToCameCase属性值设置为true,开启mybatis将下划线映射为驼峰命名的功能
- 使用resultMap来自定义映射
<?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.crj.mybatis.mapper.EmpMapper">
<!-- List<Emp> getAllEmp();-->
<resultMap id="empResultMap" type="Emp">
<!--使用resultMap后建议要把所有字段名和属性之间的关系都声明出来-->
<id property="eid" column="eid"/>
<result column="emp_name" property="empName"/> <!--注意这里的映射关系-->
<result column="age" property="age"/>
<result column="sex" property="sex"/>
</resultMap>
<select id="getAllEmp" resultMap="empResultMap">
select * from t_emp
</select>
</mapper>
id:唯一标识,不能重复
type:设置映射关系中的实体类类型
这里有两个子标签:id和result。
id:设置主键的映射关系
result:设置普通字段的映射关系
属性 :
property:设置映射关系中的属性名,必须是type属性所设置的实体类类型的属性名
column:设置映射关系中的字段名,是SQL语句查询出的字段名
2.2 多对一的映射关系
这里有员工和部门两张表,分别为二者创建实现类Emp和Dept。在多对一的映射关系中,我们考虑的是多个员工同属于一个部门。
package com.crj.mybatis.pojo;
public class Emp {
private Integer eid;
private String empName;
private Integer age;
private String sex;
private String email;
private Dept dept;
@Override
public String toString() {
return "Emp{" +
"eid=" + eid +
", empName='" + empName + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", email='" + email + '\'' +
", dept=" + dept +
'}';
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public Emp(Integer eid, String empName, Integer age, String sex, String email) {
this.eid = eid;
this.empName = empName;
this.age = age;
this.sex = sex;
this.email = email;
}
public Emp(){}
public Integer getEid() {
return eid;
}
public void setEid(Integer eid) {
this.eid = eid;
}
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 getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
2.2.1 通过级联属性赋值解决多对一映射
<resultMap id="getEmpAndDept" type="Emp">
<id property="eid" column="eid"/>
<result column="emp_name" property="empName"/>
<result column="age" property="age"/>
<result column="sex" property="sex"/>
<result column="did" property="dept.did"/>
<result column="dept_name" property="dept.deptName"/>
</resultMap>
<!-- Emp getEmpAndDept(@Param("eid") Integer eid);-->
<select id="getEmpAndDept" resultMap="getEmpAndDept">
select * from t_emp left join t_dept on t_emp.did=t_dept.did where t_emp.eid = #{eid}
</select>
2.2.2 通过association解决多对一映射问题
其中,对于association中的属性我们需要了解:
- property:需要处理多对一映射关系的属性名
- javaType: 该属性的类型
<resultMap id="getEmpAndDept" type="Emp">
<id property="eid" column="eid"/>
<result column="emp_name" property="empName"/>
<result column="age" property="age"/>
<result column="sex" property="sex"/>
<association property="dept" javaType="Dpet">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
</association>
</resultMap>
2.2.3 association分步查询解决多对一映射问题
此时association中的property属性的含义不变,但还有两个比较重要的属性需要设置相应的属性值
- select:设置分步查询的sql的唯一标识(一般是设置多对一中一处的mapper接口全类名.方法名)
- column:设置分布查询的条件,在这个例子中,分布查询是依据两个表之间的关系也就是did来实现的。
- fetchType:当开启了全局的延迟加载之后,可以通过此属性手动控制延迟加载的效果
分步查询的第一步:查询员工信息
EmpMapper.java
package com.crj.mybatis.mapper;
import com.crj.mybatis.pojo.Emp;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface EmpMapper {
/**
* 通过分布查询员工以及员工所对应的部门信息
* 分布查询第一步:查询员工信息
*/
Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);
}
EmpMapper.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.crj.mybatis.mapper.EmpMapper">
<!-- 分步查询,两表联查-->
<resultMap id="empAndDeptByStepResultMap" type="Emp">
<id property="eid" column="eid"/>
<result column="emp_name" property="empName"/>
<result column="age" property="age"/>
<result column="sex" property="sex"/>
<association property="dept"
select="com.crj.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="did"></association>
</resultMap>
<!-- Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);-->
<select id="getEmpAndDeptByStepOne" resultMap="empAndDeptResultMapTwo">
select * from t_emp where eid = #{eid}
</select>
</mapper>
分步查询第二步:根据查询到的员工信息中的did来查询相应的部门信息
DpetMapper.java
package com.crj.mybatis.mapper;
import com.crj.mybatis.pojo.Dept;
import org.apache.ibatis.annotations.Param;
public interface DeptMapper {
/**
* 通过分布查询员工以及员工所对应的部门信息
* 分布查询第二步:通过did查询员工所对应的部门
*/
Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);
}
DeptMapper.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.crj.mybatis.mapper.DeptMapper">
<!-- Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);-->
<select id="getEmpAndDeptByStepTwo" resultType="Dept">
select * from t_dept where did = #{did}
</select>
</mapper>
需要注意的是,分步查询在实际的应用场景中使用的会比较多。
注意:为什么更推荐使用分步查询呢?
这是因为通过分步查询我们可以实现MyBatis的延迟加载(懒加载 )功能,通过分步查询我们可以做到需要查询什么信息就执行什么SQL语句,比如我们仅需要查询员工信息而不需要部门的内容时,通过延迟加载我们只会执行EmpMapper中的SQL语句,同时分步查询也实现了两种不同的查询功能的隔离。MyBatis中默认是不会开启延迟加载的功能滴,
2.2.4 延迟加载
要想实现延迟加载的功能,就必须在全局配置文件中开启相应的延迟加载的开关:
- lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载,默认true;
- aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对像的所有属性。否则,每个属性会按需加载,默认false;
开启延迟加载时针对于当前的所有的分步查询,如果哪一步不需要延迟加载可通过association和collection中的fetchType属性设置当前的分步查间是否使用延迟加载,fetchType="lazy(延迟加载) l eager(立即加载)'
<settings>
<!-- 开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
2.3 一对多映射关系
2.3.1 Collection解决一对多映射关系
Dpet.java
//一对多的映射关系
private List<Emp> emps;
DeptMapper.java
/**
* 以部门为主表来获取部门中所有的员工信息
*/
Dept getDeptAndEmp(@Param("did") Integer did);
DeptMapper.xml
<resultMap id="deptAndEmpResultMap" type="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
<!--注意区分ofType和association中属性javaType的区别-->
<collection property="emps" ofType="Emp">
<id property="did" column="did"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
</collection>
</resultMap>
<!-- Dept getDeptAndEmp(@Param("did") Integer did);-->
<select id="getDeptAndEmp" resultMap="deptAndEmpResultMap">
select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did = #{did}
</select>
2.3.2 分步查询解决一对多映射关系
分步查询的第一步:查询部门信息
DeptMapper.java
package com.crj.mybatis.mapper;
import com.crj.mybatis.pojo.Dept;
import org.apache.ibatis.annotations.Param;
public interface DeptMapper {
/**
* 分步查询处理一对多的关系
* 第一步:查询部门信息
*/
Dept getDeptAndEmpByStepOne(@Param("did") Integer did);
}
DeptMapper.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.crj.mybatis.mapper.DeptMapper">
<resultMap id="deptAndEmpByStepResultMap" type="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
<collection property="emps" select="com.crj.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo" column="did"></collection>
</resultMap>
<!-- Dept getDeptAndEmpByStepOne(@Param("did") Integer did);-->
<select id="getDeptAndEmpByStepOne" resultMap="deptAndEmpByStepResultMap">
select * from t_dept where did = #{did}
</select>
</mapper>
分步查询第二步:根据查询到的员工信息中的did来查询相应的部门信息
EmpMapper.java
package com.crj.mybatis.mapper;
import com.crj.mybatis.pojo.Emp;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface EmpMapper {
/**
* 分步查询处理一对多的关系
* 第二步:根据did查询员工信息
*/
List<Emp> getDeptAndEmpByStepTwo();
}
EmpMapper.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.crj.mybatis.mapper.EmpMapper">
<!-- List<Emp> getDeptAndEmpByStepTwo();-->
<select id="getDeptAndEmpByStepTwo" resultType="Emp">
select * from t_emp where did = #{did}
</select>
</mapper>
总结
弄清楚两种映射关系以及相应的文件依赖关系和处理无疑时学习的重点,荔枝学习的时候也是感觉好像懂了,但复盘的时候还是有很多概念明显没有弄清楚。最近荔枝的学习状态有点波动,可能是摆烂了两周的缘故。。。接下来荔枝也会调整状态滴,继续学习Java后端技术栈并作出相应的Blog输出。最近看了丙哥的文章,喜欢一句话分享给大家:你知道的越多,你不知道的越多。共勉共勉哈哈哈哈~~~
今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~