序言:
文章内容输出来源:拉勾教育Java高薪训练营。
本篇文章是学习课程中的一部分课后笔记
一、自定义持久层框架Ipersistence是如何解决JDBC存在的问题?
- 使用配置文件解决硬编码问题
- 使用C3P0连接池解决了频繁创建释放数据库连接问题
- 在simpleExecute中使用了反射进行了参数的设置
- 在simpleExecute中使用了内省进行返回结果集的封装
二、进行自定义持久层框架Ipersistence优化时主要为了解决哪些问题?
- 应用在Dao层,整个操作的模板代码重复
- 调用sqlSession方法时、参数statementId硬编码
三、关于configuration 与 mappedStatement配置类的说法:
- 使用dom4j对sqlMapConfig.xml解析时会将解析出来的内容以不同形式封装到Configuration对象中
- 使用dom4j对mapper.xml解析时,每一个<select>标签的内容均对应一个mappedStatement对象
- configuration 中包含了mappedStatement的引用
四、关于genericTokenParser类的说法:
- 该类只能通过有参构造进行创建
- genericTokenParser是通用的标记解析类
- genericTokenParser在解析#{}占位符时必须通过标记处理类tokenHandler的配合
- genericTokenParser三个构造参数分别为开始标记、结束标记、标记处理器
五、关于sqlessionFactoyBuilder,sqlessionFactoy ,sqlession的说法:
- sqlessionFactoyBuilder最佳范围为方法范围,可定义为本地方法变量
- sqlessionFactoy 最佳范围是应用范围
- sqlession最佳范围是方法范围或请求范围
六、关于resultType与入参的说法:
- resultType表示返回值类型为:完整类名或别名,允许使用基本数据类型,String、int等
- resultType和resultMap的数据结构一样的,都是map结构
- mybatis中,除了使用@param注解来实现多个参数入参,还可以用Map对象实现多参数传递
七、mybatis中接口开发规范:
- mapper.xml中的namespace与mapper接口的类路径相同
- mapper接口方法名和mapper.xml中定义的每个statement的id相同
- mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType类型相同
- mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType类型相同
八、关于mybatis源码内容的说法:
- 涉及到的设计模式有:代理模式、builder模式、工厂模式、迭代器模式
- 功能架构可以分为三层:接口层、数据处理层、框架支撑层
- 支持用插件对statementHandler、paramterHandle、resultsetHandler等核心对象进行拦截
- Executor是执行器,负债sql的生成和查询缓存的维护
- statementtHandler封装了jdbc statement操作,负责对jdbc statement的操作
- typeHandler 负责java数据类型和jdbc数据类型之间的映射和转换
- sqlSource 负责根据用户传递的parameterObject动态生产sql语句,将信息封装到boundsql对象中
九、Mybatis动态sql是做什么的?都有哪些动态sql?简述一下动态sql的执行原理?
1.动态sql:
- 在数据库执行sql之前,需要对sql进行整合处理,根据传入的参数值,以及匹配的条件,有可能需要动态的去
判断是否为空,拼接sql。
2.动态sql:
<where> 、 <if> 、<include> 、<foreach>
<select id="selectUserByCondition" resultType="com.admin.entity.User">
select
<include refid="userInfo"/>
from
user
<where>
<if test="name !=null and name !=''">
and name = #{name }
</if>
<if test="roleIdList!=null and roleIdList.size() > 0 ">
and role_id in
<foreach collection="roleIdList" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
</where>
</select>
map 入参
<select id="selectByMap" resultType="com.admin.entity.User">
select * from user where
<foreach collection="map" index="key" item="value" separator="=">
${key} = #{value}
</foreach>
</select>
<choose>、 <when>、 <otherwise>
<select id="selectUserInfo" resultType="com.admin.entity.User">
select * from user
<where>
<choose>
<when test="name != null and name != ''">
and name = #{name }
</when>
<otherwise>
1 = 1
</otherwise>
</choose>
</where>
</select>
<set>、 <trim>
<update id="updateUser" parameterType="com.admin.entity.User">
update user
<set>
<if test="name != null and name != ''">
name = #{name },
</if>
</set>
where id = #{id}
</update>
<select id="selectUserByTrim" resultType="com.admin.entity.User">
select * from user
<trim prefix="where" suffix="order by id" prefixOverrides="and | or" suffixOverrides=",">
<if test="name != null and name != ''">
and name = #{name }
</if>
<if test="id != null and id !='' ">
and id = #{id}
</if>
</trim>
</select>
-- 批量更新
<update id="updateUserInfoByList" parameterType="java.util.List">
update user
<trim prefix="set" suffixOverrides=",">
<trim prefix="name =case" suffix="end,">
<foreach collection="userList" item="item">
<if test="item.name!=null">
when id=#{item.id} then #{item.name}
</if>
</foreach>
</trim>
<trim prefix="role_id =case" suffix="end,">
<foreach collection="userList" item="item">
<if test="item.roleId!=null and item.roleId!=''">
when id=#{item.id} then #{item.roleId}
</if>
</foreach>
</trim>
</trim>
<where>
<foreach collection="userList" separator="or" item="item">
id = #{item.id}
</foreach>
</where>
</update>
3.动态sql的执行原理:
- MyBatis的动态SQL是基于OGNL表达式。
- 在解析xml配置文件的时候,会有一个createSqlSource的操作。
- createSqlSource底层使用了XMLScriptBuilder调用parseScriptNode()的方法,解析xml的标签。
- 在parseScriptNode()的方法中有一个parseDynamicTags()方法,会对nodeHandlers里的标签根据不同的handler来处理不同的标签 , 然后把DynamicContext结果放回SqlSource中。
- 在Executor执行的时候,调用DynamicSqlSource的解析方法,并返回解析好的BoundSql,排好序,替换好参数的sql。
十、Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
1.Mybatis支持延迟加载
- 仅支持 association 关联对象和 collection 关联集合对象的延迟加载;
- association 指的是一对一,collection 指的是一对多查询。
- 配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false。
<settings>
<!-- 打开延迟加载的开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载,即延迟加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
2.实现原理
- 使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦
截器方法,当数据需要的时候在调用sql查询DB。 - 例如 : 调用 a.getB().getName(),拦截器 invoke()方法发现 a.getB()是
null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,
然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 a.getB().getName()方法的调用。
十一、Mybatis都有哪些Executor执行器?它们之间的区别是什么?
1.三种Executor:
- SimpleExecutor 、ReuseExecutor 、 BatchExecutor
2.区别
-
SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
-
ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。
简言之,就是重复使用Statement对象。 -
BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中addBatch(),等待统一执行executeBatch(),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。
与JDBC批处理相同。 -
作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。
-
在Mybatis配置文件中,可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数。
十二、简述下Mybatis的一级、二级缓存(分别从存储结构、范围、失效场景。三个方面来作答)?
1.存储结构:
- 一级缓存、二级缓存都是缓存到HashMap结构中。
2.范围:
-
一级缓存是SqlSession级别的,作用域是SqlSession,Mybatis默认开启一级缓存,在同一个SqlSession中,相同的Sql查询的时候,第一次查询的时候,就会从缓存中取,如果发现没有数据,那么就从DB查询出来,并且缓存到HashMap中,第二次查询直接在缓存中取,有数据就直接返回,不查DB。
-
二级缓存是mapper级别的,多个SqlSession去操作同一个mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession。
第一次调用mapper下的sql 的时候去查询信息,查询到的信息会存放到该mapper对应的二级缓存区域,第二次调用namespace下的mapper映射文件中,相同的SQL去查询,直接在二级缓存内取结果。
3.失效场景:
-
一级缓存 当进行增删改的操作的时候,缓存将会失效。
在spring容器管理中每次查询都是创建一个新的sqlSession,所以在分布式环境中不会出现数据不一致的问题。 -
二级缓存 使用时需要开启cache标签,在select上添加useCache属性为true,
在更新和删除时候需要手动开启flushCache刷新缓存。
十三、简述Mybatis的插件运行原理,以及如何编写一个插件?
1.运行原理:
- mybatis可以编写针对Executor、StatementHandler、ParameterHandler、ResultSetHandler四个接口的插件,mybatis使用JDK的动态代理为需要拦截的接口生成代理对象,然后实现接口的拦截方法,所以当执行需要拦截的接口方法时,会进入拦截方法(AOP思想)。
2.如何编写:
-
1.编写Intercepror接口的实现类
-
2.设置插件的签名,告诉mybatis拦截哪个对象的哪个方法
-
3.最后将插件注册到全局配置文件中
-
示例:
//插件签名,告诉mybatis当前插件拦截哪个对象的哪个方法
//type表示要拦截的目标对象,method表示要拦截的方法,args表示要拦截方法的参数
@Intercepts({
@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
})
public class MySecondPlugin implements Interceptor {
//拦截目标对象的目标方法执行
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("MySecondPlugin拦截目标对象:"+invocation.getTarget()+"的目标方法:"+invocation.getMethod());
/*
* 插件的主要功能:在执行目标方法之前,可以对sql进行修改已完成特定的功能
* 例如增加分页功能,实际就是给sql语句添加limit;还有其他等等操作都可以
* */
//执行目标方法
Object proceed = invocation.proceed();
//返回执行后的返回值
return proceed;
}
//包装目标对象:为目标对象创建代理对象
@Override
public Object plugin(Object target) {
System.out.println("MySecondPlugin为目标对象"+target+"创建代理对象");
//this表示当前拦截器,target表示目标对象,wrap方法利用mybatis封装的方法为目标对象创建代理对象(没有拦截的对象会直接返回,不会创建代理对象)
Object wrap = Plugin.wrap(target, this);
return wrap;
}
//设置插件在配置文件中配置的参数值
@Override
public void setProperties(Properties properties) {
System.out.println("MySecondPlugin配置的参数:"+properties);
}
}
- 插件注册到全局配置文件中
<plugins>
<plugin interceptor="com.mybatis_demo.plugin.MySecondPlugin"></plugin>
</plugins>
网友评论