面试常问:
- 延迟加载提高加载效率:
Mybatis的缓存
- 一级缓存:也即是本地缓存,是SqlSession级别的,读个sql语句之间不会共享缓存,使用@Transactional来生效,也就是说在一次事务中多次查询会使用到一级缓存
- 默认开启,可以在配置中关闭
- 当我们执行查询操作时,MyBatis会先去一级缓存中查找是否有之前查询过的数据。如果有,直接返回缓存中的数据;如果没有,去数据库查询数据,并将查询结果放入一级缓存中。
- 当我们执行更新操作(包括insert、update、delete)时,MyBatis会清空一级缓存。这是为了保证缓存中的数据和数据库中的数据是一致的。
- 当SqlSession结束或关闭时,一级缓存也就清空了。
- 二级缓存:
- 在mapper.xml上方加上
<Cache></Cache>
即可 - 根据mapper.xml中命名空间来区分,是mapper级别的,只用当执行同一个mapper中的增改删语句时才会失效,增删改频繁时二级缓存基本失效,并且,微服务中多台服务中只有被调用的那一台的二级缓存才会删除,其余的不删除,造成不一致。
- 在mapper.xml上方加上
- 默认开启,可以在配置中关闭
- Springboot Cache,会缓存方法的返回值,但是同样也只能在一个节点生效,并且,@Cacheable不会主动刷新缓存,但是@CachePut会强制刷新缓存,并把新的缓存放入
- 共享的缓存!Redis 可以在配置文件中将springboot的缓存类型设置为Redis
#{} 和 ${} 的区别是什么?
#{}
是Propeties文件中的变量占位符,会被原样替换${}
是sql的参数占位符,Mybatis会把他替换成?
后续通过反射进行替换数据
xml 映射文件中,除了常见的 select、insert、update、delete 标签之外,还有哪些标签?
resultMap 定义查询结果的映射规则
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name" />
<result property="password" column="user_password" />
<result property="email" column="user_email" />
<result property="bio" column="user_bio" />
</resultMap>
sql:定义可复用的SQL代码段
<sql id="userColumns"> ${alias}.id, ${alias}.username, ${alias}.password, ${alias}.email, ${alias}.bio </sql>
<select id="selectUsers" resultMap="userResultMap" >
select
<include refid="userColumns"><property name="alias" value="user"/></include>
from some_table user
</select>
parameterType 定义SQL语句的输入参数类型
<insert id="insertUser" parameterType="User">
insert into users (username, password, email, bio)
values (#{username}, #{password}, #{email}, #{bio})
</insert>
resultType
:定义 SQL 语句的输出结果类型。
<select id="selectUsernames" resultType="string">
select username from users
</select>
association
:定义一对一的关联关系。
<resultMap id="userResultMap" type="User">
<!-- ... -->
<association property="address" javaType="Address">
<id property="id" column="address_id" />
<result property="street" column="address_street" />
<result property="city" column="address_city" />
<result property="state" column="address_state" />
<result property="zip" column="address_zip" />
<result property="country" column="address_country" />
</association>
</resultMap>
collection
:定义一对多的关联关系。
<resultMap id="userResultMap" type="User">
<!-- ... -->
<collection property="posts" ofType="Post">
<id property="id" column="post_id" />
<result property="subject" column="post_subject" />
<result property="body" column="post_body" />
</collection>
</resultMap>
dynamic
标签如 if
、choose
、when
、otherwise
、trim
、where
、set
:用于构建动态 SQL。
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = 'ACTIVE'
<if test="title != null">
AND title like #{title}
</if>
</select>
- 执行过程
- java程序加载config文件,创建SqlSessionFactory对象,之后通过SqlSessionFactory创建SqlSession对象,之后通过SqlSession对象执行映射配置文件中定义的SQL语句,最后通过SqlSession对象提交事务,关闭SqlSession对象
Dao接口(Mapper接口)的原理
Dao中的方法,参数不同时,可以重载吗
Mapper中的方法可以 重载,使用的是全限定名 + 方法名 拼接的字符串作为key去匹配。 但是xml文件中的id只能指定一个,也即是重载的所有方法都是用一个sql语句,而这个sql语句我们可以使用动态sql来实现
<select id="getAllStu" resultType="com.pojo.Student">
select * from student
<where>
<if test="id != null">
id = #{id}
</if>
</where>
</select>
Dao接口的原理:
MyBatis运行时会使用JDK动态代理来为Dao生成代理proxy对象,代理对象会拦截接口方法,转而执行MappedStatement中的sql
MyBatis的分页
nihao
原理
答:**(1)** MyBatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页;**(2)** 可以在 sql 内直接书写带有物理分页的参数来完成物理分页功能,**(3)** 也可以使用分页插件来完成物理分页。
分页插件的原理
- 插件设置一个ThreadLocal变量来存储分页参数
- 当执行查询时,MyBatis会调用所有注册的拦截器。
- PageHelper 首先会保存原始的查询SQL,然后生成一个新的SQL,这个新的SQL在原始的SQL基础上添加了LIMIT和OFFSET
- PageHelper 将新的SQL替换成原始的SQL然后执行
- 最后PageHelper会清除ThreadLocal中变量,避免内存泄漏
MyBatis动态sql是什么的,有哪些,原理
常用的动态sql标签
<if></if>
<where></where>(trim,set)
<choose></choose>(when, otherwise)
<foreach></foreach>
<bind/>
执行原理
OGNL (Object-Graph Navigation Language)表达式,通过他可以在XML配置文件中引用Java对象和方法,在动态SQL中,常常使用其进行判断条件
- 常见的OGNL语法:
person.name #访问对象的属性 person.getName() # 调用对象的方法 persons.{name} # 获取persons对象的name属性 person.age > 18 ? 'adult' : 'child' # 条件表达式 person.agge + 1 # 算术表达式 person.age > 18 && person.gender == 'male' # 逻辑表达式
MyBatis如何将sql执行结果封装为目标对象并返回
- 使用
<resultMap>
标签去映射列表名和对象属性名之间的映射关系 - 使用sql列别名方式,将列名书写为对象属性名,例如:T_NAME AS NAME 对应的属性名是name,会忽略大小写
当映射关系建立之后,MyBatis会通过反射创建对象,然后给对象的属性一一赋值
MyBatis延迟加载的原理 待写
MyBatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的是1v1,collection是1v多
Executor执行器
- SimpleExecutor 每执行一次 update或select,就开启一个Statement对象,用完立刻关闭Statement对象
- ReuseExecutor 执行update/select 以sql为key查找Statement对象,不存在就创建,使用后放在Map中,供下一次使用
- BatchExecutor 执行update 时,将所有的sql都添加到批处理中,之后统一执行,缓存多个Statement对象,每个Statement对象都是等待sql添加之后,等待逐一执行
深入解读
基础支持层
- 解析器模块
- DOM解析将xml的标签组织成一颗DOM树,将整个xml文档加载进内存
- SAX基于时间模型的xml解析方式。加载一部分到内存中,并且当程序处理过程中满足条件时,会结束解析,不必解析剩余的xml内容。但是不支持层次关系和父子关系的保存
- XPathParser:MyBatis提供的XPathParser类封装了XPath、Document、EntityResolver
MyBatis Plus
一些插件
自定义填充字段
@Component
public class MyBatisPlusDateHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);
this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
}
}
分页插件
@Configuration(value = "dataBaseConfigurationByAdmin")
public class DataBaseConfiguration {
/**
* 分页插件
*/
@Bean
@ConditionalOnMissingBean
public MybatisPlusInterceptor mybatisPlusInterceptorByAdmin() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
使用时,只需要在自己定义的方法上加上一个Page类型的变量就可以进行分页查询了
Page<User> page = new Page<>(1, 10); // 第1页,每页10条记录
List<User> users = userService.selectUsers(page);