Java面试题(5)mybatis、数据库

一、mybatis的优缺点

优点:

1、基于sql语句编程,相当灵活,不会对应用程序或数据库的现有设计造成任何影响,SQL写在xml里,解除sql与程序代码的耦合,便于统一管理;提供xml标签,支持编写动态的sql语句,并可重用。

2、与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接。

3、很好的与各种数据库兼容(因为mybatis使用JDBC来连接数据库,所以只要JDBC支持的数据库Mybatis都支持)

4、能够与Spring很好的集成。

5、提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。

缺点:

1、sql 语句的编写工作量较大,尤其当字段多,关联表多时,对开发人员编写sql语句的功底有一定的要求。

2、sql 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

二、Mybatis 与 Hibernate 对比

Hibernate :面向对象,Mybatis :面向表结构

SQL 和 ORM的争论,永远都不会终止

开发速度的对比:

Hibernate 的真正掌握的话比mybatis要难一些,mybatis框架相对简单很容易上手,但也相对简陋些。

比起两者的开发速度,不仅仅要考虑到两者的特性及性能,更要根据项目需求去考虑究竟哪一个更适合项目开发,比如:一个项目中用到的复杂查询基本没有,就是简单的增删改查,这样选择Hibernate 效率就很快了,因为基本的sql语句已经被封装好了,根本不需要你去写sql语句,这就节省了大量的时间,但是对于一个大型项目,复杂语句较多,这样再去选择Hibernate 就不是一个太好的选择,选择mybatis就会加快许多,而且语句的管理也比较方便。

开发工作量的对比:

Hibernate 和 nybatis都有相应的代码生成工具。可以生成简单基本的DAO层方法。针对高级查询,mybatis需要手动编写sql语句,以及resultMap。而Hibernate 有良好的映射机制,开发者无需关心SQL的生成与结果映射,可以更专注于业务流程。

SQL 优化方面:(mybatis会比较灵活)

Hibernate 的查询会将表中的所有字段查询处理,这一点会有性能消耗。Hibernate 也可以自己写SQL来指定需要查询字段,但这样就破坏了Hibernate 开发的简洁性。而mybatis的SQL是手动编写的,所以可以按需求指定查询的字段。

Hibernate SQL语句的调优需要将SQL打印出来,而Hibernate 的SQL被很多人嫌弃因为太丑了。mybatis的SQL是自己手动写的所以调整方便。但Hibernate 具有自己的日志统计。mybatis本身不带日志统计,使用Log4j进行日志记录。

对象管理对比:(Hibernate 会比较好)

Hibernate 是完整的对象/关系映射解决方案,它提供了对象状态管理(state management)的功能,使开发者不再需要理会底层数据库系统的细节。也就是说,相当于常见的JDBC/SQL 持久层方案中需要管理SQL语句,Hibernate 采用了更自然的面向对象的视角来持久化Java应用中的数据。

换句话说,使用Hibernate 的开发者应该总是关注对象的状态(state),不必考虑SQL语句的执行。这部分细节已经由Hibernate 掌控妥当,只有开发者在进行系统性能调优的时候才需要进行了解。而mybatis在这一块没有文档说明,用户需要对对象自己进行详细的管理。

缓存机制对比:

相同点:都可以实现自己的缓存或使用其它第三方缓存方案,创建适配器来完全覆盖缓存行为。

不同点:Hibernate 的二级缓存配置在SessionFactory生成的配置文件中进行详细配置,然后再在具体的表-对象映射中配置是哪种缓存。

mybatis的二级缓存配置都是在每个具体的表-对象映射中进行详细配置,这样针对不同的表可以自定义不同的缓存机制。并且mybatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现。

两者比较:因为Hibernate 对查询对象有着良好的管理机制,用户无需关心SQL。所以在使用二级缓存时如果出现脏数据,系统会报出错误并提示。

而mybatis在这一方面,使用二级缓存时需要特别小心。如果不能完全确定数据更新操作的波及范围,避免Cache的盲目使用。否则,脏数据的出现会给系统的正常运行带来很大的隐患。

Hibernate 功能强大,数据库无关性好,O/R映射能力强,如果你对Hibernate 相当精通,而且对Hibernate 进行了适当的封装,那么你的项目整个持久层代码会相当简单,需要写的代码很少,开发速度很快,非常爽。

Hibernate 的缺点是:学习门槛不低,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡取得平衡,以及怎样用好Hibernate 方面需要你的经验和能力都很强才行。

iBatis 入门简单,即学即用,提供了数据库查询的自动对象绑定功能,而且延续了很好的SQL使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。

iBatis 的缺点就是框架还是比较简陋,功能尚且缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且太不容易适应快速数据库修改。

三、#{} 和${}的区别是什么?

#{} 是预编译处理,是占位符,${}是字符串替换,是拼接符。

Mybatis 在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement来赋值;

Mybatis 在处理${}时,就会把${}替换成变量的值,调用statement来赋值;

#{} 的变量替换是在DBMS中,变量替换后,#{} 对应的变量自动加上单引号

${} 的变量替换是在DBMS外,变量替换后,${} 对应的变量不会加上单引号

使用#{}可以有效的防止SQL注入,提高系统的安全性。

四、简述Mybatis 的插件运行原理,如何编写一个插件

mybatis插件指的就是mybatis的拦截器

mybatis只支持针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,拦截那些指定需要拦截的方法。

编写插件:实现mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,在配置文件中配置编写的插件。

五、数据库-索引的原理

索引用来快速地寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表。

索引的原理是:就是把无序的数据变成有序的查询。

1、把创建了索引的列的内容进行排序

2、对排序结果生成倒排序

3、在倒排表内容上拼上数据地址链

4、在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据。

六、mysql聚簇和非聚簇索引的区别

都是B+树的数据结构

聚簇索引:将数据存储与索引放到一块,并且是按照一定的顺序组织的,找到索引也就找到了数据,数据的物理存放顺序与索引顺序是一致的,即:只要索引是相邻的,那么对应的数据一定也是相邻地存放在磁盘上的。

非聚簇索引:叶子节点不存储数据、存储的是数据行地址、也就是说根据索引查找到数据行的位置再取磁盘查找数据,这个就有点类似一本书的目录,比如我们要找第三章第一节,那么我们先在这个目录里面找,找到对应的页码后再去对应的页码看文章。

优势:
1、查询通过聚簇索引可以直接获取数据,相比非聚簇索引需要第二次查询(非覆盖索引的情况下)效率更高
2、聚簇索引对于范围查询的效率很高,因为其数据是按照大小排列的
3、聚簇索引适合用在排序的场合,非聚簇索引不合适

劣势:
1、维护索引很昂贵,特别是插入新行或者主键被更新导致要分页的时候,建议在大量插入新行后,选在负载较低的时间段,通过OPTIMIZE TABLE优化表,
因为必须被移动的行数据可能造成碎片。使用独享表空间可以弱化碎片。
2、表因为使用UUID(随机ID)作为主键,使数据存储稀疏,这就会出现聚簇索引有可能有比全表扫描更慢,所以建议使用int的suto_increment作为主键 3、如果主键比较大的话,那辅助索引将会变得更大,因为辅助索引的叶子存储的是主键值;过长的主键值,会导致非叶子节点占用更多的物理空间

InnoDB中一定有主键,主键一定是聚簇索引,不手动设置,则会使用unique索引,没有unique索引,则会使用数据库内部的一个行的隐藏id来当作主键索引。在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要第二次查找,非聚簇索引都是辅助索引,像复合索引,前缀索引,唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值。

MyISM使用的是非聚簇索引,没有聚簇索引,非聚簇索引的两棵B+树看上去没什么不同,节点的结构完全一致只是存储的内容不同而已,主键索引B+树的节点存储了主键,辅助健索引B+树存储了辅助键。表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别。由于索引树是独立的,通过辅助键检索无需访问主键的索引树。

如果涉及到大数据量的排序,全表扫描,count之类的操作的话,还是MyISA占优势些,因为索引所占空间小,这些操作是需要在内存中完成的。

七、mysql索引的数据结构,各自优劣

索引的数据结构和具体存储引擎的实现有关,在mysql中使用较多的索引有Hash索引,B+树索引等,InnoDB存储引擎的默认索引实现为:B+树索引。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择B+Tree索引。

B+树:

B+树是一个平衡的多叉树,从根节点到每个叶子节点的高度差值(路径值)不超过1,而且同层级的节点间有指针互相链接。在B+树上的常规检索,从根节点到叶子节点的搜索效率基本相当,不会出现大幅波动,而且基于索引的顺序扫描时,也可以利用双向指针快速左右移动,效率非常高。因此,B+树索引被广泛应用于数据库,文件系统等场景。

哈希索引:

哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。

如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;前提是键值都是唯一的。如果键值不是唯一的,就需要先找到该键值所在位置,然后再根据链表往后扫描,直到找到相应的数据;

如果是范围查询检索,这时候哈希所用就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;

哈希索引也没办法利用索引完成排序,以及like 'xxx%' 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);

哈希索引也不支持多列联合索引的最左匹配规则;

B+树索引的关键字检索效率比较平均,不像B树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在哈希碰撞的问题。

八、索引涉及的原则?

查询更快,占用空间更小

适合建索引:

1、适合索引的列是出现在where子句中的列,或者连接子句中指定的列

2、基数较小的类,索引效果较差,没有必要在此列建立索引

3、使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间,如果搜索词超过索引前缀长度,则使用索引排除不匹配的行,然后检查其余行是否可能匹配。

4、不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。

5、定义有外键的数据列一定要建立索引。

不适合建索引:

6、更新频繁字段不适合列创建索引

7、若是不能有效区分数据的列不适合做索引列(如性别、男女未知、最多也就三种,区分度实在太低)

8、尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。

9、对于那些查询中很少涉及的列,重复值比较多的不要建立索引

10、对于定义为text、image和bit的数据类型的列不要建立索引。

九、锁的类型有哪些?

基于锁的属性分类:共享锁、排他锁。

基于锁的粒度分类:行级锁(INNODB)、表级锁(INNODB、MYISAM)、页级锁(BDB引擎)、记录锁、间隙锁、临键锁。

基于锁的状态分类:意向共享锁、意向排它锁。

  • 共享锁(Share Lock)
共享锁又称读锁,简称S锁;当一个事务为数据加上读锁之后,其它事务只能对该数据加读锁,而不能对数据加写锁,
直到所有的读锁释放之后其他事务才能对其进行加持写锁。共享锁的特性主要是为了支持并发的读取数据,读取数据的时候不支持修改,
避免出现重复读的问题。
  • 排他锁(exclusive Lock)
排他锁又称写锁,简称X锁;当一个事务为数据加上写锁时,其他请求将不能再为数据加任何锁,直到锁释放之后,其他事务才能对数据进行加锁。
排他锁的目的是在数据修改时候,不允许其它人同时修改,也不允许其他人读取。避免了出现脏数据和脏读的问题。
  • 表锁
表锁是指上锁的时候锁住的是整个表,当下一个事务访问该表的时候,必须等前一个事务释放了锁才能进行对表进行访问;
特点:粒度大,加锁简单,容易冲突;
  • 行锁
行锁是指上锁的时候锁住的是表的某一行或多行记录,其他事务访问同一张表时,只有被锁住的记录不能访问,其他的记录可正常访问;
特点:粒度小,加锁比表锁麻烦,不容易冲突,相比表锁支持的并发要高;
  • 记录锁(Record Lock)
记录锁也属于行锁中的一种,只不过记录锁的范围只是表中的某一条记录,记录锁是说事务在加锁后锁住的只是表的某一条记录。
精准条件命中,并且命中的条件字段是唯一索引
加了记录锁之后数据可以避免数据在查询的时候被修改的重复读问题,也避免了在修改的事务提交前被其他事务读取的脏读问题。
  • 页锁
页级锁是mysql中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。
所以取了折衷的页级,一次锁定相邻的一组记录。
特点:开销和加锁时间介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般
  • 间隙锁(Gap Lock)
属于行锁中的一种,间隙锁是在事务加锁后其锁住的是表记录的某一个区间,当表的相邻ID之间出现空隙则会形成一个区间,遵循左开右闭原则。
范围查询并且查询未命中记录,查询条件必须命中索引、间隙锁只会出现在REPEATABLE_READ(重复读)的事务级别中。
触发条件:防止幻读问题,事务并发的时候,如果没有间隙锁,就会发生如下图的问题,在同一个事务里,A事务的两次查询的结果会不一样。
比如表里面的数据ID为1,4,5,7,10,那么会形成以下几个间隙区间,—n-1区间,7-10区间,10-n区间(-n代表负无穷大,n代表正无穷大)
  • 临键锁(next-key lock)
也属于行锁的一种,并且它是INNODB的行锁默认的算法,总结来说它就是记录锁和间隙锁的组合,临键锁会把查询出来的记录锁住,
同时也会把该范围查询内的所有间隙空间也会锁住,再之它会 把相邻的下一个区间也会锁住。 触发条件:范围查询并命中,查询命中了索引。 结合记录锁和间隙锁的特性,临键锁避免了在范围查询时出现脏读、重复读、幻读问题。加了临键锁之后,在范围区间内数据不允许被修改和插入。

场景:

如果当事务A加锁成功之后九设置一个状态告诉后面的人,已经有人对表里的行加了一个排他锁了,你们不能对整个表加共享锁或排他锁了,那么后面需要对整个表加锁的人只需要获取这个状态就知道自己是不是可以对表加锁,避免了对整个索引树的每个节点扫描是否加锁,而这个状态就是意向锁。

意向共享锁

当一个事务试图对整个表进行加共享锁之前,首先需要获得这个表的意向共享锁。

意向排他锁

当一个事务试图对整个表进行加排他锁之前,首先需要获得这个表的意向排他锁。

十、mysq执行计划怎么看(mysql优化)

原文地址:https://www.cnblogs.com/nastu/p/15213523.html