学习目标
1. 动态SQL
2. 关联查询
3. 延迟加载
4. 缓存机制
1.动态SQL
1.1为什么要使用动态SQL
如果我们要实现多条件的查询,该怎么设计?
一般情况下,需要使用SQL拼接的方式实现
String sql = "select * from phone where 1 = 1 ";
if(a != null)
{
sql += " and a = " + a;
}
if(b != null)
{
sql += " and b = " + b;
}
缺点:
- Java代码中出现大量if,可读性和维护性比较差
- 拼接字符串容易出现错误
1.2动态SQL简介
MyBatis的一个重要特性就是动态SQL,能通过条件动态生成SQL语句
动态SQL包含几种标签:
1.sql
2.if
3.where
4.set
5.trim
6.foreach
1.2.1 sql标签
可以通过sql标签提高sql代码的复用性
定义代码片段
<sql id="sql_select">
select id,name,age,address,deptId
</sql>
<!--查询所有-->
<select id="findAll" resultType="Employee">
<include refid="sql_select"></include> from employee
</select>
1.2.2 if标签
进行条件判断,判断成功会把if内部SQL拼接到外部SQL中,否则不拼接
<if test="条件">
SQL语句
</if>
1.2.3 where标签
用于配置条件,会去掉多余的where、and、or关键词
<!--多条件查询一条信息-->
<select id="findById" resultType="Employee">
<include refid="sql_select"></include> from employee
<where>
<if test="name !=null and name !=''">
and name=#{name}
</if>
<if test="age !=null and age !=''">
and age=#{age}
</if>
<if test="address !=null and address !=''">
and address=#{address}
</if>
<if test="deptId !=null and deptId !=''">
and deptId=#{deptId}
</if>
</where>
</select>
1.2.4 set标签
用于配置update语句,用于去掉多余的,
<!--修改-->
<update id="update">
update employee
<set>
<if test="name !=null and name !=''">
name=#{name},
</if>
<if test="age !=null and age !=''">
age=#{age},
</if>
<if test="address !=null and address !=''">
address=#{address},
</if>
<if test="deptId !=null and deptId !=''">
deptId=#{deptId},
</if>
</set>
where id=#{id}
</update>
1.2.5 trim标签
可以删除或添加前缀和后缀,用来拼接SQL
<trim prefix="添加前缀" suffix="添加后缀" prefixOverride="删除前缀" suffixOverride="删除后缀">
<if>...</if>
</trim>
用trim代替set
<!--配置更新操作,id是方法名称 parameterType参数类型-->
<update id="update" parameterType="User">
update s_user
<trim prefix="set" suffixOverrides=",">
<if test="username !=null and username !=''">
username = #{username},
</if>
<if test="password !=null and password !=''">
password = #{password},
</if>
<if test="type !=null and type !=''">
type = #{type},
</if>
<if test="img !=null and img !=''">
img = #{img},
</if>
</trim>
where id=#{id}
</update>
用trim代替insert
<insert id="insert " parameterType="User">
insert into s_user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username !=null">
username,
</if>
<if test="password !=null">
password,
</if>
<if test="type !=null">
type,
</if>
<if test="img !=null">
img,
</if>
</trim>
<trim prefix="values(" suffix=")" suffixOverrides=",">
<if test="username !=null">
#{username},
</if>
<if test="password !=null">
#{password},
</if>
<if test="type !=null">
#{type},
</if>
<if test="img !=null">
#{img},
</if>
</trim>
</insert >
1.2.6 foreach标签
用于循环拼接SQL
按多个用户名查询用户:
List findUsersByUsernames(List usernames)
select * from s_user where user_name in (‘xxx’,‘ccc’,‘ffff’)
foreach collection=“usernames” item=“name” open="(" close=")" seprator=",">
#{name}
<foreach collection="集合参数名称" item="变量名" open="开始符号" close="结束符号" seprator="分割符" index="下标">
#{变量名}
</foreach>
<!--按多个用户名查询-->
<select id="findUsersByUsernames" resultType="User">
<include refid="sql_select"></include> from s_user
where username in
<foreach collection="usernames" item="username" open="(" close=")" separator=",">
#{username}
</foreach>
</select>
2.关联查询
2.1 关联关系的分类
表之间有几种关联关系:
1.一对一 如:一个人有一个身份证
2.一对多 如:一个部门有多个员工
3.多对一 如:多个员工属于一个部门
4.多对多 如:一个学生可以选择多门课程,一门课程也有多个学生
2.2 关联配置
MyBatis映射文件中,在ResultMap里可以配置关联关系
主要有两种标签来映射关联属性:
-
collection 配置集合类型的属性
-
association 配置单独对象的属性
collection和association的相关参数:
- property 属性的名称
- select 查询方法
- javaType 属性的类型
- ofType 如果属性是集合,集合中对象的类型
- column 外键字段名
2.2.1 查询集合
1.给类型添加员工集合,这里使用collection
public class Dept {
private Integer deptId;
private String deptName;
//添加员工集合
private List<Employee> employees;
...
}
2.员工类型Mapper接口
public interface DeptMapper {
//按id查询部门
Dept dept(Integer id);
}
3.在EmployeeMapper映射接口中定义方法
//通过部门Id查询所有的员工
public interface EmployeeMapper{
List<Employee> findEmployees(Integer deptId);
}
4.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">
<!--namespace 文件和接口映射起来-->
<mapper namespace="com.hp.mybatis.mapper.DeptMapper">
<!--实现Dept到 dept的映射-->
<resultMap id="deptMap" type="Dept">
<!--配置主键-->
<id property="deptId" column="deptId"></id>
<!--配置普通字段-->
<result property="deptName" column="deptName"></result>
<collection property="employees" javaType="java.util.List" ofType="Employee"
select="com.hp.mybatis.mapper.EmployeeMapper.findEmployees" column="deptId"></collection>
</resultMap>
<sql id="sql_select">
select deptId,deptName
</sql>
<!--查询部门-->
<select id="dept" resultMap="deptMap">
<include refid="sql_select"></include> from dept where deptId=#{deptId}
</select>
</mapper>
这里使用的是子查询的机制,在查询部门类型后,将每个类型id作为参数,调用员工接口的findEmployees方法查询元工集合。
2.2.2 查询单个对象
给Employee添加Dept属性
public class Employee {
private Integer id;
private String name;
private Integer age;
private String address;
private Integer deptId;
private Dept dept;
...
}
2)员工映射文件中,使用association配置Dept 属性
<?xml version="1.0" encoding="UTF-8"?><resultMap id="employeesMap" type="Employee">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
<result property="deptId" column="deptId"></result>
<association property="dept" javaType="Employee" select="com.hp.mybatis.mapper.DeptMapper.dept" column="deptId"></association>
</resultMap>
<sql id="sql_select">
select id,name,age,address,deptId
</sql>
<select id="findEmployees" resultMap="employeesMap">
<include refid="sql_select"></include> from employee where deptId=#{deptId}
</select>
3、延迟加载
问题:在查询部门类型时,如果只需要类型名称,不需要该类的员工,但还是把员工查询出来,岂不浪费时间?
我们需要一种机制来优化查询
3.1延迟加载
延迟加载也叫懒加载,需要数据时再启动查询,是MyBatis对数据库的优化机制。
3.2 配置
给MyBatis-config.xml添加配置
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 打印查询语句 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
<!--配置懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--执行所有属性按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
3.3 测试
在控制台查看,查询部门类型后,调用和不调用员工集合属性的SQL执行情况
4.缓存机制
4.1 缓存概述
将数据库的数据保存到内存或磁盘上,查询数据时直接访问内存或磁盘,减少对数据库的访问。
缓存作用:
1)减少数据库的负担,因为数据库连接数有限,并发量大的情况下,数据库连接耗尽,造成用户长期等待或者数据库宕机。
2)提升性能,对数据库的访问是通过TCP/IP网络进行访问,速度比较慢,通过缓存可以提高访问速度。
4.2.1 MyBatis两级缓存
-
一级缓存
范围:SqlSession
同一个SqlSession对象访问的数据,可以被缓存,下次访问直接获得不用查数据库
默认开启,Spring整合MyBatis默认是没有开启
失效的情况:
1)创建新的SQLSession
2)对缓存中的对象进行修改
-
二级缓存
范围:SQLSessionFactory
同一个SqlSessionFactory创建的所有SQLSession能共享缓存对象
默认开启
1)cacheEnabled 配置为true
2)在对应的映射文件上加标签
3)实体类需要序列化
4.2.2 缓存执行流程