青岛 两年工作经验 面试遇到的问题

面试问题


1. 事务 声明式事务
  1. @Transactional 注解的事务隔离级别和传播行为

    首先@Transactional支持9个属性的设置,但是就目前实际开发中最长用到只有三个属性设置。所以下面主要就是针对readOnly、propagation、isolation 来说一下。

    • Transactional(readOnly = true) readOnly=true 表明注解的方法为只会读取数据的方法,不会修改数据。
    • Transactional(readOnly = false) readOnly=false 表明注解的方法回去进行数据的修改,包括修改和删除。
  2. @Transactional(readOnly = true,isolation = Isolation.DEFAULT) 可以设置的用5种等级

    实际项目开发中使用事务,可能会遇到四种情况:

    1.脏读(dirty read) 这是其中一个事务读取到另外一个事务尚未提交的修改时,会产生脏读;

    2.不可重复读(non-repeatable read) 这是同一个事务中多次进行同样的查询,但是由于其他的事务修改数据造成每次查询返回结果不相同,此时就是发生了非重复读;

    3.幻读(phantom read) 这是由于同一查询在同一事务中执行多次,但是由于其他事务做了新增或者删除的操作,照成多次查询返回的数据量不相同,就造成了幻读;

    4.串行化(Serializable) 最严格的级别,事务串行执行,资源消耗最大;

    不可重复读和幻读比较:

    ​ 两者有些相似,但是前者针对的是update或delete,后者针对的insert。

    Spring提供的5种事务隔离级别:

    • DEFAULT 默认隔离级别:

      DEFAULT是数据库的默认隔离级别,就目前的mysql来说,默认的隔离级别为**可重复读(REPEATABLE_READ)**隔离级别;
      
    • READ_UNCOMMITTED 读未提交隔离级别:
      READ_UNCOMMITTED是最低的事隔离级别,表示的是在某事务更改数据未提交之前,其他并发事务是可以读取到当前的数据的。这可能会造成丢失更新,脏读,不可重复度,幻读。

    • READ_COMMITTED 读已提交隔离级别:
      READ_COMMITTED是以操作同一条数据为前提,以操作同一行数据为前提,读事务允许其他读事务和写事务,未提交的写事务禁止其他读事务和写事务。此隔离级别可以防止更新丢失、脏读,但不能防止不可重复读、幻读。此隔离级别可以通过“瞬间共享读锁”和“排他写锁”实现。

    • REPEATABLE_READ 可重复读取级别:

      REPEATABLE_READ保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务影响。以操作同一行数据为前提,读事务禁止其他写事务,但允许其他读事务,未提交的写事务禁止其他读事务和写事务。此隔离级别可以防止更新丢失、脏读、不可重复读,但不能防止幻读。
      
    • SERIALIZABLE 序列化级别:

      所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰。提供严格的事务隔离,此隔离级别可以防止更新丢失、脏读、不可重复读、幻读。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
      
  3. @Transactional(readOnly = true,isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED) 事务传播行为

    nu6JYR.png

    Propagation属性用来枚举事务的传播行为: Spring支持7种事务传播行为,默认为REQUIRED。

    • REQUIRED

      REQUIRED是常用的事务传播行为,如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。

    • SUPPORTS

      SUPPORTS表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么这个方法会在这个事务中运行。

    • MANDATORY

      MANDATORY表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常。不会主动开启一个事务。

    • REQUIRES_NEW

      REQUIRES_NEW表示当前方法必须运行在它自己的事务中。一个新的事务将被启动,如果存在当前事务,在该方法执行期间,当前事务会被挂起(如果一个事务已经存在,则先将这个存在的事务挂起)。如果使用JTATransactionManager的话,则需要访问TransactionManager。

    • NOT_SUPPORTED

      NOT_SUPPORTED表示该方法不应该运行在事务中,如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager。

    • NEVER

      NEVER表示当前方法不应该运行在事务上下文中,如果当前正有一个事务在运行,则会抛出异常。

    • NESTED

      NESTED表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与REQUIRED一样。嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。

      综上所述,NESTED和REQUIRES_NEW非常相似,都是开启一个属于它自己的新事务。使用REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行。两个事务互不影响,两个事务不是一个真正的嵌套事务,同时它还需要JTA事务管理器的支持。

      使用NESTED时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。嵌套事务开始执行时, 它将取得一个 savepoint,如果这个嵌套事务失败, 将回滚到此savepoint。潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。

2. 高并发导致的问题
  1. 高并发情况下最容易导致的就是 多个事务同时进行 问题,但是各个事务没有对当前处理数据加以保护,造成互相的数据修改,最终导致数据异常。

  2. 高并发下的事务问题:

    • 第一类丢失更新: A事务撤销时, 把已经提交的B事务的更新数据覆盖了。
    • 第二类丢失更新: A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失。
3. 后端限制重复提交

这个分为前后端两部分

  1. 就是在前端js代码种控制用户点击提交按钮后,弹出遮罩层或者将点击按钮直接禁用,当后端返回成功提示时页面重置。

  2. 就是后端在前一步操作时返回一个随机TOKEN(可以创建一个时间戳)信息给前端,将时间戳存入redis中,数据提交时在header携带TOKEN信息在后端方法中进行比对验证,如果相同,执行操作,如果不相同返回错误信息。

  3. 数据库做唯一约束。相同数据不允许重复。这个是最有效的防治重复提交的方法了。出现异常,回滚。

  4. Post/Redirect/Get 是一种 web 开发设计模式,用于防止表单的重复提交。 也就是 RPG设计模式

    默认情况,提交 Post 请求到服务器后,如果直接刷新浏览器,会重新在提交一次 Post 请求。在访问电商网站时,提交订单采用的是 Post 请求,如果直接刷新浏览器就容易导致重复订单的提交,这个不是用户希望发生的行为。PRG 方法就是用户防止这种现象的发生。

4. Service层接口多个实现

上面这句话的意思是在同一个service接口,有N个实现,那么在同一个Controller中如何同时注入。

​ 1. 这个其实可以改变注入方式:

​ 每次默认使用的注解是 @Autowired() 这个注解是专属于Spring的。我们可以在添加其他注解组合使用。 使用@Qualifier("baseDao") 来使用名称装配。

​ 2. 或者直接使用 @Resource(name = "AgentBackstageHomePageServiceImpl") 来指定,但是在其实现类 中, @Service("AgentBackstageHomePageServiceImpl") 指定名称。

5. 悲观锁和乐观锁

悲观锁

  1. 它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于 锁定状态 。实现依靠 数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)

  2. 悲观锁又可以衍生出 (1.共享锁 2.排它锁)

    • 共享锁在mysql中是通过什么命令来调用呢。只需要通过在执行语句后面加上 lock in share mode 就代表对某些资源加上共享锁了。

      nu6Gk9.png

    • 排它锁与共享锁相对应,就是指对于多个不同的事务,对同一个资源只能有一把锁。与共享锁类型,在需要执行的语句后面加上 for update 就可以了

乐观锁

1. 乐观锁是建立在 **每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用 *版本号* 等机制。** 乐观锁适用于多读的应用类型。
2. 实际使用每次都会在更新之前获取到当前数据的版本号,更新数据的时候 **版本号作为条件** 。
6. AOP实现及实际应用

AOP应用场景

场景一: 记录日志
场景二: 监控方法运行时间 (监控性能)
场景三: 权限控制
场景四: 缓存优化 (第一次调用查询数据库,将查询结果放入内存对象, 第二次调用, 直接从内存对象返回,不需要查询数据库 )
场景五: 事务管理 (调用方法前开启事务, 调用方法后提交关闭事务 )

AOP的实现原理
那Spring中AOP是怎么实现的呢?Spring中AOP的有两种实现方式:
1、JDK动态代理
2、Cglib动态代理

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值