6. mybatis高级

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-->
<!--    &lt;!&ndash;test后面条件判断的是传入参数的属性名&ndash;&gt;-->
<!--    <if test="sage!=null">-->
<!--      and sage=#{sage}-->
<!--    </if>-->
<!--    <if test="sex!=null">-->
<!--      and sex=#{sex}-->
<!--    </if>-->

        select sid,sname,sage,sex,address,cid from t_student
        <!--where标签可以去除多余and关键字-->
        <where>
          <!--test后面条件判断的是传入参数的属性名-->
          <if test="sage!=null">
            and sage=#{sage}
          </if>
          <if test="sex!=null">
            and sex=#{sex}
          </if>
        </where>
  </select>

  <select id="findByAgeAndSex" parameterType="bean.Tstudent" resultMap="tstudentMap">
    select sid,sname,sage,sex,address,cid from t_student where sname=#{sname}
    <!--test后面条件判断的是传入参数的属性名-->
    <choose>
      <when test="sage!=null">
        and sage=#{sage}
      </when>
      <when test="sex!=null">
        and sex=#{sex}
      </when>
    </choose>
  </select>

  <update id="updateStudent" parameterType="bean.Tstudent">
    update t_student
    <!--set标签可以去掉多余逗号,set标签中所有条件都不满足,就会报错-->
    <set>
      <if test="sname!=null">
        sname=#{sname},
      </if>
      <if test="sage!=null">
        sage=#{sage},
      </if>
      <if test="sex!=null">
        sex=#{sex},
      </if>
      <if test="address!=null">
        address=#{address},
      </if>
      <if test="cid!=null">
        cid=#{cid},
      </if>
    </set>
    where sid=#{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">
        #{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>
  </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="," >
    #{item1}
  </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=",">
    #{item2}
  </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=",">
    #{item3}
  </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(#{sname},#{sage},#{sex},#{address},#{tclass.cid});<!--tclass是传入参数Tstudent的属性,也是班级对象,tclass.cid调出的是班级编号-->
</insert>

<!--第二种:主键映射,在插入数据时,获得刚生成的主键-->
<insert id="insertStudent2" parameterType="bean.Tstudent" useGeneratedKeys="true" keyProperty="sid">
  insert into t_student(sname,sage,sex,address,cid)
  values(#{sname},#{sage},#{sex},#{address},#{tclass.cid});<!--tclass是传入参数Tstudent的属性,也是班级对象,tclass.cid调出的是班级编号-->
</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=#{cid}
</select>

<select id="findStudentByid1" parameterType="int" resultMap="tstudentMap">
  select sid,sname,sage,sex,address,cid from t_student where sid=#{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=#{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=#{cid}
</select>

<select id="findClassById1" parameterType="int" resultMap="tclassMap">
  select cid,cname from t_class where cid=#{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=#{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=#{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=#{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=#{cid}
</select>
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值