#{ } 和 ${ } 取值的区别
#{} : 是以预编译的形式,将参数设置到sql语句中,防止sql注入;${} : 取出的值会直接拼接在sql语句中,会有安全问题;
resultMap 自定义结果
resultType和resultMap只能同时用一个
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.garcon.dao.StudentMapper"><!--自定义某个javaBean的封装规则type:自定义规则的Java类型id:唯一id方便引用--><resultMap type="com.garcon.bean.Student" id="myStudent"><!--指定主键列的封装规则id定义主键会底层有优化column:指定哪一列property:指定对应的javaBean属性--><id column="sid" property="sid"/><!-- 定义普通列封装规则 --><result column="last_name" property="lastName"/><!-- 其他不指定的列会自动封装:我们只要写resultMap就把全部的映射规则都写上。 --><result column="gender" property="gender"/><result column="hobby" property="hobby"/></resultMap><!-- resultMap:自定义结果集映射规则; --><select id="getStudentById" resultMap="myStudent">select * from student where sid=#{sid}</select></mapper>
关联查询
- 自定义
关联查询结果集类型 - 查询
学生信息的时候把对应的班级信息也查询出来 Student.java学生信息
package com.garcon.bean;public class Student {private Integer sid;private String lastName;private String gender;private String hobby;private Sclass sclass;//班级信息...}
Sclass.java班级信息
package com.garcon.bean;public class Sclass {private Integer cid;//班级idprivate String cName;//班级名称...}
StudentMapper.java接口
package com.garcon.dao;public interface StudentMapper {//以sid查询学生信息public Student getStudentById(Integer sid);}
StudentMapper.xml映射文件——resultMap级联属性
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.garcon.dao.StudentMapper"><!--联合查询:级联属性封装结果集 --><resultMap type="com.garcon.bean.Student" id="mystudent"><id column="sid" property="sid"/><result column="last_name" property="lastName"/><result column="gender" property="gender"/><result column="hobby" property="hobby"/><!--关联列用级联属性--><result column="cid" property="sclass.cid"/><result column="cname" property="sclass.name"/></resultMap><!--级联查询--><select id="getStudentById" resultMap="myStudent">SELECTs.sid,s.last_name,s.gender,s.hobby,c.cid,c.cnameFROMEstudent s,sclass cWHEREs.sid=c.cidANDsid=#{sid}</select></mapper>
或 以下
StudentMapper.xml映射文件——resultMap的association属性
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.garcon.dao.StudentMapper"><!--使用association定义关联的单个对象的封装规则--><resultMap type="com.garcon.bean.Student" id="myStudent"><id column="sid" property="sid"/><result column="last_name" property="lastName"/><result column="gender" property="gender"/><result column="hobby" property="hobby"/><!--association:可以指定联合的javaBean对象property="sclass":指定哪个属性是联合的对象javaType:指定这个属性对象的类型[不能省略]--><association property="sclass" javaType="com.garcon.bean.Sclass"><id column="cid" property="cid"/><result column="cname" property="cname"/></association></resultMap><!--关联查询--><select id="getStudentById" resultMap="myStudent">SELECTs.sid,s.last_name,s.gender,s.hobby,c.cid,c.cnameFROMEstudent s,sclass cWHEREs.sid=c.cidANDsid=#{sid}</select></mapper>
关联查询 collection
package com.garcon.bean;public class Student {private Integer sid;private String lastName;private String gender;private String hobby;private Sclass sclass;//班级信息...}
Sclass.java班级类
package com.garcon.bean;public class Sclass {private Integer cid;//班级idprivate String cName;//班级名称private List<Student> students;...}
SclassMapper.java班级接口
package com.garcon.dao;public interface SclassMapper {//以班级id查询班级public Sclass getSclassById(Integer cid);}
sclassMapper.xml班级SQL映射文件
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.garcon.dao.SclassMapper"><!--嵌套结果集的方式,使用collection标签定义关联的集合类型的属性封装规则 --><resultMap type="com.garcon.bean.Sclass" id="Mysclass"><id column="cid" property="cid"/><result column="cname" property="cName"/><!--collection定义关联集合类型的属性的封装规则ofType:指定集合里面元素的类型--><collection property="students" ofType="com.garcon.bean.Student"><!-- 定义这个集合中元素的封装规则 --><id column="sid" property="sid"/><result column="last_name" property="lastName"/><result column="gender" property="gender"/><result column="hobby" property="hobby"/></collection></resultMap><select id="getSclassById" resultMap="Mysclass">SELECTs.sid,s.last_name,s.gender,s.hobyy,c.cidc.cnameFROMstudent sLEFT JOINsclass cONs.cid=c.cidWHEREc.cid=#{cid}</select></mapper>
分步查询 association
- 先以学生的
学号查询学生信息,再以学生信息中的班级编号查询班级信息 SclassMapper.java班级接口
package com.garcon.dao;public interface Sclass {//以班级id查询班级信息public Sclass getSclasstById(Integer cid);}
StudentMapper.java学生接口
package com.garcon.dao;public interface StudentMapper {//以sid查询学生信息public Student getStudentById(Integer sid);}
sclassMapper.xml班级SQL映射文件
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.garcon.dao.SclassMapper"><!--以班级号查询班级信息--><select id="getSclassById" resultType="com.garcon.bean.Sclass">SELECT * FROM sclass WHERE cid=#{cid}</select></mapper>
studentMapper.xml学生SQL映射文件
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.garcon.dao.StudentMapper"><!--使用association进行分步查询:1、先按照学生id查询学生工信息2、根据查询学生信息中的cid值去班级表查出班级信息3、班级设置到学生中;--><resultMap type="com.garcon.bean.Student" id="Mystudent"><id column="sid" property="sid"/><result column="last_name" property="lastName"/><result column="gender" property="gender"/><result column="hobby" property="hobby"/><!--association:定义关联对象的封装规则select:表明当前属性是调用select指定的方法查出的结果column:指定将哪一列的值传给这个方法流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性--><association property="dept"select="com.garcon.dao.SclassMapper.getSclassById"column="cid"></association></resultMap><select id="getStudentById" resultMap="Mystudent">SELECT * FROM student WHERE sid=#{sid}</select></mapper>
分步查询 collection
- 先以
班级编号查询班级信息,再以班级编号查询班级的所有学生信息 SclassMapper.java班级接口
package com.garcon.dao;public interface Sclass {//以班级id查询班级信息public Sclass getSclasstById(Integer cid);}
StudentMapper.java学生接口
package com.garcon.dao;public interface StudentMapper {//以班级编号查询学生信息public List<Student> getStudentByClassId(Integer sid);}
sclassMapper.xml班级SQL映射文件
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.garcon.dao.SclassMapper"><!-- collection:分段查询 --><resultMap type="com.garcon.bean.Sclass" id="mySclass"><id column="cid" property="cid"/><id column="cname" property="cName"/><collection property="students"select="com.garcon.dao.StudentMapper.getStudentByClassId"column="cid" fetchType="lazy"></collection></resultMap><select id="getSclasstById" resultMap="mySclass">select cid,cName from sclass where cid=#{cid}</select><!--扩展:多列的值传递过去:将多列的值封装map传递;column="{key1=column1,key2=column2}"如:<collectionproperty=""select=""column="{key1=column1,key2=column2}"fetchType="lazy"></collection>fetchType="lazy":表示使用延迟加载;- lazy:延迟- eager:立即--></mapper>
studentMapper.xml学生SQL映射文件
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.garcon.dao.StudentMapper"><!--以班级编号查询所有学生--><select id="getStudentById" resultType="com.garcon.bean.Student">SELECT * FROM student WHERE class_id=#{class_id}</select></mapper>
分步查询 延迟加载
适用情况:假如查看学生信息的时候不加载班级信息,当需要查看学生的班级信息的时候再加载班级信息
- 在分步查询的基础上,在mybatis的全局配置文件的settings设置项中添加如下设置即可
<settings><!--显示的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题 --><setting name="lazyLoadingEnabled" value="true"/><setting name="aggressiveLazyLoading" value="false"/></settings>
鉴别器-discriminator
鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
- 如果查出的是女生:就把班级信息查询出来,否则不查询;如果是男生,把last_name这一列的值赋值给 hobby;
studentMapper.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.garcon.dao.StudentMapper"><!--鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为封装Student:如果查出的是女生:就把班级信息查询出来,否则不查询;如果是男生,把last_name这一列的值赋值给 hobby;--><resultMap type="com.garcon.bean.Student" id="mystudent"><id column="sid" property="sid"/><result column="last_name" property="lastName"/><result column="gender" property="gender"/><result column="hobby" property="hobby"/><!--column:指定判定的列名javaType:列值对应的java类型--><discriminator javaType="string" column="gender"><!--女生 resultType:指定封装的结果类型;不能缺少。/resultMap--><case value="女" resultType="com.garcon.bean.Student"><association property="sclass"select="com.garcon.dao.SclassMapper.getSclasstById"column="class_id"></association></case><!--男生 ;如果是男生,把last_name这一列的值赋值给hobby; --><case value="男" resultType="com.garcon.bean.Student"><id column="id" property="id"/><result column="last_name" property="lastName"/><result column="last_name" property="hobby"/><result column="gender" property="gender"/></case></discriminator></resultMap>
sclassMapper.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.garcon.dao.SclassMapper"><!--以班级号查询班级信息--><select id="getSclassById" resultType="com.garcon.bean.Sclass">SELECT * FROM sclass WHERE cid=#{cid}</select></mapper>
动态SQL
if
<!--test:判断表达式(OGNL)--><if test=""></if><!--应用场境:按条件查询学生,如果参数中有sid则查询条件带上sid如果参数中有last_name则查询条件带上last_name如果参数中有gender则查询条件带上gender--><select id="getStudent" resultType="com.garcon.dao.StudentMapper">select * from student<!-- where --><where><if test="sid!=null">sid=#{sid}</if><if test="lastName!=null AND lastName!=''">and last_name like #{lastName}</if><if test="hobby!=null and hobby.trim()!=''">and hobby=#{hobby}</if><!-- ognl会进行字符串与数字的转换判断 "0"==0 --><if test="gender==0 or gender==1">and gender=#{gender}</if></where></select>
where
- 去除查询条件中多余的第一个
and或or
<!--检查以下sql语句--><select id="getStudent" resultType="com.garcon.dao.StudentMapper">select * from studentwhere<if test="sid!=null">sid=#{sid}</if><if test="lastName!=null AND lastName!=''">and last_name like #{lastName}</if></select><!--可以发现,当以上参数 sid 为空时,sql语句就变成了:select * from student where and last_name=?多出的 and 语法错误;解决以上情况可以加标签<where></where>:把所有的<if></if>条件都放在<where></where>标签中,可以去除第一个 and 和 or 如下:--><select id="getStudent" resultType="com.garcon.dao.StudentMapper">select * from student<where><if test="sid!=null">sid=#{sid}</if><if test="lastName!=null AND lastName!=''">and last_name like #{lastName}</if></where></select>
trim
<!--检查以下sql语句--><select id="getStudent" resultType="com.garcon.dao.StudentMapper">select * from studentwhere<if test="sid!=null">sid=#{sid} and</if><if test="lastName!=null AND lastName!=''">last_name like #{lastName}</if></select><!--可以发现,当以上参数 lastName 为空时,sql语句就变成了:select * from student where sid=? and结尾多出的 and 语法错误;解决以上情况可以加标签<trim></trim>:<trim prefix="" suffixOverrides="" suffix="" suffixOverrides=""></trim>后面多出的and或者or where标签不能解决prefix="":前缀:trim标签体中是整个字符串拼串 后的结果。prefix给拼串后的整个字符串加一个前缀prefixOverrides="":前缀覆盖: 去掉整个字符串前面多余的字符suffix="":后缀suffix给拼串后的整个字符串加一个后缀suffixOverrides=""后缀覆盖:去掉整个字符串后面多余的字符如下:--><select id="getStudent" resultType="com.garcon.dao.StudentMapper">select * from student<trim prefix="where" suffixOverrides="and"><if test="sid!=null">sid=#{sid} and</if><if test="lastName!=null AND lastName!=''">last_name like #{lastName}</if></trim></select>
choose
<!--使用场景:如果参数中的id不为空就以id为条件查询,如果name不为空就已以name做查询条件如下:--><select id="getStudent" resultType="com.garcon.dao.StudentMapper">select * from student<where><!-- 如果带了id就用id查,如果带了lastName就用lastName查;只会进入其中一个 --><choose><when test="id!=null">sid=#{sid}</when><when test="lastName!=null">last_name like #{lastName}</when><when test="hobby!=null">hobby = #{hobby}</when><!--如果以上条件都不满足,使用如下条件--><otherwise>gender = '女'</otherwise></choose></where></select>
update-set
如果javaBean中哪个属性带了值就更新那个字段
<update id="updateStudent"><!-- Set标签的使用 -->update student<set><if test="lastName!=null">last_name=#{lastName},</if><if test="hobby!=null">hobby=#{hobby},</if><if test="gender!=null">gender=#{gender}</if></set>where id=#{id}</update>
update-trim
<update id="updateStudent">update student<trim prefix="set" suffixOverrides=","><if test="lastName!=null">last_name=#{lastName},</if><if test="email!=null">email=#{email},</if><if test="gender!=null">gender=#{gender}</if></trim>where id=#{id}</update>
select-foreach
<select id="getStudent" resultType="com.garcon.bean.Student">select * from student<!--collection:指定要遍历的集合:list类型的参数会特殊处理封装在map中,map的key就叫listitem:将当前遍历出的元素赋值给指定的变量separator:每个元素之间的分隔符open:遍历出所有结果拼接一个开始的字符close:遍历出所有结果拼接一个结束的字符index:索引。遍历list的时候是index就是索引,item就是当前值遍历map的时候index表示的就是map的key,item就是map的值#{变量名}就能取出变量的值也就是当前遍历出的元素--><foreach collection="ids" item="item_id" separator=","open="where id in(" close=")">#{item_id}</foreach></select>
insert-foreach
批量插入
<!--MySQL下:第一种方法可以foreach遍历 mysql支持values(),(),()语法--><insert id="addStudents">insert into student(last_name,gender,hobby,class_id)values<foreach collection="students" item="student" separator=",">(#{student.lastName},#{student.gender},#{student.hobby},#{student.sclass.cid})</foreach></insert><!--第二种方法这种方式需要数据库连接属性allowMultiQueries=true;这种分号分隔多个sql可以用于其他的批量操作(删除,修改) --><insert id="addEmps"><foreach collection="emps" item="emp" separator=";">insert into student(last_name,gender,hobby,class_id)values((#{student.lastName},#{student.gender},#{student.hobby},#{student.sclass.cid})</foreach></insert><!--Oracle数据库批量保存:Oracle不支持values(),(),()Oracle支持的批量方式1、多个insert放在begin - end里面begininsert into employees(employee_id,last_name,email)values(employees_seq.nextval,'test_001','test_001@atguigu.com');insert into employees(employee_id,last_name,email)values(employees_seq.nextval,'test_002','test_002@atguigu.com');end;2、利用中间表:insert into employees(employee_id,last_name,email)select employees_seq.nextval,lastName,email from(select 'test_a_01' lastName,'test_a_e01' email from dualunionselect 'test_a_02' lastName,'test_a_e02' email from dualunionselect 'test_a_03' lastName,'test_a_e03' email from dual)oracle第一种批量方式--><insert id="addEmps" databaseId="oracle"><foreach collection="emps" item="emp" open="begin" close="end;">insert into employees(employee_id,last_name,email)values(employees_seq.nextval,#{emp.lastName},#{emp.email});</foreach></insert><!-- oracle第二种批量方式 --><insert id="addEmps" databaseId="oracle">insert into employees(employee_id,last_name,email)<foreach collection="emps" item="emp" separator="union"open="select employees_seq.nextval,lastName,email from("close=")">select #{emp.lastName} lastName,#{emp.email} email from dual</foreach></insert>
获取自增主键的值
方式一 :
使用insert标签的属性useGeneratedKeys="true" keyProperty="id"
<insert id="addUser" useGeneratedKeys="true" keyProperty="id">insert into user(username,password,email) values(#{username},#{password},#{email})</insert>
方式二 : 插入时先查询将要插入数据的自增id值
<insert id="addUser" useGeneratedKeys="true" keyProperty="id"><selectKey order="AFTER" keyProperty="id" resultType="int">select last_insert_id()</selectKey>insert into user(username,password,email) values(#{username},#{password},#{email})</insert>
参数处理
单个参数
可以接受基本类型,对象类型,集合类型的值。这种情况 MyBatis可直接使用这个参数,不需要经过任何处理。
多个参数
任意多个参数,都会被MyBatis重新包装成一个Map传入。Map的key是param1,param2,0,1…,值就是参数的值。
命名参数
为参数使用@Param起一个名字,MyBatis就会将这些参数封装进map中,key就是我们自己指定的名字
POJO
当这些参数属于我们业务POJO时,我们直接传递POJO
Map
我们也可以封装多个参数为map,直接传递
注意 : 当参数是一个Connection , List , Array数组时, 参数会封装成一个Map , key值为 : connection , list , array .
缓存机制
MyBatis 包含一个非常强大的查询缓存特性, 它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存。
一级缓存 和 二级缓存。
- 默认情况下,只有一级缓存(SqlSession级别的缓存, 也称为本地缓存)开启。
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
- 为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存
- 一级缓存(local cache), 即本地缓存, 作用域默认 为sqlSession。当 Session flush 或 close 后, 该Session 中的所有 Cache 将被清空。
- 本地缓存不能被关闭, 但可以调用 clearCache() 来清空本地缓存, 或者改变缓存的作用域.
- 在mybatis3.1之后, 可以配置本地缓存的作用域. 在 mybatis.xml 中配置
一级缓存失效情况
同一次会话期间只要查询过的数据都会保存在当 前SqlSession的一个Map中
• key:hashCode+查询的SqlId+编写的sql查询语句+参数
一级缓存失效的四种情况
- 不同的SqlSession对应不同的一级缓存
- 同一个SqlSession但是查询条件不同
- 同一个SqlSession两次查询期间执行了任何一次增 删改操作
- 同一个SqlSession两次查询期间手动清空了缓存
二级缓存(second level cache),全局作用域缓存
- 二级缓存默认不开启,需要手动配置
- MyBatis提供二级缓存的接口以及实现,缓存实现要求 POJO实现Serializable接口
- 二级缓存在 SqlSession 关闭或提交之后才会生效
使用步骤
- 全局配置文件中开启二级缓存
<setting name="cacheEnabled" value="true"/>
- 在需要使用二级缓存的映射文件处使用cache配置缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache><!-- <cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache> --><!--eviction:缓存的回收策略:• LRU – 最近最少使用的:移除最长时间不被使用的对象。• FIFO – 先进先出:按对象进入缓存的顺序来移除它们。• SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。• WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。• 默认的是 LRU。flushInterval:缓存刷新间隔缓存多长时间清空一次,默认不清空,设置一个毫秒值readOnly:是否只读:true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快false:非只读:mybatis觉得获取的数据可能会被修改。mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢size:缓存存放多少元素;type="":指定自定义缓存的全类名;实现Cache接口即可;-->
- 注意:POJO需要实现Serializable接口
缓存相关设置
- 全局setting的cacheEnable:– 配置二级缓存的开关。一级缓存一直是打开的。
- select标签的useCache属性: – 配置这个select是否使用二级缓存。一级缓存一直是使用的
- sql标签的flushCache属性: – 增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。 查询默认flushCache=false。
- sqlSession.clearCache(): – 只是用来清除一级缓存。
- 当在某一个作用域 (一级缓存Session/二级缓存 Namespaces) 进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。
第三方缓存整合
- EhCache 是一个纯Java的进程内缓存框架,具有快速、精 干等特点,是Hibernate中默认的CacheProvider。
- MyBatis定义了Cache接口方便我们进行自定义扩展。
步骤:
- 导入ehcache包,以及整合包,日志包
ehcache-core-2.6.8.jarmybatis-ehcache-1.0.3.jarslf4j-api-1.6.1.jarslf4j-log4j12-1.6.2.jar
- 编写ehcache.xml配置文件
- 配置cache标签
<ehcache type="org.mybatis.caches.ehcache.EhcacheCache">
参照缓存:若想在命名空间中共享相同的缓存配置和实例。 可以使用 cache-ref 元素来引用另外一个缓存。
