MyBatis学习笔记(二)
动态SQL,关联查询,缓存机制,转义字符
个人笔记,如有错误,恳请批评指正。
动态SQL操作
IF语句
修改配置文件deptMapper.xml,添加
<!-- 动态Sql语句 -->
<!-- 根据多个条件生成动态的sql语句,查询信息 -->
<!-- 弊端:当不存在条件时,会查询所有的数据,改用choose可以解决问题 -->
<select id="selectDeptUseIf" parameterType="Dept" resultMap="deptResultMap"><!-- 若字段名与属性名一致可以直接用resultType="Dept" -->
select * from dept where 1=1
<!-- 直接写属性,不需要#{},and不能丢 -->
<if test="deptId!=null">and dept_id = #{deptId}</if>
<if test="deptAddress!=null">and dept_address = #{deptAddress}</if>
<if test="deptName!=null">and dept_name = #{deptName}</if>
</select>
修改DeptDaoImpl.java,添加selectListUseIf方法:
//根据参数使用配置文件的IF语句自动填充查询的过滤条件
public List<Dept> selectDeptUseIf(Dept dept){
List<Dept> list = null;
try {
session = MybatisSessionFactory.getSession();
list = session.selectList("cn.ustb.entity.DeptMapper.selectDeptUseIf", dept);
session.commit();
} catch (Exception e) {
e.printStackTrace();
session.rollback();
}
return list;
}
WHERE语句
修改配置文件deptMapper.xml,添加
<!-- 动态Where条件 ,一般也需要与if结合使用,与纯if比较,省略了where 1=1-->
<!-- 如果需要去掉1=1,可用where -->
<select id="selectDeptUseWhere" parameterType="Dept" resultMap="deptResultMap">
select * from dept
<where>
<if test="deptId!=null">and dept_id = #{deptId}</if>
<if test="deptAddress!=null">and dept_address = #{deptAddress}</if>
<if test="deptName!=null">and dept_name = #{deptName}</if>
</where>
</select>
choose(when,otherwise)语句
修改配置文件deptMapper.xml,添加
<!-- choose 用choose时,前面的条件符合就不再执行之后的条件-->
<select id="selectDeptUseChoose" parameterType="Dept" resultMap="deptResultMap">
select * from dept
<where>
<choose>
<when test="deptId != null">and dept_id = #{deptId}</when>
<when test="deptAddress != null">and dept_address = #{deptAddress}</when>
<when test="deptName != null">and dept_name = #{deptName}</when>
<otherwise>1=2</otherwise>
</choose>
</where>
</select>
SET语句
修改配置文件deptMapper.xml,添加
<!-- 动态set语句可以用来更新数据 -->
<update id="updateDeptUseSet" parameterType="Dept">
update dept
<set>
<if test="deptAddress!=null">dept_address = #{deptAddress}</if>
<if test="deptName!=null">dept_name = #{deptName}</if>
</set>
where dept_id = #{deptId}
</update>
ForEach语句
修改配置文件deptMapper.xml,添加
<!-- 若要查询多个id的数据 -->
<!-- 定义根据多个部门ID查询部门相关部门信息的SQL语句 ,resultMap的值是指集合里元素的类型,parameterType不用指定 -->
<!-- foreach -->
<select id="selectDeptUseForeach" resultMap="deptResultMap">
select * from dept where dept_id in (
<!-- collection="array或list",array用来对应参数为数组,list对应参数为 集合 -->
<foreach collection="array" item="deptId" separator=",">
#{deptId}
</foreach>
)
</select>
include语句
修改配置文件deptMapper.xml,添加
<insert id="insertDeptUseInclude" parameterType="Dept">
insert into dept
<include refid="key"></include>
values
<include refid="value"></include>
</insert>
<sql id="key">
<trim suffixOverrides="," prefix="(" suffix=")">
<if test="deptName!=null">dept_name,</if>
<if test="deptAddress!=null">dept_address,</if>
</trim>
</sql>
<sql id="value">
<trim suffixOverrides="," prefix="(" suffix=")">
<if test="deptName!=null">#{deptName},</if>
<if test="deptAddress!=null">#{deptAddress},</if><!-- #{} necessary -->
</trim>
</sql>
关联查询
创建数据库及表:
drop database if exists mybatis;
create database mybatis CHARACTER SET UTF8;
use mybatis;
create table dept(
dept_id int primary key auto_increment,
dept_name varchar(50),
dept_address varchar(50)
);
insert into dept(dept_name,dept_address) values('研发部1部','北京');
insert into dept(dept_name,dept_address) values('研发部2部','广州');
insert into dept(dept_name,dept_address) values('研发部3部','深圳');
create table emp(
emp_id varchar(20) primary key,
emp_name varchar(50),
emp_age int(2),
emp_gender char(1),
dept_id int
);
insert into emp(emp_id,emp_name,emp_age,emp_gender,dept_id) values('430726199210102210','张三','18','男','1');
insert into emp(emp_id,emp_name,emp_age,emp_gender,dept_id) values('430726199210102211','李四','20','女','3');
insert into emp(emp_id,emp_name,emp_age,emp_gender,dept_id) values('430726199210102212','王五','19','男','2');
select * from emp;
select * from dept;
select e.*,d.* from emp e inner join dept d on e.dept_id = d.dept_id;
基于association查询(用于多对一或一对一)
创建存在关联的实体类
public class Dept implements Serializable{
private String deptAddress;
private String deptName;
private Integer deptId;
public class Emp implements Serializable{
private String empId;
private String empName;
private String empSex;
private Dept dept;
配置DeptMapper.xml/EmpMapper.xml
(重点加入级联的查询语句),并映射文件信息到mybatis-config.xml中:
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="cn.ustb.entity.DeptMapper">
<resultMap type="Dept" id="deptResultMap">
<id column="dept_id" property="deptId" />
<result column="dept_name" property="deptName" />
<result column="dept_address" property="deptAddress" />
</resultMap>
</mapper>
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="cn.ustb.entity.EmpMapper">
<resultMap type="Emp" id="empResultMap">
<id column="emp_id" property="empId"/>
<result column="emp_name" property="empName"/>
<result column="emp_age" property="empAge"/>
<result column="emp_gender" property="empGender"/>
<association property="dept" column="dept_id" resultMap="cn.ustb.entity.DeptMapper.deptResultMap"></association>
</resultMap>
<select id="selectEmp" parameterType="string" resultMap="empResultMap">
select e.*,d.* from emp e inner join dept d on e.dept_id = d.dept_id
where e.emp_name = #{empName}
</select>
</mapper>
配置文件myBatis-config.xml
<!-- 通过别名简化对类的使用 -->
<typeAliases>
<typeAlias type="cn.ustb.entity.Dept" alias="Dept"/>
<typeAlias type="cn.ustb.entity.Emp" alias="Emp"/>
</typeAliases>
…….
<!--导入SQL映射文件 -->
<mappers>
<mapper resource="cn/ustb/entity/DeptMapper.xml"></mapper>
<mapper resource="cn/ustb/entity/EmpMapper.xml"></mapper>
</mappers>
编写EmpDaoImpl.java实现查询
public class EmpDaoImpl {
private SqlSession session;
public Emp selectEmp(String empName){
Emp emp = null;
try {
session = MybatisSessionFactory.getSession();
emp = session.selectOne("cn.ustb.entity.EmpMapper.selectEmp", empName);
} catch (Exception e) {
e.printStackTrace();
}
return emp;
}
}
基于collection查询(用于一对多或多对多)
编写 Dept.java/Emp.java实体类
Dept.java
public class Dept implements Serializable{
private String deptAddress;
private String deptName;
private Integer deptId;
private List<Emp> emps;
Emp.java
public class Emp implements Serializable{
private String empId;
private String empName;
private String empSex;
配置DeptMapper.xml
DeptMapper.xml文件,配置resultMap(重点是collection配置)和查询SQL语句:
<?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="cn.ustb.entity.DeptMapper">
<resultMap type="Dept" id="deptResultMap">
<id column="dept_id" property="deptId" />
<result column="dept_name" property="deptName" />
<result column="dept_address" property="deptAddress" />
<collection property="emps" resultMap="cn.ustb.entity.EmpMapper.empResultMap"></collection><!-- 没有column -->
</resultMap>
<select id="selectDept" parameterType="String" resultMap="deptResultMap">
select d.*,e.* from dept d inner join emp e on d.dept_id = e.dept_id
where d.dept_name = #{deptName}
</select>
</mapper>
配置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="cn.ustb.entity.EmpMapper">
<resultMap type="Emp" id="empResultMap">
<id column="emp_id" property="empId"/>
<result column="emp_name" property="empName"/>
<result column="emp_age" property="empAge"/>
<result column="emp_gender" property="empGender"/>
</resultMap>
</mapper>
编写数据库操作类DeptDaoImpl.java
public class DeptDaoImpl {
private SqlSession session = null;
public Dept selectDept(String deptName){
Dept dept = null;
try {
session = MybatisSessionFactory.getSession();
dept = session.selectOne("cn.ustb.entity.DeptMapper.selectDept", deptName);
session.commit();
} catch (Exception e) {
e.printStackTrace();
session.rollback();
}finally{
MybatisSessionFactory.closeSession();
}return dept;
}
}
一对多双向关联查询示例
编写实体类:Dept.java/Emp.java
Dept.java
public class Dept implements Serializable{
private String deptAddress;
private String deptName;
private Integer deptId;
private List<Emp> emps;
Emp.java
public class Emp implements Serializable{
private String empId;
private String empName;
private String empSex;
private Dept dept;
编写DeptMapper.xml/EmpMapper.xml文件
DeptMapper.xml
*值得注意的是,如果在各自的resultMap相互配置了引用,在查询时会导致堆栈溢出的异常。解决的办法是在映射文件中分割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="cn.ustb.entity.DeptMapper">
<!-- 缓存的第二级开关,写上即开启 -->
<!-- 一级默认开启,二级默认关闭,三级默认开启 -->
<cache eviction="LRU" size="2" readOnly="false"></cache>
<resultMap type="Dept" id="deptResultMap">
<id column="dept_id" property="deptId" />
<result column="dept_name" property="deptName" />
<result column="dept_address" property="deptAddress" />
</resultMap>
<resultMap type="Dept" id="deptExtResultMap" extends="deptResultMap">
<collection property="emps" resultMap="cn.ustb.entity.EmpMapper.empResultMap"></collection><!-- resultMap是empResultMap而不是empExtResultMap,即没有配置多的属性,避免了相互查询时的内存溢出 -->
</resultMap>
<select id="selectDept" parameterType="String" resultMap="deptExtResultMap" useCache="true"><!-- 缓存的三级开关 语句级,默认开启 -->
select d.*,e.* from dept d inner join emp e on d.dept_id = e.dept_id
where d.dept_name = #{deptName}
</select>
<select id="selectDeptById" parameterType="string" resultMap="deptExtResultMap">
select d.*,e.* from dept d inner join emp e on d.dept_id = e.dept_id
where d.dept_id = #{deptId}
</select>
</mapper>
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="cn.ustb.entity.EmpMapper">
<resultMap type="Emp" id="empResultMap">
<id column="emp_id" property="empId" />
<result column="emp_name" property="empName" />
<result column="emp_age" property="empAge" />
<result column="emp_gender" property="empGender" />
</resultMap>
<resultMap type="Emp" id="empExtResultMap" extends="empResultMap">
<association property="dept" column="dept_id"
resultMap="cn.ustb.entity.DeptMapper.deptResultMap"></association>
<!-- 同样,引用的是deprResultMap,没有配置多的属性 -->
</resultMap>
<select id="selectEmp" parameterType="string" resultMap="empExtResultMap">
select
e.*,d.* from emp e inner join dept d on e.dept_id = d.dept_id
where
e.emp_name = #{empName}
</select>
</mapper>
编写数据操作类:DeptDaoImpl.java/EmpDaoImpl.java
DeptDaoImpl.java,查询部门员工信息,返回类型为List,关键代码:
public List<Dept> selectDeptEmpList(Dept dept){
SqlSession session=null;
List<Dept> deps=null;
try{
session=MyBatisUtil.getSession();
deps=session.selectList("cn.ustb.entity.DeptMapper.selectDeptEmpList",dept);
session.commit();
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
session.rollback();
}finally{
MyBatisUtil.closeSession();
}
return deps;
}
EmpDaoImpl.java查询员工及其所在部门信息,返回类型为List< Emp >,关键代码
public List<Emp> selectEmpDeptList(Emp emp){
SqlSession session=null;
List<Emp> emps=null;
try{
session=MyBatisUtil.getSession();
emps=session.selectList("cn.ustb.entity.EmpMapper.selectEmpDeptList",emp);
session.commit();
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
session.rollback();
}finally{
MyBatisUtil.closeSession();
}
return emps;
}
缓存
Mybatis和hibernate一样,也使用缓存;缓存分为一级缓存和二级缓存,一级缓存指在SqlSession内共享数据;二级缓存能被所有的SqlSession共享。
全局缓存配置(一级缓存)
在mybatis-config.xml的主配置文件中进行配置:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 默认有启用全局缓存的,禁用可以把value设为false,如果这里设为false,Mapper.xml或SQL语句级的缓存配置不再起作用 -->
<setting name="cacheEnabled" value="true"/>
</settings>
<!—省略其它配置信息 -->
</configuration>
Mapper文件级缓存配置(二级缓存)
使用二级缓存机制:需要开启全局缓存,文件级缓存 ,语句级缓存,才能使用二级缓存。默认情况下文件级缓存没有开启。
<!-- 缓存的第二级开关,写上即开启 -->
<!-- 一级默认开启,二级默认关闭,三级默认开启 -->
<cache eviction="LRU" size="2" readOnly="false"></cache>
关于二级缓存的配置,在使用者手册中有详细说明,摘取如下内容:
可用的回收算法如下:
• LRU – 最近最少使用:移出最近最长时间内都没有被使用的对象。
• FIFO – 先进先出:移除最先进入缓存的对象。
• SOFT – 软引用: 基于垃圾回收机制和软引用规则来移除对象(空间内存不足时才进行回收)。
• WEAK – 弱引用: 基于垃圾回收机制和弱引用规则(垃圾回收器扫描到时即进行回收)。
默认使用LRU。flushInterval :设置任何正整数,代表一个以毫秒为单位的合理时间。默认是没有设置,因此 没有刷新间隔时间被使用,在语句每次调用时才进行刷新。
Size:属性可以设置为一个正整数,您需要留意您要缓存对象的大小和环境中可用的内存空间。 默认是1024。
readOnly:属性可以被设置为true 或false。只读缓存将对所有调用者返回同一个实例。因此这些对象都不能被修改,这可以极大的提高性能。可写的缓存将通过序列化来返回一个缓存对象的拷贝。这会比较慢,但是比较安全。所以默认值是false。
缓存配置分析:
<!--
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="false"/>
创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会 导致冲突。可用的收回策略有以下几种, 默认的是 LRU:
1. LRU – 最近最少使用的:移除最长时间不被使用的对象。
2. FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
3. SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
4. WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
flushInterval:刷新间隔时间,可以被设置为任意的正整数,单位毫秒。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size:内存资源数目,可以被设置为任意正整数。默认值是1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓 存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。
-->
<cache eviction="LRU" size="2" readOnly="false" />
SQL语句级缓存配置
在相关的Mapper.xml文件中配置SQL查询,关键代码如下(示例):
<!-- useCache默认值为true,设为false时缓存不起作用 -->
<select id="selectOne" parameterType="int" resultMap="deptResultMap" useCache="true" >
select * from dept where dept_id=#{id}
</select>
XML 中的特殊字符处理
如果 MyBatis 使用 XML 配置,那不可避免地会遇到一些对 XML 来说是特殊的字符。如小于号 “<”,因此要进行转义。主要有两个方式:
使用转义实体
下面是五个在 XML 文档中预定义好的转义实体:
- <; < 小于号
- >; > 大于号
- &; &
- &apos; ’ 单引号
- "; ” 双引号
- 小于等于“<=”,其转义为:<;=
- 大小等于“>=”,转义为:>;=
使用 CDATA 部件
CDATA 部件以”
<select id= "selectBlog_use_collection" resultMap= "blogResult" >
<![CDATA[ SELECT id , title, author_id as authored FROM BLOG WHERE ID > 0 and ID < 10 ]]> </select>
“