mybatis基础面试
- mybatis查询数据输出结果为null,如何解决
1.问题:可能是Java实体类的属性名和表中的字段名称不一致
解决方法:
1,查询出来的字段和你实体类中对应的属性名保持一致
2.当进行单表简单查询时且返回值类型是基本类型时,一般尽量使用resultType;
当进行表关联查询时,或者说xml中定义了相关的resultMap标签,那么就一般尽量使用resultMap;
resultType和resultMap不能同时使用。
- mybatis中,#{}和${}的区别
"#{}是预编译处理,${}是字符替换
在使用#{}时,mybatis会将SQL中的#{}替换成“?”,配合preparedStatement的set方法赋值,
这样可以有效的防止SQL注入,保证程序运行的安全"
- mybatis的分页方式
分页方式:逻辑分页和物理分页
逻辑分页:使用mybatis自带的rowBounds进行分页,它是一次性查询很多数据,然后在数据中再进行检索
物理分页:自己手写sql分页,或者使用分页插件pageHelper,去数据库查询指定条数的分页数据形式
- mybatis逻辑分页和物理分页的区别
逻辑分页是一次查询很多数据,然后再结果中检索分页的数据。
弊端:需要消耗大量的内存,有内存溢出的风险,对数据库压力较大
物理分页是从数据库查询指定条数的数据,弥补了一次性全部查出的所有数据的种种缺点
比如需要大量的内存,对数据库查询压力较大等问题
- mybatis分页插件的实现原理是什么
分页插件的基本原理是使用mybatis提供的插件接口,自定义插件,在插件的拦截方法内拦截带执行的SQL
然后重写SQL,根据dialect方言,添加对应的物理分页语句和物理分页参数
- mybatis如何编写一个自定义插件
自定义插件的原理:
mybatis自定义插件针对mybatis四大对象(Executor,StatementHandler,ParameterHandler,ResultSetHandler)进行拦截
Executor:拦截内部执行器,它负责调用StatementHandler操作数据库,并把结果集通过ResultSetHandler进行自动映射,另外它还处理了二级缓存的操作;
StatementHandler:拦截SQL语法构建的处理,它是mybatis直接和数据库执行SQL脚本的对象,另外它也实现了mybatis 的一级缓存
ParameterHandler:拦截参数的处理
ResultSetHandler:拦截结果集的处理
自定义插件的关键:
/**mybatis插件要实现interceptor接口,*/
public interface Interceptor(
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
/** setProperties方法是在mybatis进行配置插件的时候可以配置自定义相关属性;
plugin方法是插件用于封装目标对象的,通过该方法我们可以返回目标对象本身,
也可以返回一个它的代理,可以决定是否需要进行拦截进而决定要返回一个什么样的目标对象,官方示例:return Plugin.wrap(target,this);
intercept方法就是要进行拦截的时候要执行的方法.
*/
自定义插件实现
@Intercepts({@Signature(type=Executor.class.method="query",
args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class})})
public class TestInterceptor implements Interceptor{
public Object intercept(Invocation invocation) throws Throwable{
Object target=invocation.getTarget();//被代理对象
Method method=invocation.getMethod();//代理方法
Object[] args=invocation.getArgs();//方法参数
//do something... 方法拦截前执行代码块
Object result=invocation.proceed();
//do something...方法拦截后执行代码块
return result;
}
public Object plugin(Object target){
return Plugin.wrap(target,this);
}
- mybatis的一级缓存和二级缓存
一级缓存:
基于PerpetualCache的HashMap本地缓存,它的声明周期和SQLSession一致的,有多个SQLSession或者分布式的环境中数据库操作,可能会出现脏数据。
当Session flush或close之后,该Session中的所有cache就将清空。
默认一级缓存是开启的。
二级缓存:
也是基于PerpetualCache的HashMap本地缓存,不同在于其存储作用域为Mapper级别的,如果多个SQLSession之间需要共享数据,则需要使用到二级缓存,并且二级缓存可自定义存储源,如E和Cache
默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态)
开启二级缓存数据查询流程:
二级缓存》一级缓存》数据库
缓存更新机制:
当某一个作用域(一级缓存Session/二级缓存Mapper)进行了增删改操作后,默认该作用域下所有select中的缓存将被clear
- mybatis有哪些执行器(Executor)
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批处理相同
- mybatis是否支持延迟加载,延迟加载的原理是什么
mybatis支持延迟加载,设置lazyLoadingEnabled=true即可
延迟加载的原理:
调用的时候触发加载,而不是初始化的时候就加载信息
(例如:调用a.getB().getName(),这时候发现a.getB()的值为null,此时会单独触发实现保存号的关联B对象的SQL,
先查询出来B,然后再调用a.setB(b),而这时候再调用a.getB().getName()就有值了,这就是延迟加载的基本原理)
- RowRounds是一次性查询全部的结果吗,为什么
RowRounds表面是在“所有”数据中检索数据,其实并非是一次性查询出所有数据
因为mybatis是对jdbc的封装,在jdbc驱动中有一个Fetch Size的配置,它规定了每次最多从数据库查询多少条数据,
假如你要查询更多数据,它会在你执行next()的时候,去查询更多的数据。
(就好比你去自动提款机取1w元,但取款机每次最多只能取2.5k,所以要取4次才能把钱取完。)
只对于jdbc来说,当你调用next()的时候会自动帮你完成查询工作,这样做的好处可以有效的防止内存溢出。