Mybatis学习笔记

Mybatis教程: https://blog.csdn.net/hellozpc/article/details/80878563

sql语句的参数类型

parameterType有简单类型(8个基本类型和String)以及对象类型。有两种获取参数值的方法

#{字段名}:若参数类型为对象类型时,字段名必须与对象的属性名一致,若为简单类型则不用。String类型的参数会给参数值加上单引号;

${字段名}:若参数类型为简单类型时,必须用${value}获取参数值。${}直接替换占位符,String类型的参数不会给参数值加上单引号。sql语句动态生成的时候,使用${};

<select id="queryUserByName" parameterType="String" reusltType="com.sa.User">
  select * from user where uname = #{name} or uname like '%${name}%'
  and address_code = #{adderss.code}
  order by ${name}
</select>

 

sql语句的返回类型

a. resultType 

1. 类的全路径
<select id="queryAllUsers"  resultType="com.sa.User">
  select * from user
</select>
2. 使用类的别名
在mybatis配置文件中指定别名,然后mapper中可以直接使用别名 resultType="User"
<typeAliases> 
    <typeAlias type="com.sa.User" alias="User"/> 
</typeAliases>

b.resultMap

解决实体类和表字段名称不一致问题

autoMapping默认完成映射,需要开启驼峰匹配
<resultMap id="userResult" type="com.sa.User" autoMapping="true">
  <id property="id" column="user_no"/>
  <result property="name" column="uname"/>
</resultMap>
也可以使用如下方法解决字段名称不一致的问题
<select id="queryAllUsers"  resultType="HashMap">
  select user_no "id", uname "name" from user
</select>

解决实体类和表字段类型不一致问题  需要用到类型转换器

 

调用存储过程

a. 编写存储过程
create or replace procedure procQueryCountByName (name in varchar, count out number)
as begin
  select count(*) from user where uname = name;
end;

b. 定义Mapper接口
int queryCountByAgeWithProc(HashMap params);

c. 编写Mapper.xml sql
存储过程的入参必须为对象类型
<select id="queryCountByNameWithProc" statementType="CALLABLE" parameterType="HashMap">
  { CALL procQueryCountByName(
    #{name, jdbcType=VARCHAR, mode=IN},
    #{count, jdbcType=INTEGER, mode=OUT},
  )}
</select>

d. 调用接口
Map<String, Object> params = new HashMap<>();
params.put("name", "lily");
userMapper.queryCountByAgeWithProc(params);
Object count = params.get("count");  // 获取输出参数

 

Mybatis动态sql

a. Mybatis标签

1. 使用where 1=1拼接动态sql
<select id="queryUserByNameAndAge" parameterType="User" resultType="userResult">
  select * from user
  where 1=1
  <if test=" name!=null and name!='' ">
    and uname=#{name}
  </if>
</select>

2. 使用<where>标签,该标签会自动处理第一个<if>中的and
<select id="queryUserByNameAndAge" parameterType="User" resultType="userResult">
  select * from user
  <where>
    <if test=" name!=null and name!='' ">
    and uname=#{name}
    </if>
  </where>
</select>

b. 集合处理

入参类型为List时,在mapper.xml文件中必须使用list代替该集合。数组则用array,简单类型数组:parameterType=“int[]” ,对象类型数组:parameterType=“Object[]”

<select id="queryUsersByIds" parameterType="java.util.List" resultType="userResult">
  select * from user where 1=1
  <if test="list!=null and list.size>0">
     and id in 
    <foreach collection="list" open="(" close=")" item="id" separator=",">
      #{id}
    </foreach>
  </if>
</select>

c. SQL片段

1. 提取重复的SQL语句
<sql id="idCondition">
  <if test="list!=null and list.size>0">
     and id in 
    <foreach collection="list" open="(" close=")" item="id" separator=",">
      #{id}
    </foreach>
  </if>
<sql>
2. 引用SQL片段
<select id="queryUsersByIds" parameterType="java.util.List" resultType="userResult">
  select * from user where 1=1
  <include refid="idCondition" />
</select>

 关联查询

一对一关联查询

1. 使用业务扩展类StudentBO
Student(sno, sname, cardid), StudentCard(cardid, cardinfo),  StudentBO(sno, sname, cardid, cardinfo)
<select id="queryStudentInfo" resultType="StudentBO">
  select s.*, c.* from student s, student_card c where s.cardid=c.cardid
</select>
2. 使用resultMap关联
a. 创建关联实体类
Student(sno, sname, StudentCard card)
b. 定义返回类型resultMap,一对一使用association 
<resultMap id="studentResult" type="student">
  <id property="sNo" column="sno"/>
  <result property="sName" column="sname"/>
  <association property="card" javaType="StudentCard">
    <id property="cardId" column="cardid"/>
    <result property="cardInfo" column="cardinfo"/>
  </association>
</resultMap>
c. SQL查询语句
<select id="queryStudentInfo" resultMap="studentResult">
  select s.*, c.* from student s, student_card c where s.cardid=c.cardid
</select>

一对多关联查询

a. 创建关联实体类
Student(sno, sname, classid), StudentClass(classId, className, List<Student> students)
b. 定义返回类型resultMap
<resultMap id="studentClassResult" type="studentClass">
  <id property="classId" column="classid"/>
  <result property="className" column="classname"/>
  <!-- students属性类型javaType="List" 元素类型ofType="student" -->
  <collection property="students" ofType="student">
    <id property="sNo" column="sno"/>
    <result property="sName" column="sname"/>
  </collection>
</resultMap>
c. SQL查询语句
<select id="queryStudentClassInfo" resultMap="studentResult">
  select c.*, s.* from student_class c inner join student s  on c.classid=s.classid
</select>

Mybatis整合日志

1. 导入log4j.jar包,添加配置文件log4j.properties

2. 在mybatis配置文件中开启日志, 指定使用的具体日志框架

  <settings> <setting name="logImpl" value="LOG4J"/> </settings>

延迟加载

1. 开启延迟加载
<settings>
    <setting name="lazyLoadingEnabled" value="true"/> 开启延迟加载
    <setting name="AggressiveLazyLoading" value="false"/> 关闭立即加载
</settings>    
2. 查询学生的sql,正常情况会查询两张表student和student_card,开启延迟加载后,只会查询student表,column是外键
<resultMap id="lazyLoadStudentResult" type="student">
  <id property="sNo" column="sno"/>
  <result property="sName" column="sname"/>
  <association property="card" javaType="StudentCard" select="queryStudentCardById" column="cardid"/>
  </association>
</resultMap>

<select id="queryStudentInfo" resultMap="lazyLoadStudentResult">
  select s.*, c.* from student s, student_card c where s.cardid=c.cardid
</select>
如果是一对多的情况,查询SQL中应该只查询主表student_class,不用关联student表了
3. 查询学生证的sql,当获取student.getCard()时,才会再次发起数据库连接执行该sql
<select id="queryStudentCardById" parameterType="int" resultType="student">
    select * from student_card where cardid=#{cardid}
</select>

Mybatis缓存

缓存类型 默认开启 缓存范围 数据存储位置 保存条件 缓存失效条件
一级缓存 同一个SqlSession对象 内存 第一次查询之后 执行commit
二级缓存 同一个namespace 硬盘 执行close 执行commit

 

 

一级缓存
    Mybatis默认开启一级缓存,如果用同一个SqlSession对象查询相同的数据,mybatis只会在第一次查询时发起数据库查询,并将结果放入session中,后续的查询直接从缓存中取数据。当执行session.commit()(进行增删改操作)后会清理所有缓存对象,防止脏数据。
二级缓存

Mybatis自带二级缓存:
    Mybatis自带二级缓存,默认没有开启,同一个namespace产生的Mapper对象可以共享缓存。当执行session.close()将数据保存到二级缓存(实际上是保存到硬盘中),因此缓存的对象必须实现序列化接口。当其他session执行commit后会清理所有缓存对象,当前session只有在close后才会写入二级缓存,因此在该session执行期间不需要刷新二级缓存。
a. 在mybatis配置文件开启二级缓存
  <setting name="cacheEnabled" value="true"/>
b. 在Mapper.xml中指定cache语句,开启mybatis默认的二级缓存,则该namespace的sql可以进行缓存,也可以使用useCache禁用某个sql的二级缓存

<mapper namespace="com.sa.mapper.studentMapper>
    <cache/>
    <select> ...
    <select useCache="false"> ...
</mapper>

如果是同一个SqlSession对象进行多次相同的查询,则直接进入一级缓存;
如果不是同一个SqlSession对象进行多次相同的查询,但是均来自同一个namespace,则进入二级缓存

 

 

 

SqlSession session1 = sessionFactory.open();
StudentMapper mapper1 = session1.getMapper(StudentMapper.class);
Student student = mapper1.queryStudentById(2); // 将数据保存到一级缓存
student  = mapper1 .queryStudentById(2);   // 进入一级缓存
session1.commit();
student  = mapper1 .queryStudentById(2);  // 执行commit后,一级缓存中的数据被清除,即缓存失效
session1.close(); // 将数据保存到二级缓存
SqlSession session2 = sessionFactory.open();
StudentMapper mapper2 = session2.getMapper(StudentMapper.class);
student = mapper2.queryStudentById(2); // 进入二级缓存

三方提供的二级缓存:

要想整合三方提供的二级缓存,必须实现Mybatis的Cache接口。整合ehcache二级缓存的步骤如下:
a. 导入Ehcache-core.jar、mybatis-Ehcache.jar、slf4j-api.jar
b. 编写配置文件Ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
        updateCheck="false">
 
   <diskStore path="c:\\ehcache"/> 
   
   <cache   name = "EhcacheDefaultCache"    
     maxElementsInMemory = "20000"    
     maxElementsOnDisk = "0"    
     eternal = "false"    
     overflowToDisk = "false"    
     diskPersistent = "false"    
     timeToIdleSeconds = "1800"    
     timeToLiveSeconds = "1800"    
     diskSpoolBufferSizeMB = "200"    
     diskExpiryThreadIntervalSeconds = "10"    
     memoryStoreEvictionPolicy = "FIFO"    
     >  
     <cacheEventListenerFactory class="com.seryo.cache.MyCacheEventListenerFactory"/>
   </cache> 
     
    <!-- 
    name: Cache的名称,必须是唯一的(ehcache会把这个cache放到HashMap里)。
    maxElementsInMemory: 在内存中缓存的element的最大数目。 
    maxElementsOnDisk: 在磁盘上缓存的element的最大数目,默认值为0,表示不限制。 
    eternal: 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断。 
    overflowToDisk: 如果内存中数据超过内存限制,是否要缓存到磁盘上。
     
    以下属性是可选的: 
    timeToIdleSeconds: 对象空闲时间,指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示一直可以访问。
    timeToLiveSeconds: 对象存活时间,指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示一直可以访问。
    diskPersistent: 是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false。 
    diskExpiryThreadIntervalSeconds: 对象检测线程运行时间间隔。标识对象状态的线程多长时间运行一次。 
    diskSpoolBufferSizeMB: DiskStore使用的磁盘大小,默认值30MB。每个cache使用各自的DiskStore。 
    memoryStoreEvictionPolicy: 如果内存中数据超过内存限制,向磁盘缓存时的策略。默认值LRU,可选FIFO、LFU。
     
    缓存的3 种清空策略 :
    FIFO ,first in first out (先进先出).
    LFU , Less Frequently Used (最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存。
    LRU ,Least Recently Used(最近最少使用). (ehcache 默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
     -->
</ehcache>
————————————————
版权声明:本文为CSDN博主「酒丿精」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hujiujun/article/details/81699624
View Code

c. 开启ehcache二级缓存

<mapper namespace="com.sa.mapper.studentMapper>
  <cache type="org.mybatis.caches.ehcache.EhcacheCache">
     <!-- 可以覆盖Ehcache.xml中的值 -->
        <property name="maxElementsInMemory" value="2000"/>
  </cache>
</mapper>

逆向工程

通过表自动生成对应的实体类和mapper接口

 

 步骤如下:

a. 导入mybatis-generator-core.jar

b. 逆向工程的配置文件generator.xm



 

posted @ 2020-02-08 14:05  安小  阅读(211)  评论(0编辑  收藏  举报