目录
4.MyBatis是如何将sql执行结果封装为目标对象并返回的?有哪几种映射方式?
7.MyBatis是否支持延迟加载?如果支持,它的实现原理是什么?
1.什么是MyBatis?
- MyBatis是一个半ORM(Object Relation Mapping对象关系映射)框架、是支持自定义SQL、存储过程以及高级映射的持久层框架;
- 内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建传输器、关闭资源等繁杂的过程。程序员直接编写写原生态sql,可以严格控制sql执行性能,灵活度高;
- MyBatis可以使用xml或注解来配置和映射原生信息,将POJO映射成数据库中的记录,避免l几乎所有的JDBC代码和手动设置参数以及获取结果集的工作;
从执行sql到返回result的过程:通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。
2.MyBatis的优缺点?
优点:
- 基于sql语句编程,相当灵活,不会对应程序或者数据库的现有设计造成任何影响,sql写在xml里,解除了SQL与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态sql语句,并可重用;
- 与JDBC相比,消除了JDBC大量冗余的代码,不需要手动开关连接;
- 很好的与各种数据库兼容
- 能够与Spring很好的集成;
- 提供映射标签,支持对象与数据库的ORM字段关系映射,提供对象关系映射标签,支持对象关系组件维护。
缺点:
- SQL语句主要依赖于数据库,导致数据库移植性差,不能随意更换数据库;
- SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的有一定要求。
3.MyBatis是如何进行分页?分页插件的原理是什么?
- MyBatis使用RowBounds对象进行分页(是针对ResultSet结果集执行的内存分页,并非是物理分页)。可以在sql内直接拼写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页,比如:MySQL数据的时候,在原有SQL后面拼写limit。
- 分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的SQL,然后重写SQL,根据dialect方言,添加对应的物理分页语句和物理分页参数。
4.MyBatis是如何将sql执行结果封装为目标对象并返回的?有哪几种映射方式?
- 使用标签,一一定义数据库列名和对象属性名之间的映射关系;
- 使用sql列的别名,将列的别名写为对象的属性名;
有了列名与属性名的映射关系后,MyBatis通过反射创建对象,同时使用反射给对象的属性一一赋值并返回,(找不到映射关系的属性,是无法赋值的)
5.说说MyBatis的缓存机制:
MyBatis整体:
一级缓存localCache:
在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的sql,MyBatis提供了一次缓存的方案优化这部分场景,如果是相同的sql语句,会优化命中一级缓存,避免直接对数据库进行查询,提供性能。
每个sqlsession中持有了exectuor,每个Executor中有一个LocalCache。当用户发起查询时,Mybatis根据当前执行的语句生成MappedStatement,在Local Cache进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入Local Cache,最后返回结果给用户。具体实现类的类关系图如图所示:
1.MyBatis一级缓存的生命周期和sqlsession一致;
2.MyBatis一级缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺;
3. Mybatis的一级缓存最大范围是sqlsession内部,有多个sqlsession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为statement。
二级缓存:
在上文中提到的一级缓存中,其最大的共享范围就是一个sqlsession内部,如果多个sqlsession之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询,具体的工作流程如下所示。
二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存被多个sqlsession共享,是一个全局的变量。
当开启缓存后,数据的查询执行的流程为:
二级缓存-->一级缓存-->数据库
- MyBatis的二级缓存相对于一级缓存来说,实现了sqlsession之间缓存数据的共享,同时粒度更加细,能够到namespace级别,通过cahce接口实现类不同的组合,对cache的可控性也更强;
- Mybatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻;
- 在分布式环境下,由于默认的MyBatis Cahce 实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将Mybatis 的 Cache接口实现,有一定的开发成本,直接使用Redis、Memcached等分布式缓存可能成本更低,安全性也更高。
6.如何实现批量插入?
首先,创建一个简单的insert语句:
<insert id="insertname">
insert into names (name) values (#{value})
</insert>
然后在java代码中像下面这样执行批处理插入:
list<string> names = new arraylist();
names.add("fred");
names.add("barney");
names.add("betty");
names.add("wilma");
// 注意这里 executortype.batch
sqlsession sqlsession = sqlsessionfactory.opensession(executortype.batch);
try {
namemapper mapper = sqlsession.getmapper(namemapper.class);
for (string name : names) {
mapper.insertname(name);
}
sqlsession.commit();
}catch(Exception e){
e.printStackTrace();
sqlSession.rollback();
throw e;
}
finally {
sqlsession.close();
}
7.MyBatis是否支持延迟加载?如果支持,它的实现原理是什么?
8.#{}和${}的区别是?
取值操作:#{属性值}、${属性值}
- #取值采用占位符的方式,更加安全,能防止sql注入;
- $一般使用场景,以字段为参数时使用;
- 使用#号时候,默认为添加""号;
- 能使用#{},绝不用${}
#{} | ${} | |
1.编译过程 | (占位符)动态解析->预编译处理->执行 | 动态解析->字符串替换(编译)->执行 |
2.是否自动加入单引号 | 对应的变量会自动添加 | 不会 |
3.安全性 | 可以有效的防止SQL注入,提高系统安全性 | 不会 |
4.处理方式 | MyBatis在处理#{}时,会将sql中的#{}替换成?号,调用PreparedStatement的set方法来赋值 | MyBatis在处理${}时,是把${}替换成变量的值 |
(什么是sql注入?sql注入是一种常见的web安全漏洞,主要形成的原因是在数据交互中,前端的数据传入后台处理时,没有做到严格的判断,导致其传入的数据拼接到SQL语句中后,被当作SQL语句中的一部分执行,从而导致数据库受损)
9. MyBatis实现一对一有几种方式?如何操作?
有联合查询和嵌套查询两种
- 联合查询是几个表联合查询,只查询一次,通过resultMap里面配置association节点配置一对一的类就可以完成;
- 嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。
10. MyBatis中比如UserMapper.java是接口,为什么没有实现类还能调用?
使用JDK动态代理+MapperProxy。本质上调用的是MapperProxy的invoke方法