MyBatis
== 1、#{}和${}的区别:==
(1)#{}是参数占位符,MyBatis会将SQL中的#{}替换为 ?,实际参数值替换
在SQL运行前会使用PreparedStatement对象的参数设置方法,执行sql语句,将按序给参数占位符替换后的?
安全性高、效率高、能避免sql注入
例:
select id,name,email,age from student where id=#{studentId}select id,name,email,age from student where id=?
#{user.name} 利用反射从参数对象中获取 user对象 的属性值 name
(2)${}是变量占位符,静态文本替换、字符串替换
使用$包含的字符串替换所在的位置,使用Statement对象把sql语句和${}内存连接起来
安全性低、效率低、有sql注入风险
一般可用于替换表名、列名,能确定数据安全的可以使用
例:
select id,name,email,age from student where id=${studentId}select id,name,email,age from student where id=1005
${driver} 静态替换为 com.mysql.jdbc.Driver
==2、xml映射文件中的常见标签==
select、insert、update、delete、trim、where、set、foreach、if、choose、when、otherwise、bind、sql、include、resultMap、parameterMap、selectKey
==3、动态sql==
原理:使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql,来完成动态sql的功能。
动态sql可以在xml映射文件中,以标签形式编写动态sql,实现逻辑判断和动态拼接sql的功能
if
如果没有传入“title”,那么所有处于“ACTIVE”状态的BLOG都会返回,反之若传入了“title”,那么就会把模糊查找“title”内容的BLOG结果返回
加入另一个条件同理
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
choose 、when、otherwise 选择其中的条件之一,没有条件达成就返回所有
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
trim、where、set
if中有值的情况下,才会执行where,where会自动去除AND\OR
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
set一般用于动态更新 会自动消去无关的“,”
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
sql
foreach 允许指定一个集合 或 指定字符串(可有分隔符)
<sql id="studentSql">
select id,name,age,email from student
</sql>
<select id="selectStudentForeachOne" resultType="com.qifengle.domain.Student">
<include refid="studentSql"/> where id in
<foreach collection="list" item="myI" open="(" close=")" separator="," >
#{myI}
</foreach>
</select>
==4、主要的类的介绍==
(1)Resources 负责读取mybatis配置文件
InputStream in = Resources.getResourceAsStream(”mybatis.xml“);
(2)SqlSessionFactoryBuilder 通过build方法创建SqlSessionFactory对象 创建后的不需要了,相当于局部变量
SqlSessionFactoryBuilder sfb = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = sfb.build(in);
(3)SqlSessionFactory 核心类 是一个接口 创建后运行期间一直存在 全局变量 可以创建多个SqlSession
接口实现类:DefaultSqlSessionFactory
SqlSessionFactory作用: 获取SqlSession对象
SqlSession sqlSession = factory.openSession();
openSession()方法:无参方法获取非自动提交事务的SqlSession对象
openSession(boolean)方法:true获取自动提交事务的SqlSession对象 不用写commit提交事务、false要写commit来提交事务
(4)SqlSession 接口 定义了操作数据库的方法(select()、insert()、commit()等) 每个SqlSession可以有多个Mapper业务
该接口实现类:DefaultSqlSession :实现操作数据库的方法
SqlSession对象不是线程安全的,需要在方法内部使用,
在执行sql语句前,使用openSession()获取SqlSession对象
在执行完sql语句后,需要关闭它,执行sqlSession.close(),这样能保证它的使用是线程安全的
==5、通常一个XML映射文件对应一个Dao接口(Mapper接口),工作原理是什么?==
Mapper接口工作原理:JDK动态代理+key-value
JDK动态代理,Mybatis运行时会使用JDK动态代理为Mapper接口生成proxy代理对象,该代理对象会拦截接口方法,执行MappedStatement所代表的sql,执行sql后返回结果。
接口权限名+方法名 作为 key值,可以唯一定位一个MappedStatement
(1)Mapper接口,接口权限名 == XML映射文件中的namespace
(2)接口的方法名 == XML映射文件中MappedStatement的id值
(3)接口方法内参数 == 传递给sql的参数
例:com.heyuliang.mapper.StudentMapper.findStudentById
Key:namespace(com.heyuliang.mapper.StudentMapperid)+id(findStudentById)
Value:对应的唯一的MappedStatement对象
mybatis中每个<select>
、 <insert>
、 <update>
、 <delete>
标签都会解析为一个个MappedStatement对象
==6、分页==
分页插件的原理:使用MyBatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截sql,然后重新sql,根据dialect方言,添加对应的物理分页语句和物理分页参数
MyBatis仅可以编写针对这 4 种接口的插件:ParameterHandler
、 ResultSetHandler
、 StatementHandler
、 Executor
分页实现过程:JDK 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能。
实现拦截的方法:InvocationHandler的
invoke()` 方法
自定义插件:实现 Interceptor 接口,重写intercept()方法,执行要拦截的接口及方法
例:select * from student 拦截后重写
select t.* from(select * from student) t limit 0 ,10
(1)PageHelper 物理分页(数据量大时推荐使用) MyBatis通用的分页插件 支持多种数据库
调用startPage,会通过PageInterceptor对后边的第一个sql语句进行拦截,并拼接上limit语句
(2)RowBounds 内存分页(数据量小适合使用)
使用RowBounds对象进行分页,是针对ResultSet结果集执行的内存分页。先将所有结果查询出来,然后通过计算offset和limit,返回部分结果,操作在内存中进行。
(3)SQL内部limit 物理分页
直接在SQL语句中直接书写带物理分页的参数(limit)来完成物理分页功能
==7、一对一一对多 关联查询==
(1)一对一 一个sql查询关联对象
(2)使用嵌套查询,使用join查询
去重原理:<resultMap> 标签中的<id>标签,指定了唯一确定记录的id列,MyBatis根据<id>列来完成去重复的功能,之一著对象有去重。
==8、**延迟加载**==
(1)、仅支持association关联对象(一对一)和collection关联集合对象(一对多)的延迟加载
(2)配置文件中配置: lazyLoadingEnabled=true|false
(3)原理:使用CGLIB动态代理插件目标对象的代理对象。
例:调用 a.getB().getName()
,拦截器 invoke()
方法发现 a.getB()
是 null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 a.getB().getName()
方法的调用。
==9、不同XML映射文件id是否可重复==
获取MappedStatement的方式是用Map集合获取Map<String,MappedStatement>,key-value的形式,可以是namespace+id。
不同映射文件,若配置了namespace,id可以重复,若没有配置namespace,则id不能重复。
==10、MyBatis的执行器==
三种基本的Executor执行器
(1)SimpleExecutor执行器
每执行一次update\select,就开启一个Statement对象,用完后就关闭
(2)ReusuExecutor执行器:
每执行update\select,先根据key查找Map中是否存在Statement对象,若不存在就创建并使用,若存在就使用,用完不关闭,放置回Map。可重复使用Statement对象
(3)BatchExecutro执行器
完成批处理 JDBC批处理不支持select
执行update,缓存了多个Statement对象,将所有的sql添加到批处理中(addBatch()),等待统一执行(executeBatch())。
(4)指定Executor执行器:配置文件中指定的ExecutorType类型参数
==11、MyBatis映射文件和内部数据结构的映射关系==
(1)xml配置信息被封装到Configuration内部中。
(2)<parameterMap>
标签会被解析为 ParameterMap
对象,其每个子元素会被解析为 ParameterMapping 对象。
(3)<resultMap>
标签会被解析为 ResultMap
对象,其每个子元素会被解析为 ResultMapping
对象。
(4) <select>、<insert>、<update>、<delete>
标签均会被解析为 MappedStatement
对象
(5)标签内的 sql 会被解析为 BoundSql 对象。
==12、MyBatis缓存==
(1)查询缓存特性,提高查询效率 MyBatis有两级缓存
(2)默认开启:一级缓存(SqlSession级别的缓存)
(3)开启二级缓存配置:setting
(4)选择缓存:select标签
(5)flushCache属性:增删改默认flushCache=true Sql执行后同时清空一二级缓存
(6)sqlSession.clearCache():只用来清除一级缓存
= 1、一级缓存
(1)SqlSession级别缓存,本地缓存
(2)作用域:默认sqlSession
(3)清空清空:Session flush \ close 时 所有Cache将清空
(4)不能被关闭,可配置作用域
(5)工作机制:同一次会话期间,只要查询过的数据都保存在当前SqlSession的一个Map中。
(6)缓存失效情况:不同SqlSession对应不同的一级缓存、同一个SqlSession下查询不同的条件、同一SqlSession下两次查询中有增删改操作、有进行过手动清空缓存的操作
=2、二级缓存
(1)全局作用域缓存
(2)默认不开启,需要手动配置
(3)需要pojo实现Serializable接口
(4)开启的配置:<setting name = "cacheEnable" vlaue = "true"/>
=3、自定义缓存
(2)EhCache框架的使用
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!