Java面试考点解析-- 数据库篇Spring+MyBatis整合

Posted newbie27

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java面试考点解析-- 数据库篇Spring+MyBatis整合相关的知识,希望对你有一定的参考价值。

-------------------------------   一、数据库基础知识 -------------------------------  

1、在进行数据库编程时,连接池有什么作用? 
  由于创建连接和释放连接都有很大的开销(尤其是数据库服务器不在本地时,每次建立连接都需要进行TCP的三次握手,释放连接需要进行TCP四次握手,造成的开销是不可忽视的),为了提升系统访问数据库的性能,可以事先创建若干连接置于连接池中,需要时直接从连接池获取,使用结束时归还连接池而不必关闭连接,从而避免频繁创建和释放连接所造成的开销,这是典型的用空间换取时间的策略(浪费了空间存储连接,但节省了创建和释放连接的时间)。

    池化技术在Java开发中是很常见的,在使用线程时创建线程池的道理与此相同。基于Java的开源数据库连接池主要有:C3P0、Proxool、DBCP、BoneCP、Druid等。
    补充:在计算机系统中时间和空间是不可调和的矛盾,理解这一点对设计满足性能要求的算法是至关重要的。大型网站性能优化的一个关键就是使用缓存,而缓存跟上面讲的连接池道理非常类似,也是使用空间换时间的策略。可以将热点数据置于缓存中,当用户查询这些数据时可以直接从缓存中得到,这无论如何也快过去数据库中查询。当然,缓存的置换策略等也会对系统性能产生重要影响,对于这个问题的讨论已经超出了这里要阐述的范围。

 

-------------------------------   二、数据库调优  -------------------------------  

1、如何进行数据库优化?

  数据优化的定位:查找,定位慢查询,并优化 
  优化方式如下:

  • (1)创建索引:创建合适的索引,我们就可以在索引中查询,查询到以后直接找对应的额记录 
  • (2)分表:当一张表的数据比较多或者一张表的某些字段的值比较多并且很少使用时,采用水平分表或垂直分表来优化,比如spu表 
  • (3)读写分离:当一台服务器不能满足需要时,采用将读写分离的方式进行集群 
  • (4)使用缓存:使用redis来进行缓存

2、如果定位慢查询?

  在项目自验或项目转测之前,在启动mysql数据库时开启慢查询,并且把执行慢的语句写到日志中,在运行一定的时间后,通过查看日志,找到对应的慢查询信息。使用EXPLAIN +慢查询语句,,来详细分析语句的问题。

3、数据库优化-数据库表设计遵循范式

  • 三范式:首先符合1nf,才能满足2nf,进一步满足3nf 
  • 反三范式:没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。具体做法是,在概念数据模型设计时遵守三范式,降低方式标准的工作放到物理数据模型设计时考虑,降低范式就是增加字段,允许冗余.订单和订单项,相册浏览次数和照片的浏览次数.

 4、数据库优化-选择合适的数据库引擎

 (1)Myisam和innodb存储引擎?

  • Myisam存储引擎:如果表对事务要求不高,同时是以查询和添加为主的,我们考虑使用myisam存储引擎,比如论坛中的发帖表,回复表
  • Innodb存储引擎:如果对事务要求高,保存的数据都是重要数据.我们建议使用innodb,比如订单表,账号表

 (2)Myisam和innodb的区别: 

  • 1. 事务安全:myisam不支持事务,而innodb支持事务。 
  • 2. 查询和添加速度:myisam不用支持事务就不用考虑同步锁,查找和添加的速度快 
  • 3. 支持全文索引:myisam支持,innodb不支持 
  • 4. 锁机制:myisam支持表锁,innodb支持行锁 
  • 5. 外键:myisam不支持外键,innodb支持外键

 

5、数据库优化-选择合适的索引

  索引是帮助DBMS高效获取数据的数据结构. 
  索引分为:普通索引,唯一索引,主键索引,全文索引 

  • 1.普通索引:允许重复的值出现 
  • 2.唯一索引:除了不能有重复的记录外,其它和普通索引一样.(用户名;用户身份证;手机号) 
  • 3.主键索引:是随着设定主键而创建的;也就是把某个列设为主键的时候,数据库就会给该列创建索引;唯一且没有null值 
  • 4.全文索引:用来对表中文本域(char,varchar,text)进行索引,全文索引针对myisam

 6、使用索引的一些技巧

(1)索引的弊端: 

  • 占用磁盘空间. 
  • 对dml(插入,修改.删除)操作有影响,变慢 

(2)索引的使用场景: 

  • 肯定在where条件经常使用,如果不做查询就没有意义 
  • 该字段的内容不是唯一的几个值(sex). 
  • 字段内容不是频繁变化

(3)使用索引的技巧: 

  • 1. 对于创建的多列索引(复合索引),不是使用的第一部分就不会使用索引(最左匹配) 
  • 2. 对于使用like查询,查询如果是”%aaa”不会使用到索引,而”aaa%”会使用到索引 
  • 3. 如果条件中有or,有条件没有使用索引,即使其中有条件带索引,也不会使用。也就是说,要求索引的所有字段,都必须单独使用时才能使用索引. 
  • 4. 如果列类型是字符串,拿一定要在条件中将数据使用引号引用起来,否则索引失效 
  • 5. 如果mysql估计使用全表扫描要比索引快,则不适用索引.例子:表里只有一条记录

 

7、数据库优化-分表

  分表分为水平分表(按行)和垂直分表(按列)
  水平分表:在实际操作中,mysql表数据一般达到百万级别,查询效率会很低,容易造成表锁,甚至堆积很多连接,直接挂掉.水平分表能够很大程度的减少这些压力.
  垂直分表:如果一张表中某个字段值非常多(长文本,二进制等),而且只有在很少的情况下会查询,比如商品的详情描述,这时候就可以把字段单个放到一个表,通过外键与原表关联起来
  水平分表策略: 

  • 1. 按时间分表:这种分表方式有一定的局限性,当数据有较强的时效性.如微博发布纪录,微信消息纪录等,这种数据很少会有用户查询几个月前的数据,这时可以按月分表 
  • 2. 按区间范围分表:一般在有严格的自增id需求上,如按照user_id水平分表 
  • 3. Hash分表(用的多):通过一个原始目标的id或者名称通过一定的hash算法计算出数据库存储表的表名,然后访问相应的表.

 

8、数据库优化-数据库的读写分离

  一台数据库支持的最大并发连接数是有限的,如果用户并发访问太多,一台服务器满足不了要求时,可以集群处理.mysql的集群处理技术最常用的是读写分离,

  • 主从同步:数据库最终会把数据持久化到磁盘,如果集群必须确保每个数据库服务器的数据时一致的.能改变数据库数据的操作都往主数据库去写,而其他的数据库从主数据库上同步数据 
  • 读写分离:使用负载均衡来实现写的操作都往主数据.而读的操作都往从数据库去

 

9、数据库优化-缓存

  在持久层(dao)和数据库(db)之间添加一个缓存层,如果用户访问的数据已经缓存起来时,在用户访问数据时从缓存中获取,不用访问数据库,而缓存是在操作内存,访问速度快
增加缓存的作用:减少数据库服务器压力,减少访问时间
  Java中常用的缓存: 
  (1)hibernate的二级缓存,这种缓存不支持分布式缓存 
  (2)可以使用redis来作为中央缓存,对缓存的数据进行集中处理

 

10、SQL语句优化小技巧

(1)DDL优化: 

  • 1. 通过禁用索引来提供导入数据性能,这个操作主要针对有数据的表追加数据 
  • 2. 关闭唯一校验 
  • 3. 修改事务提交方式(导入)(变多次提交为一次)

(2)DML优化: 把多条插入的语句合并为一条(变多次提交为一次)
(3)DQL优化:

  • Order by 优化 
  • 多用索引排序
  • 普通结果排序(非索引排序)
  • Group by 优化:如果对排序的结果没有排序的需求,可以考虑在其后面加上order by nul

(4)子查询优化
(5)Or优化: 

  • 1.or两边都是用索引字段做判读,性能好 
  • 2.or两边,有一边不用,性能差 
  • 3.如果name=”a” or name=”b”,这种方式,索引失效

(6)Limit优化

 

-------------------------------   三、数据库事务  -------------------------------  

1、JDBC中如何进行事务处理? 
  Connection提供了事务处理的方法,通过调用setAutoCommit(false)可以设置手动提交事务;当事务完成后用commit()显式提交事务;如果在事务处理过程中发生异常则通过rollback()进行事务回滚。除此之外,从JDBC 3.0中还引入了Savepoint(保存点)的概念,允许通过代码设置保存点并让事务回滚到指定的保存点。

 

2、事务的ACID是指什么? 

  • - 原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作的失败都会导致整个事务的失败; 
  • - 一致性(Consistent):事务结束后系统状态是一致的; 
  • - 隔离性(Isolated):并发执行的事务彼此无法看到对方的中间状态; 
  • - 持久性(Durable):事务完成后所做的改动都会被持久化,即使发生灾难性的失败。通过日志和同步备份可以在故障发生后重建数据。
  关于事务,首先需要知道的是,只有存在并发数据访问时才需要事务。当多个事务访问同一数据时,可能会存在5类问题,包括3类数据读取问题(脏读、不可重复读和幻读)和2类数据更新问题(第1类丢失更新和第2类丢失更新)。
 (1)脏读(Dirty Read):A事务读取B事务尚未提交的数据并在此基础上操作,而B事务执行回滚,那么A读取到的数据就是脏数据。
 (2)不可重复读(Unrepeatable Read):事务A重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务B修改过了。  
 (3)幻读(Phantom Read):事务A重新执行一个查询,返回一系列符合查询条件的行,发现其中插入了被事务B提交的行。

 

3、数据库(mysql)的四个事务隔离级别

  数据库通常会通过锁机制来解决数据并发访问问题,按锁定对象不同可以分为表级锁和行级锁;按并发事务锁定关系可以分为共享锁和独占锁。
直接使用锁是非常麻烦的,为此数据库为用户提供了自动锁机制,只要用户指定会话的事务隔离级别,数据库就会通过分析SQL语句然后为事务访问的资源加上合适的锁,此外,数据库还会维护这些锁通过各种手段提高系统的性能,这些对用户来说都是透明的。

  ANSI/ISO SQL 92标准定义了4个等级的事务隔离级别,如下表所示:

隔离级别 脏读 不可重复读 幻读 第一类丢失更新 第二类丢失更新
READ UNCOMMITED 允许 允许 允许 不允许 允许
READ COMMITTED 不允许 允许 允许 不允许 允许
REPEATABLE READ 不允许 不允许 允许 不允许 不允许
SERIALIZABLE 不允许 不允许 不允许 不允许 不允许
需要说明的是,事务隔离级别和数据访问的并发性是对立的,事务隔离级别越高并发性就越差。所以要根据具体的应用来确定合适的事务隔离级别,这个地方没有万能的原则。

 

4、锁机制有什么用?什么是悲观锁和乐观锁机制。?

  有些业务逻辑在执行过程中要求对数据进行排他性的访问,于是需要通过一些机制保证在此过程中数据被锁住不会被外界修改,这就是所谓的锁机制。 
  悲观锁,顾名思义悲观的认为在数据处理过程中极有可能存在修改数据的并发事务(包括本系统的其他事务或来自外部系统的事务),于是将处理的数据设置为锁定状态。悲观锁必须依赖数据库本身的锁机制才能真正保证数据访问的排他性。

  乐观锁,顾名思义,对并发事务持乐观态度(认为对数据的并发操作不会经常性的发生),通过更加宽松的锁机制来解决由于悲观锁排他性的数据访问对系统性能造成的严重影响。最常见的乐观锁是通过数据版本标识来实现的,读取数据时获得数据的版本号,更新数据时将此版本号加1,然后和数据库表对应记录的当前版本号进行比较,如果提交的数据版本号大于数据库中此记录的当前版本号则更新数据,否则认为是过期数据无法更新。

  提示:使用乐观锁会增加了一个版本字段,很明显这需要额外的空间来存储这个版本字段,浪费了空间,但是乐观锁会让系统具有更好的并发性,这是对时间的节省。因此乐观锁也是典型的空间换时间的策略。

 

-------------------------------   四、Spring 数据源、事务支持  -------------------------------  

1、如何在Spring IoC容器中配置数据源? 

  在Spring bean配置文件中,配置DataSource数据源的实例bean。配置方法如下:
(1)DBCP数据源配置:

<context:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="${jdbc.driverClassName}"/>
  <property name="url" value="${jdbc.url}"/>
  <property name="username" value="${jdbc.username}"/>
  <property name="password" value="${jdbc.password}"/>
</bean>

(2)C3P0数据源配置:

<context:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
  <property name="driverClass" value="${jdbc.driverClassName}"/>
  <property name="jdbcUrl" value="${jdbc.url}"/>
  <property name="user" value="${jdbc.username}"/>
  <property name="password" value="${jdbc.password}"/>
</bean>

 

 2、Spring事务的传播特性和隔离级别

  事务的传播特性,是指存在多个事务时,应该采用什么策略进行处理。
(1)传播行为分为以下七种:

  • PROPAGATION_MANDATORY :表示该方法必须运行在一个事务中。如果当前没有事务正在发生,将抛出一个异常
  • PROPAGATION_NESTED :表示如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于封装事务进行提交或回滚。如果封装事务不存在,行为就像PROPAGATION_REQUIRES一样。
  • PROPAGATION_NEVER :表示当前的方法不应该在一个事务中运行。如果一个事务正在进行,则会抛出一个异常。
  • PROPAGATION_NOT_SUPPORTED :表示该方法不应该在一个事务中运行。如果一个现有事务正在进行中,它将在该方法运行期间被挂起。
  • PROPAGATION_SUPPORTS :表示当前方法不需要事务性上下文,但是如果有一个事务已经在运行的话,它也可以在这个事务里运行。
  • PROPAGATION_REQUIRES_NEW :表示当前方法必须在它自己的事务里运行。一个新的事务将被启动,而且如果有一个现有事务在运行的话,则将在这个方法运行期间被挂起。
  • PROPAGATION_REQUIRES :表示当前方法必须在一个事务中运行。如果一个现有事务正在进行中,该方法将在那个事务中运行,否则就要开始一个新事务。

(2)事务隔离级别 

  • ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别。
  • ISOLATION_READ_UNCOMMITTED :允许读取尚未提交的更改。可能导致脏读、幻影读或不可重复读。
  • ISOLATION_READ_COMMITTED :允许从已经提交的并发事务读取。可防止脏读,但幻影读和不可重复读仍可能会发生。
  • ISOLATION_REPEATABLE_READ :对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻影读仍可能发生。
  • ISOLATION_SERIALIZABLE :完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。

3、Spring支持的事务管理类型有哪些?你在项目中使用哪种方式? 

  Spring支持编程式事务管理和声明式事务管理。许多Spring框架的用户选择声明式事务管理,因为该方式和应用程序的关联较少,更加符合轻量级容器的概念。声明式事务管理要优于编程式事务管理,尽管在灵活性方面它弱于编程式事务管理,因为编程式事务允许你通过代码控制业务。

  事务分为全局事务和局部事务。全局事务由应用服务器管理,需要底层服务器JTA支持(如WebLogic)。局部事务和底层采用的持久化方案有关,例如使用JDBC进行持久化时,需要使用Connetion对象来操作事务;而采用Hibernate进行持久化时,需要使用Session对象来操作事务。

  Spring提供的事务管理器,如下所示:

  • DataSourceTransactionManager :注入DataSource
  • HibernateTransactionManager :注入SessionFactory
  • JdoTransactionManager :管理JDO事务
  • JtaTransactionManager :使用JTA管理事务
  • PersistenceBrokerTransactionManager :管理Apache的OJB事务

  以上这些事务管理器的父接口都是PlatformTransactionManager。Spring的事务管理机制是一种典型的策略模式。

PlatformTransactionManager代表事务管理接口,该接口定义了三个方法,该接口并不知道底层如何管理事务,但是它的实现类必须提供getTransaction()方法(开启事务)、commit()方法(提交事务)、rollback()方法(回滚事务)的多态实现,这样就可以用不同的实现类代表不同的事务管理策略。

 

 

-------------------------------   五、MyBatis框架  -------------------------------  

1、ORM是什么?ORM框架是什么?

  对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单来说,将程序中的兑现自动持久化到关系数据库中。

127、持久层设计要考虑的问题有哪些?
  所谓"持久"就是将数据保存到可掉电式存储设备中以便今后使用,简单的说,就是将内存中的数据保存到关系型数据库、文件系统、消息队列等提供持久化支持的设备中。持久层就是系统中专注于实现数据持久化的相对独立的层面。

  持久层设计的目标包括: 

  • - 数据存储逻辑的分离,提供抽象化的数据访问接口。 
  • - 数据访问底层实现的分离,可以在不修改代码的情况下切换底层实现。 
  • - 资源管理和调度的分离,在数据访问层实现统一的资源调度(如缓存机制)。 
  • - 数据抽象,提供更面向对象的数据操作。

2、为什么使用Mybatis?

  Mybatis是java中的orm框架,屏蔽了jdbc的api底层访问细节,使我们不用与jdbc打交道,就可以完成对数据的持久化操作。MyBatis将sql语句与Java代码分离,提供将结果集自动封装为实体对象和对象的集合的功能。queryForList返回对象集合.用queryForObject返回单个对象.提供自动将实体对象的属性传递给sql语句的参数 。

3、MyBatis中使用#和$书写占位符有什么区别?

  • #将传入的数据都当成一个字符串,会对传入的数据自动加上引号;
  • $将传入的数据直接显示生成在SQL中。注意:使用$占位符可能会导致SQL注射攻击,能用#的地方就不要使用$。
  • 写order by子句的时候应该用$而不是#。

4、解释一下MyBatis中命名空间(namespace)的作用。 
  在大型项目中,可能存在大量的SQL语句,这时候为每个SQL语句起一个唯一的标识(ID)就变得并不容易了。为了解决这个问题,在MyBatis中,可以为每个映射文件起一个唯一的命名空间,这样定义在这个映射文件中的每个SQL语句就成了定义在这个命名空间中的一个ID。只要我们能够保证每个命名空间中这个ID是唯一的,即使在不同映射文件中的语句ID相同,也不会再产生冲突了。

5、MyBatis中的动态SQL是什么意思? 
  对于一些复杂的查询,我们可能会指定多个查询条件,但是这些条件可能存在也可能不存在,此时就需要根据用户指定的条件动态生成SQL语句。MyBatis中用于实现动态SQL的元素主要有以下几种方式:

  • - if 
  • - choose / when / otherwise 
  • - trim 
  • - where 
  • - set 
  • - foreach

 

以上是关于Java面试考点解析-- 数据库篇Spring+MyBatis整合的主要内容,如果未能解决你的问题,请参考以下文章

程序员面试备战篇:69个经典Spring面试专题解析(干货分享答案)

华为OD机试 - 招聘(JavaScript) | 机试题+算法思路+考点+代码解析 2023

面试Spring 事务面试考点吐血整理(建议珍藏)

Java面试高Spring Boot+Sentinel+Nacos高并发已撸完

华为OD机试 -报数游戏(Java) | 机试题+算法思路+考点+代码解析 2023

Java面试高频题:Spring Boot+Sentinel+Nacos高并发已撸完