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
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