1.动态Sql
1.1:判断元素:if元素,choose元素
1.2:拼关键字:where元素,set元素,trim元素
1.3:循环:foreach元素(list,array,Map)
eg:<?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="dao.TstudentMapper">
<!--配置resultmap结果集-->
<resultMap id="tstudentMap" type="bean.Tstudent">
<id column="sid" jdbcType="INTEGER" property="sid" />
<result column="sname" jdbcType="VARCHAR" property="sname" />
<result column="sage" jdbcType="INTEGER" property="sage" />
<result column="sex" jdbcType="CHAR" property="sex" />
<result column="address" jdbcType="VARCHAR" property="address" />
<result column="cid" jdbcType="INTEGER" property="cid" />
</resultMap>
<select id="findBystudent1" parameterType="bean.Tstudent" resultMap="tstudentMap">
<!-- select sid,sname,sage,sex,address,cid from t_student where 1=1-->
<!-- <!–test后面条件判断的是传入参数的属性名–>-->
<!-- <if test="sage!=null">-->
<!-- and sage=
<!-- </if>-->
<!-- <if test="sex!=null">-->
<!-- and sex=
<!-- </if>-->
select sid,sname,sage,sex,address,cid from t_student
<!--where标签可以去除多余and关键字-->
<where>
<!--test后面条件判断的是传入参数的属性名-->
<if test="sage!=null">
and sage=
</if>
<if test="sex!=null">
and sex=
</if>
</where>
</select>
<select id="findByAgeAndSex" parameterType="bean.Tstudent" resultMap="tstudentMap">
select sid,sname,sage,sex,address,cid from t_student where sname=
<!--test后面条件判断的是传入参数的属性名-->
<choose>
<when test="sage!=null">
and sage=
</when>
<when test="sex!=null">
and sex=
</when>
</choose>
</select>
<update id="updateStudent" parameterType="bean.Tstudent">
update t_student
<!--set标签可以去掉多余逗号,set标签中所有条件都不满足,就会报错-->
<set>
<if test="sname!=null">
sname=
</if>
<if test="sage!=null">
sage=
</if>
<if test="sex!=null">
sex=
</if>
<if test="address!=null">
address=
</if>
<if test="cid!=null">
cid=
</if>
</set>
where sid=
</update>
<insert id="insertStudent" parameterType="bean.Tstudent">
insert into t_student
<!--trim标签可以拼sql,prefix表示拼接开始,suffix拼接结束,suffixOverrides表示可以忽略条件中结尾多余逗号,prefixOverrides表示可以忽略条件中开始多余xx-->
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="sname!=null">
sname,
</if>
<if test="sage!=null">
sage,
</if>
<if test="sex!=null">
sex,
</if>
<if test="address!=null">
address,
</if>
<if test="cid!=null">
cid,
</if>
</trim>
<trim prefix="values(" suffix=")" suffixOverrides=",">
<if test="sname!=null">
</if>
<if test="sage!=null">
</if>
<if test="sex!=null">
</if>
<if test="address!=null">
</if>
<if test="cid!=null">
</if>
</trim>
</insert>
<!--用foreach操作list集合的参数-->
<select id="findStudentByIds1" parameterType="java.util.ArrayList" resultMap="tstudentMap">
select sid,sname,sage,sex,address,cid from t_student where sid in
<!--collection指定是参数是集合,还是数组,还map集合.如果是list集合,collection的值为list;如果是数组,collection的值为array;如果是map集合,collection的值为map的key;-->
<!--item属性表示集合或数组中每个遍历的当前元素,index当前遍历的元素的索引,open参数开始拼接,close参数结束拼接,separator参数与参数之间分隔符-->
<foreach collection="list" item="item1" index="index1" open="(" close=")" separator="," >
</foreach>
</select>
<select id="findStudentByIds2" resultMap="tstudentMap">
select sid,sname,sage,sex,address,cid from t_student where sid in
<foreach collection="array" item="item2" index="index2" open="(" close=")" separator=",">
</foreach>
</select>
<select id="findStudentByIds3" parameterType="java.util.Map" resultMap="tstudentMap">
select sid,sname,sage,sex,address,cid from t_student where sid in
<foreach collection="mapkey1" item="item3" index="index3" open="(" close=")" separator=",">
</foreach>
</mapper>
/**
* 根据条件(指定几个sid)查询学生信息,参数list集合
*/
@Test
public void findStudentByIds1(){
//准备集合存要查询学生编号
List<Integer> ids=new ArrayList<>();
ids.add(1);
ids.add(11);
ids.add(21);
ids.add(31);
//创建会话对象
SqlSession session=factory.openSession();
//创建接口映射器
TstudentMapper tm=session.getMapper(dao.TstudentMapper.class);
//用映射器调用接口方法
List<Tstudent> slist=tm.findStudentByIds1(ids);
slist.stream().forEach(System.out::println);
}
/**
* 根据条件(指定几个sid)查询学生信息,参数数组
*/
@Test
public void findStudentByIds2(){
//准备数组存要查询学生编号
Integer[] ids={2,12,22,32};
//创建会话对象
SqlSession session=factory.openSession();
//创建接口映射器
TstudentMapper tm=session.getMapper(dao.TstudentMapper.class);
//用映射器调用接口方法
List<Tstudent> slist=tm.findStudentByIds2(ids);
slist.stream().forEach(System.out::println);
}
/**
* 根据条件(指定几个sid)查询学生信息,参数map集合
*/
@Test
public void findStudentByIds3(){
//准备集合存要查询学生编号
List<Integer> ids=new ArrayList<>();
ids.add(1);
ids.add(11);
ids.add(21);
ids.add(31);
Map<String,List<Integer>> hmap=new HashMap();
hmap.put("mapkey1",ids);
//创建会话对象
SqlSession session=factory.openSession();
//创建接口映射器
TstudentMapper tm=session.getMapper(dao.TstudentMapper.class);
//用映射器调用接口方法
List<Tstudent> slist=tm.findStudentByIds3(hmap);
slist.stream().forEach(System.out::println);
}
2.MyBatis关联映射
2.1:主键映射:在插入数据时,获得主键.
注意:为了保证sql效率,如果不需要自动生成的主键,可以不用主键.如果需要 主键,用主键映射.
<!--第一种:主键映射,在插入数据时,获得刚生成的主键-->
<insert id="insertStudent1" parameterType="bean.Tstudent">
<selectKey keyProperty="sid" resultType="int" order="AFTER">
select last_insert_id()
</selectKey>
insert into t_student(sname,sage,sex,address,cid)
values(
</insert>
<!--第二种:主键映射,在插入数据时,获得刚生成的主键-->
<insert id="insertStudent2" parameterType="bean.Tstudent" useGeneratedKeys="true" keyProperty="sid">
insert into t_student(sname,sage,sex,address,cid)
values(
</insert>
2.2:关联映射:根据表中公共列,将公共列映射成当前类的一个对象属性.在查询当前对象时, 顺带查询对象属性.eg:查询学生信息时,顺带查询与学生相关班级信息
2.2.1:嵌套查询:查询多次.缺点:n+1查询,查询效率比较低.
eg:<!--配置resultmap结果集-->
<resultMap id="tstudentMap" type="bean.Tstudent">
<id column="sid" jdbcType="INTEGER" property="sid" />
<result column="sname" jdbcType="VARCHAR" property="sname" />
<result column="sage" jdbcType="INTEGER" property="sage" />
<result column="sex" jdbcType="CHAR" property="sex" />
<result column="address" jdbcType="VARCHAR" property="address" />
<!--关联映射第一种:嵌套查询-->
<!--association表示当前的属性是一个对象属性,property属性名,javaType属性全限制类型,column表示当前的属性是通过这个列映射过来,select表示查询当前这个对象属性的sql语句的id名-->
<association property="tclass" javaType="bean.Tclass" column="cid" select="selectClassByCid">
</association>
</resultMap>
<!--关联映射第一种:嵌套查询--><!--前提:Tclass的属性名与查询出列名一致-->
<select id="selectClassByCid" parameterType="int" resultType="bean.Tclass">
select cid,cname from t_class where cid=
</select>
<select id="findStudentByid1" parameterType="int" resultMap="tstudentMap">
select sid,sname,sage,sex,address,cid from t_student where sid=
</select>
2.2.2:嵌套结果:只查询一次,联表查询.
<!--配置resultmap结果集-->
<resultMap id="tstudentMap" type="bean.Tstudent">
<id column="sid" jdbcType="INTEGER" property="sid" />
<result column="sname" jdbcType="VARCHAR" property="sname" />
<result column="sage" jdbcType="INTEGER" property="sage" />
<result column="sex" jdbcType="CHAR" property="sex" />
<result column="address" jdbcType="VARCHAR" property="address" />
<!--关联映射第二种:嵌套结果-->
<!--association表示当前的属性是一个对象属性,property属性名,javaType属性全限制类型,column表示当前的属性是通过这个列映射过来,select表示查询当前这个对象属性的sql语句的id名-->
<association property="tclass" javaType="bean.Tclass" column="cid">
<id column="cid" property="cid"></id>
<result column="cname" property="cname"></result>
</association>
</resultMap>
<select id="findStudentByid2" parameterType="int" resultMap="tstudentMap">
select s.sid,s.sname,s.sage,s.sex,s.address,c.cid,c.cname
from t_student s
inner join t_class c on s.cid=c.cid
where sid=
</select>
2.3:集合映射
2.3.1:嵌套查询:执行n+1查询,效率低.
eg:<!--配置resultmap结果集-->
<resultMap id="tclassMap" type="bean.Tclass">
<id column="cid" jdbcType="INTEGER" property="cid" />
<result column="cname" jdbcType="VARCHAR" property="cname" />
<!--集合映射的第一种:嵌套查询-->
<!--collection标签表示当前属性是集合属性,property表示属性名,javaType表示当前属性类型,ofType表示当前集合属性中每个元素的类型,column公共列名,select表示查询关联数据时调用sql的名-->
<collection property="slist" javaType="java.util.ArrayList" ofType="bean.Tstudent" column="cid" select="selectStudentsByCid">
</collection>
</resultMap>
<!--集合映射的第一种:嵌套查询-->
<select id="selectStudentsByCid" parameterType="int" resultType="bean.Tstudent">
select sid,sname,sage,sex,address,cid from t_student where cid=
</select>
<select id="findClassById1" parameterType="int" resultMap="tclassMap">
select cid,cname from t_class where cid=
</select>
2.3.2:嵌套结果:联表查询
eg:<!--配置resultmap结果集-->
<resultMap id="tclassMap" type="bean.Tclass">
<id column="cid" jdbcType="INTEGER" property="cid" />
<result column="cname" jdbcType="VARCHAR" property="cname" />
<!--集合映射的第二种:嵌套结果-->
<!--collection标签表示当前属性是集合属性,property表示属性名,javaType表示当前属性类型,ofType表示当前集合属性中每个元素的类型,column公共列名,select表示查询关联数据时调用sql的名-->
<collection property="slist" javaType="java.util.ArrayList" ofType="bean.Tstudent" column="cid">
<id column="sid" jdbcType="INTEGER" property="sid" />
<result column="sname" jdbcType="VARCHAR" property="sname" />
<result column="sage" jdbcType="INTEGER" property="sage" />
<result column="sex" jdbcType="CHAR" property="sex" />
<result column="address" jdbcType="VARCHAR" property="address" />
</collection>
</resultMap>
<select id="findClassById2" parameterType="int" resultMap="tclassMap">
select c.cid,c.cname,s.sid,s.sname,s.sage,s.sex,s.address
from t_class c
inner join t_student s on c.cid=s.cid
where c.cid=
</select>
2.4:在Mybatis连表查询的结果有几种接收结果的方式:
2.4.1:对象接收结果:输出模板对象.pojo对象(与表对象实体对象,公共列映射成对象 或集合).
2.4.2:用Map接收结果:List<Map<String,Object>>第一行结果对应一个Map集合,有多 行结果就有多个map集合,共同组成list集合.
3.性能优化 
3.1:延迟加载:只查询主要数据,在真正使用相关联数据的时候才发起查询,不用的时候不查询关联的数据,延迟加载又叫按需查询(懒加载)
第一步:在核心配置文件中设置延迟加载
<!-- 全局参数配置:延迟加载 -->
<settings>
<!--配置相关联的懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--属性按需要加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
第二步:在sql映射文件中,用嵌套查询来实现延迟加载.
注意:在输出对象时,或者调用toString时会触发及时加载,失懒加载失效
<!--指定对象的方法(默认值:equals,clone,hashCode,toString)触发一次延迟加载。-->
<setting name="lazyLoadTriggerMethods" value="" />
3.2:使用association和collection进行延迟加载:嵌套查询,先查询主要数据,如果需要关联数据时,再调用嵌套的sql来查询关联数据.
3.3:一级缓存(SqlSession):默认启动
3.3.1:一级缓存的执行原理图 
3.3.2:一级缓存区原理: 
3.3.3:一级缓存的失效:
当用户执行添加,修改,删除sql后调用commit时,清空一级缓存;
当调用sqlSession对象的close()时,清空一级缓存.
eg:/**
* 测试一级缓存
*/
@Test
public void sessionMethod1(){
//创建会话对象
SqlSession session=factory.openSession();
//创建接口映射器
TstudentMapper tm=session.getMapper(dao.TstudentMapper.class);
//第一次查询编号为3的学生信息,缓存中没有,从数据库中查询,执行查询的sql语句
Tstudent stu3=tm.findStudentByid1(3);
System.out.println("学生信息为:"+stu3);
/*因为执行插入语句后调用了commit,清空了一级缓存sqlSession*/
//准备学生对象
Tstudent stu1=new Tstudent();
stu1.setSname("亮哥");
stu1.setSage(20);
stu1.setSex("男");
stu1.setAddress("深圳");
Tclass tclass=new Tclass();
tclass.setCid(1);
stu1.setTclass(tclass);
//用映射器调用接口方法
int result=tm.insertStudent2(stu1);
//提交事务
session.commit();
//第二次查询编号为3的学生信息,如果缓存中有,就直接从缓存拿数据,不执行sql语句
Tstudent stu2=tm.findStudentByid1(3);
System.out.println("学生信息为:"+stu2);
}
3.4:二级缓存(Mapper):手动开启.作用:减少应用程序与数据交互的次数,提高访问速度和性能.
3.4.1:二级缓存的工作原理 
3.4.2:二级缓存区的原理 
注意:为什么执行insert,update,delete的sql语句之后会清空二级缓存区,原因是 在MyBatis的insert,update,delete的sql语句的flashCache="true",如果你不想在执行insert,update,delete的sql语句之后清空二级缓存区,那么你需要手动在当前sql语句中设置flashCache="false".
3.4.3:二级缓存的使用步骤:
第一步:在mybatis的核心配置文件中配置二级缓存
<settings>
<!-- 开启二级缓存总开关 -->
<setting name="cacheEnabled" value="true"/>
</settings>
第二步:在映射文件中配置<cache></cache>
cache 标签有多个属性
eviction: 缓存回收策略,有这几种回收策略
LRU - 最近最少回收,移除最长时间不被使用的对象
FIFO - 先进先出,按照缓存进入的顺序来移除它们
SOFT - 软引用,移除基于垃圾回收器状态和软引用规则的对象
WEAK - 弱引用,更积极的移除基于垃圾收集器和弱引用规则的对象
默认是 LRU 最近最少回收策略
flushinterval 缓存刷新间隔,缓存多长时间刷新一次,默认不清空,设置一个毫秒值
readOnly: 是否只读;true 只读,MyBatis 认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。MyBatis 为了加快获取数据,直接就会将数据在缓存中的引用交给用户。不安全,速度快。读写(默认):MyBatis 觉得数据可能会被修改
size : 缓存存放多少个元素
type: 指定自定义缓存的全类名(实现Cache 接口即可)
blocking:阻塞缓存, 若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。
第三步:只有实现序列化接口(java.io.Serializable)的类的对象才能进行二级缓存.
3.4.4:禁用二级缓存:在mybatis的sql语句中用属性useCache=false,可以禁用当前 select语句的二级缓存,默认是true.
eg:<!--在sql中用useCache="false"属性设置二级缓存失效,表示这条sql语句查询出的数据不会保存到 二级缓存区中-->
<select id="findStudentByid1" parameterType="int" resultMap="tstudentMap" useCache="false">
select sid,sname,sage,sex,address,cid from t_student where sid=
</select>
3.4.5:刷新二级缓存:在mybatis的sql语句中用属性flushCache=true可以刷新当前的二级缓存,默认情况下如果是select语句,那么flushCache是false。
如果是insert、update、delete语句,那么flushCache是true。
注意:在查询的sql语句中用useCache=false禁用sql的二级缓存时,它是查询数据后,不将数据存在二级缓存区中;在查询的sql语句中用flushCache=true刷新当前的二级缓存区,查询时会将数据存在二级缓存区,只是每次执行这个查询时,先清二级缓存区再查询.但是他们达到的效果都可以使用二级缓存失效.
3.4.6:二级缓存的应用场景:对于访问响应速度要求高,但是实时性不高的查询,可以采用二级缓存技术。(表中数据增加,修改,删除比较少,查询比较频繁,且 要求查询效率高时就用二级缓存技术.)
注意:在使用二级缓存的时候,要设置一下刷新间隔(cache标签中有一个 flashInterval属性)来定时刷新二级缓存,这个刷新间隔根据具体需 求来设置,比如设置30分钟、60分钟等,单位为毫秒,防止产生脏数 据。
3.4.7:二级缓存的局限:Mybatis二级缓存对细粒度的数据级别的缓存实现不好.(如果当前数据比较多,访问比较频繁(查询),不方便用二级缓存.因为每次刷新缓存区时,会将整个缓存区所有数据全刷新,不能实现只刷新更改的数据,这时就可以用第三方缓存容器,eg:redis).
4.扩展:
4.1:查询sql的简写:
eg:<!--将要查询的列存在一个sql标签中,哪个sql语句要用这些列,在sql中引入-->
<sql id="studentColum">
sid,sname,sage,sex,address,cid
</sql>
<select id="findStudentByid1" parameterType="int" resultMap="tstudentMap">
select
<!--用include标签引入要查询的列-->
<include refid="studentColum"></include>
from t_student where sid=
</select>
4.2:在mybatis的核心配置文件中给全限定类名取别名
<!--给类或包取别名-->
<typeAliases>
<!--第一种:给限定类名取别名
<typeAlias type="bean.Tclass" alias="tc1"></typeAlias>-->
<!--第二种:扫描包,可以直接用类名作为别名,用作sql的返回类型-->
<package name="bean"/>
<!--第三种:如果当前扫描包中及其子包中有相同类名时,直接用类名作为别名会冲突,配合在类上用注解 @alias("别名")-->
</typeAliases>
@Alias("tc2")
public class Tclass implements Serializable {
}
<!--关联映射第一种:嵌套查询-->
<select id="selectClassByCid" parameterType="int" resultType="别名(tc1/Tclass/tc2)">
select cid,cname from t_class where cid=
</select>
|