问题【框架】
创始人
2025-05-31 05:24:45
0

框架

  • Spring
    • 1、Spring 是什么
    • 2、谈谈自己对于 Spring IOC 的了解
    • 3、将一个类声明为 Bean 的注解有哪些
    • 3、Bean 的作用域有哪些
    • 4、单例 Bean 的线程安全问题了解吗
    • 5、Spring 容器对象的懒加载(控制对象的创建的时机)
    • 6、Spring 容器实例化和管理对象的四种方式
    • 7、Spring 容器中的 bean 生命周期
    • 8、谈谈自己对于 Spring DI 的了解
    • 9、注入 Bean 的注解有哪些
    • 10、谈谈自己对于 AOP 的了解
    • 11、Spring AOP里面的几个名词的概念
    • 12、Spring 通知(Advice)有哪些类型
    • 13、多个切面的执行顺序如何控制
    • 14、Spring 如何解决循环依赖问题
    • 15、Spring 的自动装配
    • 16、Spring 事务的实现方式和实现原理
    • 17、Spring 管理事务的方式有几种
    • 18、Spring 事务中哪几种事务传播行为
    • 19、Spring 事务中的隔离级别有哪几种
    • 20、Spring 框架中用到了哪些设计模式
    • 21、如何使用 JPA 在数据库中非持久化一个字段
    • 22、JPA 的审计功能是做什么的,有什么用
    • 23、实体之间的关联关系注解有哪些
    • 24、有哪些控制请求访问权限的方法
    • 25、如何对密码进行加密
  • Spring MVC
    • 说说自己对于 Spring MVC 了解
    • Spring MVC 执行流程
    • Spring MVC 怎么样设定重定向和转发的
    • Spring MVC 常用的注解有哪些
    • Sping MVC 中的控制器的注解一般用哪个,有没有别的注解可以替代
    • Sping MVC 和 struts2 的区别有哪些
    • 如何解决POST请求中文乱码问题,GET的又如何处理呢
    • Spring MVC 里面拦截器是怎么写的
    • 如果在拦截请求中,想拦截get方式提交的方法怎么配置
    • 当一个方法向AJAX返回特殊对象,譬如Object,List等需要做什么处理
    • SpringMvc的控制器是不是单例模式?如果是,有什么问题?怎么解决
    • 如果想在拦截的方法里面得到从前台传入的参数,怎么得到
    • 前台传入多个参数,并且这些参数都是一个对象的属性,怎么进行参数绑定?
    • SpringMVC用什么对象从后台向前台传递数据的?
    • 怎样在控制器方法里面得到request或者session
    • 怎么样把ModelMap里面的数据放入Session里面
    • SpringMVC四种返回值类型总结
  • MyBatis
    • 1、什么是 MyBatis
    • 2、为什么说 MyBatis 是半ORM框架,与 Hibernate 有哪些不同
    • 3、#{ } 和 ${ } 的区别是什么
    • 4、MyBatis 是怎么解决实体类中的属性名和表中的字段名不一样的问题
    • 5、如何在 Mapper 中传递多个参数
    • 6、MyBatis 的接口绑定是什么,有哪些绑定方式
    • 7、在MyBatis 中使用 Mapper 接口开发时有哪些要求
    • 8、MyBatis 中 Mapper 接口中的方法支持重载么
    • 9、MyBatis 的动态SQL是什么,主要标签有哪些
    • 10、MyBatis 映射文件中,A 标签通过 include 引用了 B 标签的内容,B 标签能否定义在 A 标签的后面
    • 11、MyBatis 的 Mapper 接口工作原理是什么
    • 12、MyBatis 的工作原理是什么
    • 13、MyBatis 中一对一查询、一对多查询是怎么实现的
    • 14、MyBatis 的分页方法有哪些
    • 15、MyBatis 中都有哪些Executor执行器,它们之间的区别是什么
    • 16、SpringBoot + Mybatis 一级缓存和二级缓存详解
    • 17、MyBatis 是否支持延迟加载,如果支持,它的实现原理是什么
    • 18、MyBatis 是如何防止sql注入的

Spring

1、Spring 是什么

Spring是一个轻量级的容器框架,目的是用于简化企业应用程序的开发,Spring 的本质核心是创建和管理应用程序的对象,因为代码中是抽象耦合,那么把 new 具体类对象放在 Spring 框架中管理,通过 Spring 框架根据用户的需求,把对象注入到需要的地方,相当于在代码中没有使用任何具体子类的实现。

主要包括以下七个模块:

  • Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等)。
  • Spring Core:核心类库,所有功能都依赖于该类库,提供IOC和DI服务。
  • Spring AOP:AOP服务。
  • Spring Web:提供了基本的面向Web的综合特性,提供对常见框架如Struts2的支持,Spring能够管理这些框架,将Spring的资源注入给框架,也能在这些框架的前后插入拦截器。
  • Spring MVC:提供面向Web应用的Model-View-Controller,即MVC实现。
  • Spring DAO:对JDBC的抽象封装,简化了数据访问异常的处理,并能统一管理JDBC事务。
  • Spring ORM:对现有的ORM框架的支持。

2、谈谈自己对于 Spring IOC 的了解

什么是IOC

就是把 new 对象不放在具体类中去 new,把 new 对象的控制权反转给第三方 Spring 容器去 new 对象。指将对象的控制权转移给Spring框架,由 Spring 来负责控制对象的生命周期(比如创建、销毁)和对象间的依赖关系。

Spring 容器初始化

  • 本地文件系统的方式
    ApplicationContext context = new FileSystemXmlApplicationContext()
  • 类路径的方式
    AbstractApplicationContext context = new ClassPathXmlApplicationContext()

Spring 容器中获取对象

  • getBean(String)根据 id 获取容器中的对象,需要强制转换。
  • getBean(Class)根据类的类型获取容器中的对象,Class类型在容器中唯一,不需要强制转换。
  • getBean(String,Class)根据 id 和类同时获取具备获取容器中的对象,id在容器中必须唯一。

Spring IOC 原理

  1. 启动容器 Spring 正确加载和解析 Spring 的 xml文件。
  2. 把 Spring 的 xml 中的 bean 节点的内容解析出来储存给 map 集合。
  3. 循环遍历map集合中所有的数据 ,取出 class 属性的值,通过反射实例化对象。
    Object obj=Class.forName(“包名.类名”).newInstance()
  4. 把创建完的对象储存到另一个map集合中,用bean的 id 作为 key,对象做为value。
  5. 如果属性需要注入,spring框架就会帮程序员注入数据。

3、将一个类声明为 Bean 的注解有哪些

  • @Controller : 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。
  • @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。
  • @Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。
  • @Component :通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层使用。

3、Bean 的作用域有哪些

Spring 中 Bean 的作用域通常有下面几种:

  • singleton : 只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
  • prototype : 每次获取都会创建一个新的 bean 实例,连续 getBean() 两次,得到的是不同的 Bean 实例。
  • request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
  • session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。
  • application/global-session (仅 Web 应用可用): 每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
  • websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。

如何配置 bean 的作用域呢

xml 方式:


注解方式:

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {return new Person();
}

单例和多例优缺点

  • 单例: 所有请求用同一个对象来处理,通过单例模式,可以保证系统中一个类只有一个实例。
  • 多例:每一个请求用一个新的对象来处理。
  1. 单例优点降低了实例创建和销毁所占用的资源,缺点线程共享一个实体,会发生线程安全问题。
  2. 多例线程之间数据隔离,所以不会有线程安全问题,但是频繁的实例创建和销毁会带来资源的大量浪费。

4、单例 Bean 的线程安全问题了解吗

大部分时候我们并没有在项目中使用多线程,所以很少有人会关注这个问题。单例 Bean 存在线程问题,主要是因为当多个线程操作同一个对象的时候是存在资源竞争的。不过,大部分 Bean 实际都是无状态(没有实例变量)的(比如 Controller、Dao、Service等),这种情况下, Bean 是线程安全的。

  • 有状态Bean(Stateful Bean) :就是有实例变量的对象,可以保存数据,是非线程安全的。
  • 无状态Bean(Stateless Bean):就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的。
    常见的有两种解决办法:
  1. 在 Bean 中尽量避免定义可变的成员变量。
  2. 在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。

5、Spring 容器对象的懒加载(控制对象的创建的时机)

如果Spring容器创建,对象立即创建,则称为立即加载。如果 Spring 容器创建,对象在被使用的时候创建,则称为懒加载。

注解:@Lazy 表示为懒加载

懒加载XML写法




lazy-init 是否懒加载scope 单例多例对象的创建结果
truesingleton懒加载
trueprototype懒加载
default/flasesingleton立即加载
default/flaseprototype懒加载

只要对象是多例模式 则都是懒加载! 在单例模式中控制懒加载才有效

6、Spring 容器实例化和管理对象的四种方式

Hello 类

public class Hello {public Hello() {System.out.println("Hello()");}
}
  1. 通过无参构造函数实例化对象

直接通过 Hello 类无参构造创建。


特点:把创建的对象放在 Spring 容器中,Spring 帮你创建和管理对象。

  1. 静态工厂

所谓的静态工厂,通过类的名字调用类中的静态方法,由静态方法生产一个对象,把生产出来的对象交给 Spring 容器来管理,class 是一个类,可以是抽象类,可以是普通类,factory-method 一定是类中静态方法。

public class StaticFactory_Hello {/*** 这是一个静态工厂*/public static Hello getObject(){	return new Hello();}
}

特点:对象是被动渠道创建的,但是交给了 Spring 来管理。

  1. 实例工厂

所谓的实例工厂,生产对象的方法一定是非静态的,首先要创建一个实例工厂的对象,class类不能是抽象类,通过对象来调用非静态的方法。

public class Instance_Factory {/*** 这是一个实例工厂*/public Hello getObject() {return new Hello();}
}

特点:对象是被动渠道创建的,但是交给了 Spring 来管理。



  1. Spring 工厂

所谓的 Spring 工厂要求必须实现FactoryBean接口,自动调用重写getObject方法,此方法返回一个对象并且一定要返回一个对象,然后把对象放置到 Spring 容器中。


特点:对象是被动渠道创建的,但是交给了 Spring 来管理。

7、Spring 容器中的 bean 生命周期

  1. 调用 Bean 构造方法实例化 Bean。
  2. 利用依赖注入完成 Bean 中所有属性值的注入。
  3. 如果 Bean 实现了 BeanNameAware 接口,则调用 setBeanName() 方法传入当前 Bean 的 id。
  4. 如果 Bean 实现了 BeanFactoryAware 接口,则调用 setBeanFactory() 方法传入当前工厂实例的引用。
  5. 如果 Bean 实现了 ApplicationContextAware 接口,则 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。
  6. 如果 BeanPostProcessor 和 Bean 关联,则调用 postProcessBeforeInitialzation() 方法对 Bean 进行加工操作,Spring 的 AOP 就是利用它实现的。
  7. 如果 Bean 实现了 InitializingBean 接口,则调用 afterPropertiesSet() 方法。
  8. 如果 在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。
  9. 如果 BeanPostProcessor 和 Bean 关联,则调用 postProcessAfterInitialization() 方法。此时,Bean 已经可以被应用系统使用了。
  10. 如果在XML中指定了该 Bean 的作用范围为 scope=“singleton”,则将该 Bean 放入缓存池中,将触发 Spring 对该 Bean 的生命周期管理,如果指定了该 Bean 的作用范围为 scope=“prototype”,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。
  11. 如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的 Bean 销毁。
  12. 或者在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁(同11一样效果)。

在这里插入图片描述

8、谈谈自己对于 Spring DI 的了解

从spring容器中取出容器中的对象,然后把对象注入到需要的地方。

一共有两种注入方式,第一中是构造函数的方式,另一种是setter方式

构造函数方式

public class Constructor {private Hello hello;private String name;public Constructor(Hello hello, String name) {super();this.hello = hello;this.name = name;}@Overridepublic String toString() {return "Constructor [hello=" + hello + ", name=" + name + "]";}	
}





setter方式

bean 节点用来告知 Spring 实例化或管理对象,property 节点用来告知 Spring 有对象注入关系,相当于告知spring要做对象的关系管理。每一个property 节点都必须对应一个 setter 方法 和 name 属性,把 name 属性的值的第一个字母大写,前面加上set构建出字符串,拿这个字符串去 类中寻找是否有此名称的方法 ,如果有setter方法,就反射调用这个setter方法。

① 对象注入

ref 是要引入对象,ref 引用的对象,一定是引自 Spring 容器且是容器中的唯一的id。





② 单值注入



③ 集合注入(直接结合注入和间接集合注入)

直接集合注入


北京上海广州北京上海广州北京上海广州

间接集合注入,集合的对象要交给spring容器来管理

       
北京
上海
广州

北京
上海
广州






北京
上海
广州






④ 表达式注入

必须有属性文件,表达式注入就是把属性文件通过 Spring 注入给某个对象中。

mysql.propertiesjdbc_driverClass=com.mysql.jdbc.Driverjdbc_url=jdbc:mysql://localhost:3306/tesdbjdbc_userName=rootjdbc_userPassword=root
mysql.propertses	

属性注入的方式有两种

(1)用 ${} 方式 location属性可以放置多个属性文,用,间隔,建议用类路径classpath取,仅针对spring框架。




(2)用 #{} 方式,逗号间隔,必须给一个id 把属性文件的数据存储给spring容器,容器以manyProperty,值是若干属性键值对。



⑤ 空值注入







9、注入 Bean 的注解有哪些

Spring 内置的 @Autowired 以及 JDK 内置的 @Resource 和 @Inject 都可以用于注入 Bean。

AnnotaionPackageSource
@Autowiredorg.springframework.bean.factorySpring 2.5+
@Resourcejavax.annotationJava JSR-250
@Injectjavax.injectJava JSR-250

@Autowired 和@Resource使用的比较多一些。

10、谈谈自己对于 AOP 的了解

AOP,一般称为面向切面,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为切面(Aspect),减少系统中的重复代码,降低了模块间的耦合度,提高系统的可维护性。可用于权限认证、日志、事务处理。

AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。

(1)AspectJ是静态代理,也称为编译时增强,AOP框架会在编译阶段生成AOP代理类,并将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。

(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:

① JDK动态代理只提供接口的代理,不支持类的代理,要求被代理类实现接口。JDK动态代理的核心是InvocationHandler接口和Proxy类,在获取代理对象时,使用Proxy类来动态创建目标类的代理类(即最终真正的代理类,这个类继承自Proxy并实现了我们定义的接口),当代理对象调用真实对象的方法时, InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起。

② 如果被代理类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

(3)静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。

jdk动态代理 和 cglib动态代理的区别

  1. jdk动态代理目标业务类必须有接口,cglib动态代理业务类有无接口皆可。
  2. jdk动态代理必须实现InvocationHandler接口,cglib动态代理必须实现MethodInterceptor接口。
  3. jdk动态代理代理类和目标业务类是兄弟关系,因为隶属于同一个接口,cglib动态代理代理类和目标业务类是父子关系,业务类是父类,业务类不能是final类,代理类是子类。
  4. jdk动态代理创建代理类快,执行代理类慢,cglib动态代理创建代理类慢,执行代理类快。

11、Spring AOP里面的几个名词的概念

  • 连接点(Join point):指程序运行过程中所执行的方法,一个连接点总代表一个方法的执行。
  • 切面(Aspect):被抽取出来的公共模块,可以用来会横切多个对象。Aspect切面可以看成 Pointcut切点 和 Advice通知 的结合,一个切面可以由多个切点和通知组成。
  • 切点(Pointcut):切点用于定义 要对哪些Join point进行拦截。
  • 通知(Advice):指要在连接点(Join Point)上执行的动作,即增强的逻辑,比如权限校验和、日志记录等。通知有各种类型,包括Around、Before、After、After returning、After throwing。
  • 目标对象(Target):包含连接点的对象,也称作被通知(Advice)的对象。 由于Spring AOP是通过动态代理实现的,所以这个对象永远是一个代理对象。
  • 织入(Weaving):通过动态代理,在目标对象(Target)的方法(即连接点Join point)中执行增强逻辑(Advice)的过程。
  • 引入(Introduction):添加额外的方法或者字段到被通知的类。Spring允许引入新的接口(以及对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。

12、Spring 通知(Advice)有哪些类型

  1. 前置通知(Before Advice):在连接点(Join point)之前执行的通知。
  2. 后置通知(After Advice):当连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
  3. 环绕通知(Around Advice):包围一个连接点的通知,这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也可以选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
  4. 后置通知(AfterReturning Advice):在连接点正常完成后执行的通知(如果连接点抛出异常,则不执行)。
  5. 后置异常通知(AfterThrowing advice):在方法抛出异常退出时执行的通知。
正常情况:环绕前置 ==> 前置通知@Before ==> 目标方法执行 ==> 后置返回通知@AfterReturning ==> 后置通知@After ==> 环绕返回 ==> 环绕最终
异常情况:环绕通知 ==> 前置通知@Before ==> 目标方法执行 ==> 后置异常通知@AfterThrowing ==> 后置通知@After ==> 环绕异常 ==> 环绕最终

13、多个切面的执行顺序如何控制

  1. 通常使用@Order 注解直接定义切面顺序
// 值越小优先级越高
@Order(3)
@Component
@Aspect
public class LoggingAspect implements Ordered {
  1. 实现Ordered 接口重写 getOrder 方法。
@Component
@Aspect
public class LoggingAspect implements Ordered {// ....@Overridepublic int getOrder() {// 返回值越小优先级越高return 1;}
}

14、Spring 如何解决循环依赖问题

15、Spring 的自动装配

@Autowired、@Resource

16、Spring 事务的实现方式和实现原理

Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。Spring只提供统一事务管理接口,具体实现都是由各数据库自己实现,数据库事务的提交和回滚是通过 redo log 和 undo log实现的。Spring会在事务开始时,根据当前环境中设置的隔离级别,调整数据库隔离级别,由此保持一致。

17、Spring 管理事务的方式有几种

  • 编程式事务 : 在代码中硬编码 : 通过 TransactionTemplate或者TransactionManager
    手动管理事务,实际应用中很少使用,但是对于你理解 Spring 事务管理原理有帮助。
  • 声明式事务 : 在 XML 配置文件中配置或者直接基于注解 : 实际是通过 AOP 实现(基于@Transactional 的全注解方式使用最多)

18、Spring 事务中哪几种事务传播行为

事务传播行为是为了解决业务层方法之间互相调用的事务问题。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。

例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。正确的事务传播行为可能的值如下:

① PROPAGATION_REQUIRED:(默认传播行为)如果当前没有事务,就创建一个新事务;如果当前存在事务,就加入该事务。
② PROPAGATION_REQUIRES_NEW:无论当前存不存在事务,都创建新事务进行执行。
③ PROPAGATION_SUPPORTS:如果当前存在事务,就加入该事务;如果当前不存在事务,就以非事务执行。‘
④ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
⑤ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则按REQUIRED属性执行。
⑥ PROPAGATION_MANDATORY:如果当前存在事务,就加入该事务;如果当前不存在事务,就抛出异常。
⑦ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

19、Spring 事务中的隔离级别有哪几种

① ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。
② ISOLATION_READ_UNCOMMITTED:读未提交,允许事务在执行过程中,读取其他事务未提交的数据。
③ ISOLATION_READ_COMMITTED:读已提交,允许事务在执行过程中,读取其他事务已经提交的数据。
④ ISOLATION_REPEATABLE_READ:可重复读,在同一个事务内,任意时刻的查询结果都是一致的。
⑤ ISOLATION_SERIALIZABLE:所有事务逐个依次执行。

20、Spring 框架中用到了哪些设计模式

  • 工厂设计模式 : Spring 使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
  • 代理设计模式 : Spring AOP 功能的实现。单例设计模式 : Spring 中的 Bean 默认都是单例的。
  • 模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
  • 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
  • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
  • 适配器模式 : Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。

21、如何使用 JPA 在数据库中非持久化一个字段

假如我们有下面一个类:

Entity(name="USER")
public class User {@Id@GeneratedValue(strategy = GenerationType.AUTO)@Column(name = "ID")private Long id;@Column(name="USER_NAME")private String userName;@Column(name="PASSWORD")private String password;private String secrect;}

如果我们想让secrect 这个字段不被持久化,也就是不被数据库存储怎么办?我们可以采用下面几种方法:

static String transient1; // not persistent because of static
final String transient2 = "Satish"; // not persistent because of final
transient String transient3; // not persistent because of transient
@Transient
String transient4; // not persistent because of @Transient

一般使用后面两种方式比较多,我个人使用注解的方式比较多。

22、JPA 的审计功能是做什么的,有什么用

审计功能主要是帮助我们记录数据库操作的具体行为比如某条记录是谁创建的、什么时间创建的、最后修改人是谁、最后修改时间是什么时候。

@Data
@AllArgsConstructor
@NoArgsConstructor
@MappedSuperclass
@EntityListeners(value = AuditingEntityListener.class)
public abstract class AbstractAuditBase {@CreatedDate@Column(updatable = false)@JsonIgnoreprivate Instant createdAt;@LastModifiedDate@JsonIgnoreprivate Instant updatedAt;@CreatedBy@Column(updatable = false)@JsonIgnoreprivate String createdBy;@LastModifiedBy@JsonIgnoreprivate String updatedBy;
}
  • @CreatedDate: 表示该字段为创建时间字段,在这个实体被 insert 的时候,会设置值
  • @CreatedBy :表示该字段为创建人,在这个实体被 insert 的时候,会设置值@LastModifiedDate、@LastModifiedBy同理

23、实体之间的关联关系注解有哪些

@OneToOne : 一对一。
@ManyToMany :多对多。
@OneToMany : 一对多。
@ManyToOne :多对一。

利用 @ManyToOne 和 @OneToMany 也可以表达多对多的关联关系

24、有哪些控制请求访问权限的方法

  • permitAll() :无条件允许任何形式访问,不管你登录还是没有登录。
  • anonymous() :允许匿名访问,也就是没有登录才可以访问。
  • denyAll() :无条件决绝任何形式的访问。
  • authenticated():只允许已认证的用户访问。
  • fullyAuthenticated() :只允许已经登录或者通过 remember-me 登录的用户访问。
  • hasRole(String) : 只允许指定的角色访问。
  • hasAnyRole(String) : 指定一个或者多个角色,满足其一的用户即可访问。
  • hasAuthority(String) :只允许具有指定权限的用户访问。
  • hasAnyAuthority(String) :指定一个或者多个权限,满足其一的用户即可访问。
  • hasIpAddress(String) : 只允许指定 ip 的用户访问。

25、如何对密码进行加密

如果我们需要保存密码这类敏感数据到数据库的话,需要先加密再保存。Spring Security 提供了多种加密算法的实现,开箱即用,非常方便。这些加密算法实现类的父类是 PasswordEncoder ,如果你想要自己实现一个加密算法的话,也需要继承 PasswordEncoder。

PasswordEncoder 接口一共也就 3 个必须实现的方法。

public interface PasswordEncoder {// 加密也就是对原始密码进行编码String encode(CharSequence var1);// 比对原始密码和数据库中保存的密码boolean matches(CharSequence var1, String var2);// 判断加密密码是否需要再次进行加密,默认返回 falsedefault boolean upgradeEncoding(String encodedPassword) {return false;}
}

官方推荐使用基于 bcrypt 强哈希函数的加密算法实现类。

Spring MVC

说说自己对于 Spring MVC 了解

MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码。

MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。Spring MVC 可以帮助我们进行更简洁的 Web 层的开发,并且它天生与 Spring 框架集成。Spring MVC 下我们一般把后端项目分为Controller 层(控制层,返回数据给前台页面)、 Service 层(处理业务)、Dao 层(数据库操作)、Entity 层(实体类)。

Spring MVC 执行流程

这里插入图片描述

  1. 用户发起 HTTP Request 请求,该请求会被提交到 DispatcherServlet(前端控制器)。
  2. 由 DispatcherServlet 把请求的 URL 路径给到 HandlerMapping(处理器映射器)。
    HandlerMapping(处理器映射器)据请求的 URL 路径,通过注解或者 XML 配置,寻找匹配的处理器(Handler)信息,构造执行链并返回。
    DispatcherServlet 将执行链返回的 Handler 信息发送给 HandlerAdapter(处理器适配器)。
    HandlerAdapter (处理器适配器)根据 Handler 信息找到并执行相应的 Handler(常称为 Controller)。
    Handler 执行完毕后会返回给 HandlerAdapter (处理器适配器)一个 ModelAndView 对象(Spring MVC 的底层对象,包括 Model 数据模型和 View 视图信息)。
    HandlerAdapter(处理器适配器) 接收到 ModelAndView 对象后,将其返回给 DispatcherServlet(前端控制器)。
    DispatcherServlet(前端控制器) 接收到 ModelAndView 对象后,会请求 ViewResolver(视图解析器)对视图进行解析。
    ViewResolver (视图解析器)根据 View 信息匹配到相应的视图结果,并返回给 DispatcherServlet(前端控制器)。
    DispatcherServlet (前端控制器)接收到具体的 View 视图后,进行视图渲染,将 Model 中的模型数据填充到 View 视图中的 request 域,生成最终的 View(视图)。
    视图负责将结果显示到浏览器(客户端)。

流程说明(重要):

  1. 客户端(浏览器)发送请求, DispatcherServlet(核心的中央处理器,负责接收请求、分发,并给予客户端响应。) 拦截请求。
  2. DispatcherServlet 根据请求信息调用 HandlerMapping 。HandlerMapping(处理器映射器) 根据 uri 去匹配查找能处理的 Handler(也就是我们平常说的 Controller 控制器) ,并会将请求涉及到的拦截器和 Handler 一起封装。
  3. DispatcherServlet 调用 HandlerAdapter适配执行 Handler 。
  4. Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给DispatcherServlet,ModelAndView 顾名思义,包含了数据模型以及相应的视图的信息。Model 是返回的数据对象,View 是个逻辑上的 View。
  5. ViewResolver 会根据逻辑 View 查找实际的 View。
  6. DispaterServlet 把返回的 Model 传给 View(视图渲染)。
  7. 把 View 返回给请求者(浏览器)

HandlerMapping :处理器映射器,根据 uri 去匹配查找能处理的 Handler ,并会将请求涉及到的拦截器和 Handler 一起封装。HandlerAdapter :处理器适配器,根据 HandlerMapping 找到的 Handler ,适配执行对应的 Handler;Handler :请求处理器,处理实际请求的处理器。ViewResolver :视图解析器,根据 Handler 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 DispatcherServlet 响应客户端

Spring MVC 怎么样设定重定向和转发的

请求转发与重定向的区别

  1. 请求转发在服务器端完成的;重定向是在客户端完成的。
  2. 请求转发的速度快;重定向速度慢。
  3. 请求转发的是同一次请求;重定向是两次不同请求。
  4. 请求转发不会执行转发后的代码;重定向会执行重定向之后的代码。
  5. 请求转发地址栏没有变化;重定向地址栏有变化。
  6. 请求转发必须是在同一台服务器下完成;重定向可以在不同的服务器下完成。

Spring MVC 设定请求转发

@RequestParam("/login")
public String redirect(User user){if{//登录成功...}else{//登录失败,转发到登录页面,在返回值前面加"forward:"。return "forward:tologin";}
}

Spring MVC 设定重定向

@RequestParam("/login")
public String redirect(User user){if{//登录成功...}else{//登录失败,重定向到登录页面,在返回值前面加"redirect:"。例如我们在登录的时候,登录失败会重定向到登录页面。return "redirect:tologin";}
}

Spring MVC 常用的注解有哪些

@RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。

@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。

@ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。

Sping MVC 中的控制器的注解一般用哪个,有没有别的注解可以替代

一般用@Controller注解,也可以使用@RestController,@RestController注解相当于@ResponseBody + @Controller,表示是表现层,除此之外,一般不用别的注解代替。

Sping MVC 和 struts2 的区别有哪些

(1)springmvc的入口是一个servlet即前端控制器(DispatchServlet),而struts2入口是一个filter过虑器(StrutsPrepareAndExecuteFilter)。

(2)springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。

(3)Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。

如何解决POST请求中文乱码问题,GET的又如何处理呢

(1)解决post请求乱码问题:在web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf-8

CharacterEncodingFilterorg.springframework.web.filter.CharacterEncodingFilterencodingutf-8
CharacterEncodingFilter/*

(2)get请求中文参数出现乱码解决方法有两个:

①修改tomcat配置文件添加编码与工程编码一致,如下:


②另外一种方法对参数进行重新编码:

String userName = new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")

ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码。

Spring MVC 里面拦截器是怎么写的

有两种写法,一种是实现HandlerInterceptor接口,另外一种是继承适配器类,接着在接口方法当中,实现处理逻辑;然后在SpringMvc的配置文件中配置拦截器即可

https://blog.csdn.net/yy139926/article/details/127916974

如果在拦截请求中,想拦截get方式提交的方法怎么配置

可以在@RequestMapping注解里面加上method=RequestMethod.GET。

@RequestMapping(value="/toLogin",method = RequestMethod.GET)
public ModelAndView toLogin(){}

可以使用@GetMapping注解。

@GetMapping(value="/toLogin")
public ModelAndView toLogin(){}

当一个方法向AJAX返回特殊对象,譬如Object,List等需要做什么处理

  1. 在方法上加@ResponseBody注解,表示该方法的返回值不管是什么类型,都会返回JSON格式的数据。
  2. 把原来Controller类上的@Controller注解替换为@RestController注解。@RestController = @Controller + @ResponseBody,表明该Controller类所有的方法都返回JSON格式的数据(没有加@RequestMapping注解的方法除外)。
  3. 加入@ResponseBody注解就能返回JSON格式数据的原因是:SpringMVC提供的HttpMessageConverter自动转为JSON ,如果使用了Jackson或者Gson,不需要额外配置就可以自动返回JSON了,因为框架帮我们提供了对应的HttpMessageConverter ,如果使用了Alibaba的Fastjson的话,则需要自己手动提供一个相应的 HttpMessageConverter的实例。

SpringMvc的控制器是不是单例模式?如果是,有什么问题?怎么解决

是单例模式,在多线程访问的时候有线程安全问题,解决方案是在控制器里面不能写可变状态量,如果需要使用这些可变状态,可以使用ThreadLocal机制解决,为每个线程单独生成一份变量副本,独立操作,互不影响。

如果想在拦截的方法里面得到从前台传入的参数,怎么得到

直接在控制器方法的形参里面声明这个参数就可以,但名字必须和传过来的参数名称一样,否则参数映射失败。
下面方法形参中的userId,就会接收从前端传来参数名称为userId的值。

@RequestMapping("/deleteUser")
public void deleteUser(Long userId){//删除用户操作...
}

前台传入多个参数,并且这些参数都是一个对象的属性,怎么进行参数绑定?

直接在控制器方法的形参里面声明这个参数就可以,SpringMvc就会自动会请求参数赋值到这个对象的属性中。
下面方法形参中的user用来接收从前端传来的多个参数,参数名称需要和User实体类属性名称一致。

@RequestMapping("/saveUser")
public void saveUser(User user){//保存用户操作...
}
1234
@Data
public class User {private Long userId;private String username;private String password;//...
}

SpringMVC用什么对象从后台向前台传递数据的?

  1. 使用Map、Model和ModelMap的方式,这种方式存储的数据是在request域中
@RequestMapping("/getUser")
public String getUser(Map map,Model model,ModelMap modelMap){//1.放在map里  map.put("name", "xq");//2.放在model里,一般是使用这个model.addAttribute("habbit", "Play");//3.放在modelMap中 modelMap.addAttribute("city", "gd");modelMap.put("gender", "male");return "userDetail";
}
  1. 使用request的方式
@RequestMapping("/getUser")
public String getUser(Map map,Model model,ModelMap modelMap,HttpServletRequest request){//放在request里  request.setAttribute("user", userService.getUser());return "userDetail";
}
  1. 使用ModelAndView
@RequestMapping("/getUser")  
public ModelAndView getUser(ModelAndView modelAndView) {mav.addObject("user", userService.getUser());  mav.setViewName("userDetail");  return modelAndView;  
}  

怎样在控制器方法里面得到request或者session

直接在控制器方法的形参中声明request,session,SpringMvc就会自动把它们注入。

@RequestMapping("/login")
public ModelAndView login(HttpServletRequest request, HttpSession session){}

怎么样把ModelMap里面的数据放入Session里面

在类上添加@SessionAttributes注解将指定的Model数据存储到session中。

@SessionAttributes

  1. 默认情况下Spring MVC将模型中的数据存储到request域中。当一个请求结束后,数据就失效了。如果要跨页面使用。那么需要使用到session。而@SessionAttributes注解就可以使得模型中的数据存储一份到session域中。
  2. @SessionAttributes只能定义在Class,interface enum上,作用是将指定的Model中的键值对添加至session中,方便在一个会话中使用。

@SessionAttributes参数

  1. names:这是一个字符串数组。里面应写需要存储到session中数据的名称。
  2. types:根据指定参数的类型,将模型中对应类型的参数存储到session中。
  3. value:其实和上面的names是一样的。
@SessionAttributes(value={"names"},types={Integer.class})
@Controller
public class session{@RequestMapping("/session")public String session(Model model){model.addAttributes("names", Arrays.asList("caoyc","zhh","cjx"));model.addAttributes("age", 22);return "/session";}
}

在上面代码中,在类上添加@SessionAttributes注解,并指定将names名称的Model数据存储到session域中,以及将Integer类型的Model数据存储到session域中。

SpringMVC四种返回值类型总结

虽然现在SpringBoot框架很火,但是SpringBoot并不能处理以及响应客户端的请求,最终还是要依赖SpringMVC框架,所以接下来介绍SpringMVC Controller方法的返回值类型,涵盖所有返回值类型。这篇博客只是扫盲点,没具体深入。

  1. ModelAndView

我们在使用SpringMVC的时候,经常返回ModelAndView类型,现在前后端分离后,后端都是返回JSON格式数据为主。返回 ModelAndView类型,我们可以在ModelAndView对象中指定视图名称,然后也可以绑定数据,如下面代码:

@RequestMapping("/userList")
public ModelAndView getAllUser(ModelAndView mv) {List users= userService.getAllUser();//添加数据模型到request域中mv.addObject("users", users);mv.setViewName("userList");//指定视图名return mv;
}
  1. void

如果返回值为void的话,并不是真正没有返回值,而是会出现以下几种情况:

如果方法内真的没有返回值,那么SpringMVC默认把deleteUser(映射的URL)当成视图名称来解析,如果存在该视图,就返回给客户端;如果不存在该视图,就会报视图找不到异常。

@RequestMapping("/deleteUser")
public void deleteUser() {//删除操作
}

通过加@ResponseBody来修改默认行为,加上该注解表示返回JSON数据,这里返回空JSON数据,而不是把URL当成视图名称来解析

@RequestMapping("/deleteUser")
@ResponseBody
public void deleteUser() {//删除操作
}

请求转发

@GetMapping("/")
public void root(HttpServletRequest req,HttpServletResponse resp) {req.getRequestDispatcher("/WEB-INF/jsp/index.jsp").forward(req,resp);
}

重定向

@RequestMapping("/")
@ResponseBody
public void root(HttpServletResponse resp){resp.setStatus(302);resp.addHeader("Location","/WEB-INF/jsp/index.jsp");
}
@RequestMapping("/")
@ResponseBody
public void root(HttpServletResponse resp){resp.sendRedirect("/WEB-INF/jsp/index.jsp");
}
  1. String

当方法的返回值为String的时候,也会出现下面几种情况:

逻辑视图名:返回String最常见的是逻辑视图名,这种时候一般利用默认的参数Model来传递数据

@RequestMapping("/deleteUser")
//方法返回JSON数据
@ResponseBody	
public String deleteUser(Model model) {model.addAttribute("msg","删除成功");return "userList";
} 

重定向:登录失败的时候重定向到登录页面。

@RequestParam("/login")
public String redirect(User user){if{//登录成功...}else{//登录失败,重定向到登录页面return "redirect:tologin";}
}

请求转发:登录失败的时候请求转发到登录页面。

@RequestParam("/login")
public String redirect(User user){if{//登录成功...}else{//登录失败,转发到登录页面return "forward:tologin";}
}

真的返回String,相当于JSON格式的数据

@RequestMapping("/deleteUser")
@ResponseBody
public String deleteUser() {try{//删除成功return "删除成功";}catch(Exception e){return "删除失败";}
}
  1. JSON

现在前后端分离的情况下,大部分后端只需要返回JSON数据即可,List 集合、Map集合,实体类等都可以返回,这些数据由 HttpMessageConverter自动转为JSON ,如果使用了Jackson或者Gson,不需要额外配置就可以自动返回JSON了,因为框架帮我们提供了对应的HttpMessageConverter ,如果使用了Alibaba的Fastjson的话,则需要自己手动提供一个相应的 HttpMessageConverter的实例,方法的返回值如下面代码:

@GetMapping("/getUser")
@ResponseBody
public User getUser() {User user = userService.getUser();return user;
}
@RequestMapping("/userList")
@ResponseBody
public List getAllUser() {List users = userService.getAllUser();return users;
}

MyBatis

1、什么是 MyBatis

MyBatis是一个半ORM框架(模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术,将Java中的对象和数据库中的表关联对应起来,理解为:Java对象与数据库表的映射管理框架),它内部封装了JDBC,开发的时候只需要关注SQL语句本身就可以了,我们不需要花太多精力去处理原生的JDBC那一套流程,比如 加载驱动、创建connection连接、创建statement等。

2、为什么说 MyBatis 是半ORM框架,与 Hibernate 有哪些不同

Hibernate是全自动ORM框架,而Mybatis是半自动的。hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql。而mybatis仅有基本的字段映射,对象数据以及对象实际关系仍然需要通过手写sql来实现和管理

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

  1. #{ } 是预编译处理当中的一个占位符。MyBatis在处理 #{ } 的时候,会将SQL中的 #{ } 替换成 ? ,然后,再调用PreparedStatement对象的set方法进行赋值,由此来防止SQL注入的问题。底层如下:
// 实例化
PreparedStatement pstmt = con.prepareStatement(sql);// 装载占位符
pstmt.setString(1, "6");
pstmt.setString(2, "bb");// 执行sql语句
ResultSet rs = pstmt.executeQuery();
  1. ${ } 是单纯的字符串文本替换。MyBatis在处理 ${ } 的时候,只是简单的把 ${ } 替换为变量的值而已,由此会造成SQL注入,带来不必要的风险。
  2. 大部分情况下,我们都是使用 #{ } 来处理业务。但是,针对一些特殊的情况,比如 通过一个“变化的字段”做排序等,也可以使用 ${ } 。
${param} 传入的参数会被当成SQL语句中的一部分,举例:order by ${param},则解析成的sql为:order by id

4、MyBatis 是怎么解决实体类中的属性名和表中的字段名不一样的问题

(1)第一种是使用 标签,逐一定义列名和实体类对象属性名之间的映射关系。



(2)第二种是使用在SQL中定义列的别名,将列的别名与实体类对象的属性名一一对应起来


5、如何在 Mapper 中传递多个参数

(1)第一种是使用 @param 注解的方式。比如:

user selectUser(@param("username") string username, @param("password") string password);

(2)第二种是使用Java对象的方式。此时,在Java对象中可以有多个属性,每一个属性其实都是一个参数,这样也可以实现在Mapper中传递多个参数。

(3)第三种是使用map集合的方式。此时,需要使Mapper接口方法的输入参数类型和mapper.xml中定义的每个SQL的parameterType的类型都相同。

6、MyBatis 的接口绑定是什么,有哪些绑定方式

接口绑定,就是在MyBatis中定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口中的方法就可以操作数据库了。这样比起直接使用SqlSession对象提供的原生态的方法,更加灵活与简单。

(1)第一种是通过注解绑定,也就是说 在接口的方法上面加上 @Select、@Update 等注解,注解里面包含SQL语句来进行绑定。这种方式可以省去SQL的 xml 映射文件,对于简单的SQL来说比较适用,后期维护比较困难,平时在业务中基本不怎么使用。

(2)第二种是通过在SQL的xml映射文件里面写SQL来进行绑定, 在这种情况下,要指定 xml 映射文件里面的 namespace 参数必须为接口的全类名。不管是SQL简单还是复杂,xml 文件的方式 都比较简单高效,也是最常用的。

7、在MyBatis 中使用 Mapper 接口开发时有哪些要求

一般情况下,在日常开发的时候,会遵循一个mapper.xml映射文件对应于一张表的增删改查。

  1. mapper.xml映射文件中的namespace属性,必须要定义为对应的Mapper接口的全类名,以此来标识一个mapper级别的二级缓存。
  2. Mapper接口中的方法名要和mapper.xml中定义的每个SQL语句的id属性相同。
  3. Mapper接口中的方法的输入参数类型要和mapper.xml中定义的每个SQL语句的parameterType的类型相同。
  4. Mapper接口中的方法的输出参数类型要和mapper.xml中定义的每个SQL语句的resultType的类型相同,或者使用resultMap也行。

8、MyBatis 中 Mapper 接口中的方法支持重载么

通常一个 xml 映射文件,都会写一个 Dao 接口与之对应。Dao 接口就是人们常说的 Mapper 接口,接口的全限名,就是映射文件中的 namespace 的值,接口的方法名,就是映射文件中 MappedStatement 的 id 值,接口方法内的参数,就是传递给 sql 的参数。

Mapper 接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个 MappedStatement

举例: com.mybatis3.mappers. StudentDao.findStudentById,可以唯一找到 namespace 为 com.mybatis3.mappers. StudentDao 下面 id = findStudentById 的 MappedStatement,在 MyBatis 中,每一个 select * from studentid = #{id}

能正常运行,并能得到相应的结果,这样就实现了在 Dao 接口中写重载方法。Mybatis 的 Dao 接口可以有多个重载方法,但是多个接口对应的映射必须只有一个,否则启动会报错。

9、MyBatis 的动态SQL是什么,主要标签有哪些

MyBatis的动态SQL标签,主要有以下几类:(1) if 标签,配合 test 标签用来做简单的条件判断。(2)choose 标签,配合 when、otherwise 标签,相当于Java语言中的switch…case语句实现分支选择功能。(3)where 标签,主要是用来简化SQL语句中where条件判断的,能智能的处理 and、or,不用担心有多余的 and 或者 or 导致语法错误。(4)set 标签,主要用来做数据update的时候。(5)trim 标签,对包含的内容加上前缀 prefix、或者后缀 suffix。 (6)foreach 标签,主要用在 Mybatis in 语句中。

(1)if 标签


name->Name->getName 用getName去parameterType指定的类中寻找是否有此方法,如果有就反射调用,调用完反射结果不为null就拼装sql语句 null就不拼装,address同理。

如果两个条件都不为null
select * from user where age=20 username like ? and address like ?

(2)choose when otherwise 标签


choose when otherwise 标签 多个when条件同时成立,就取第一个条件成立的when。

(3)where 标签

	

where标签是为了给sql语句添加where关键字,where标签中的条件都不成立,where关键字就不添加了,如果两个条件都成立
select * from t_user where username like ? and address like ?
如果第一个不成立,第二个条件成立
select * from t_user where and address like ?
他会自动去掉and关键字

(4)set 标签

 update userusername=#{uname},address=#{uaddress}where id=#{uid}
	

set 标签只能用于更新语句,第一个条件成立,第二条件不成立,则自动取消逗号。

(5)trim 标签

trim 替换where标签


trim标签替换 set标签

 update t_userusername=#{uname},address=#{uaddress}where id=#{uid}

可以替换where标签和set标签。

(6)foreach 标签


10、MyBatis 映射文件中,A 标签通过 include 引用了 B 标签的内容,B 标签能否定义在 A 标签的后面

虽然 MyBatis 解析 xml 映射文件是按照顺序解析的,但是,被引用的 B 标签依然可以定义在任何地方,MyBatis 都可以正确识别。

原理是,MyBatis 解析 A 标签,发现 A 标签引用了 B 标签,但是 B 标签尚未解析到,尚不存在,此时,MyBatis 会将 A 标签标记为未解析状态,然后继续解析余下的标签,包含 B 标签,待所有标签解析完毕,MyBatis 会重新解析那些被标记为未解析的标签,此时再解析 A 标签时,B 标签已经存在,A 标签也就可以正常解析完成了。

11、MyBatis 的 Mapper 接口工作原理是什么

Mapper接口,它是没有实现类的。当调用接口方法的时候,它是采用了JDK的动态代理的方式。

在这里插入图片描述

UserMapper userMapper=session.getMapper(UserMapper.class);
  1. Mapper接口的Class对象会被包装成MapperProxyFactory对象,通过MapperProxyFactory对象调用newInstance创建Mapper接口动态代理对象MapperProxy。
  2. 执行Mapper接口方法时候,其实执行的就是用代理对象执行接口方法,本质执行的是MapperProxy代理类的invoke方法,invoke方法中使用MapperMethod对象执行execute方法。
  3. 在execute方法中根据MapperMethod对象中的操作类型选择调用的原生方法(接口名,参数)。

每次通过调用接口方法操作数据库的时候,Mybatis都会利用MapperProxyFactory创建当前Mapper接口对应的MapperProxy代理实现类,在此代理类定义的增强中,会利用sqlSession、接口、方法等信息构造MapperMethod。MapperMethod是Mybatis对Mapper的接口方法生成的对应封装类,此封装类定义了真正的操作数据库的代码实现,最终对数据库的操作就是依赖他实现的。

12、MyBatis 的工作原理是什么

  1. 系统启动时候会读取全局配置文件mybatis-config.xml和加载映射文件Mapper.xml,加载的相关信息都会保存在Configuration对象中。
  2. MyBstis的环境配置信息构建会话工厂SqlSessionFactory(单例模式)
  3. SqlSessionFactory会话工厂创建SqlSession对象,这个对象中包含执行SQL语句的所有方法
  4. MyBatis底层定义了一个Executer执行器来操作数据库,它会根据SqlSession传递的参数生成需要执行的语句,同时负责查询缓存维护。
  5. 在Executer中MappedStatement对象,该参数是对映射信息的封装,用于储存要映射的SQL语句的id,参数等信息

在这里插入图片描述

13、MyBatis 中一对一查询、一对多查询是怎么实现的

  1. 在MyBatis中,使用association标签来解决一对一的关联查询。association标签可用的属性有:(1)property:对象属性的名称(2)javaType:对象属性的类型(3) column:对应的外键字段名称(4)select:使用另一个查询封装的结果。
  2. 在MyBatis中,使用collection标签来解决一对多的关联查询。collection标签可用的属性有:(1)property:指的是集合属性的值(2)ofType:指的是集合中元素的类型(3)column:所对应的外键字段名称(4)select:使用另一个查询封装的结果。

一对一关联查询举例如下



select Id, Name, ClassId, Status, AddTime, UpdateTime from RUN_Student

一对多关联查询举例如下



select c.Id, c.ClassName, c.Status, c.AddTime, c.UpdateTime,s.Id as sId, s.Name as sName, s.ClassId as sClassId, s.Status as sStatus, s.AddTime as sAddTime, s.UpdateTime as sUpdateTimefrom RUN_Class c join RUN_Student s on c.Id =  s.classId

14、MyBatis 的分页方法有哪些

(1)在MyBatis中,是使用RowBounds对象进行分页的,它是针对ResultSet结果集执行的逻辑分页,而不是物理分页。
(2)另外,我们可以在SQL内,直接书写带有物理分页的参数来完成物理分页功能,也可以使用第三方的分页插件PageHelper来完成物理分页。



(3)分页插件的原理(物理分页):就是使用MyBatis提供的插件接口,来实现自定义插件。在自定义插件的拦截方法内,拦截待执行的SQL,然后根据设置的分页参数 重写SQL ,生成带有分页语句的SQL,最终执行的是重写之后的SQL,从而实现分页。 举例:

select * from student,分页插件拦截SQL后 重写SQL为:select t.* from (select * from student)t limit 0,10;

15、MyBatis 中都有哪些Executor执行器,它们之间的区别是什么

在 MyBatis 配置文件中,可以指定默认的 ExecutorType 执行器类型,也可以手动给 SqlSessionFactory 的创建 SqlSession 的方法传递 ExecutorType 类型参数。

在这里插入图片描述
BaseExecutor:基础抽象类,实现了Executor接口的大部分方法,主要提供了缓存管理和事务管理的能力,使用了模板模式,doUpdate、doQuery、doQueryCursor 等方法的具体实现交给不同的子类去实现。

  1. SimpleExecutor:BaseExecutor的具体子类实现,且为默认配置,在doQuery方法中使用PrepareStatement对象访问数据库,每次访问都要创建新的PrepareStatement对象,用完立刻关闭PrepareStatement。

  2. ReuseExecutor:BaseExecutor的具体子类实现,与SimpleExecutor不同的是,在doQuery方法中,以 sql 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map内,供下一次使用。简言之,会重用缓存中的statement对象,而不是每次都创建新的PrepareStatement。

  3. BatchExecutor:BaseExecutor的具体子类实现,在doUpdate方法中,提供批量执行多条SQL语句的能力。将所有 sql 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个 Statement 对象,每个 Statement 对象都是 addBatch()完毕后,等待逐一执行 executeBatch()批处理。与 JDBC 批处理相同。

  4. CachingExecutor:直接实现Executor接口,使用装饰器模式提供二级缓存能力。先从二级缓存中查询,缓存没有命中再从数据库中查询,最后将结果添加到缓存中再返回给用户。如果在xml文件中配置了节点,则会创建 CachingExecutor。

16、SpringBoot + Mybatis 一级缓存和二级缓存详解

一级缓存

一级缓存在 mybatis 中默认是开启的并且是 session 级别,它的作用域为一次 sqlSession 会话。 一个SqlSession对象中创建一个本地缓存(local cache),在同一个SqlSession中,执行相同的SQL查询时,会尝试去本地缓存中查找是否在缓存,如果在缓存中,就直接从缓存中取出,然后返回给用户,否则,从数据库读取数据,将查询结果存入缓存并返回给用户。
在这里插入图片描述

代码演示:

 @Test@Transactional(rollbackFor = Throwable.class)public void testFistCache(){// 第一次查询,缓存到一级缓存userMapper.selectById(1);// 第二次查询,直接读取一级缓存userMapper.selectById(1);}
console 2023-03-15 14:53:58.084 DEBUG [BaseJdbcLogger.java:137] : ==>  Preparing: select * from user where id = ?
console 2023-03-15 14:53:58.084 DEBUG [BaseJdbcLogger.java:137] : ==> Parameters: 12(Integer)
console 2023-03-15 14:53:58.103 DEBUG [BaseJdbcLogger.java:137] : <==      Total: 1
User{userId=12, userName='Endo Riku', userSex='M', userAge=238}
User{userId=12, userName='Endo Riku', userSex='M', userAge=238}

可以看到,虽然进行了两次查询,但最终只请求了一次数据库,第二次查询命中了一级缓存,直接返回了数据。

这里有两点需要说明一下:

  1. 为什么开启事务

使用了数据库连接池,默认每次查询完之后自动 commite,这就导致两次查询使用的不是同一个sqlSessioin,根据一级缓存的原理,它将永远不会生效。当我们开启了事务,两次查询都在同一个 sqlSession 中,从而让第二次查询命中了一级缓存。

  1. 两种一级缓存模式

一级缓存的作用域有两种:session(默认)和 statment,可通过设置 local-cache-scope 的值来切换,默认为 session。二者的区别在于 session 会将缓存作用于同一个 sqlSesson,而 statment 仅针对一次查询,所以,local-cache-scope: statment 可以理解为关闭一级缓存。

  1. 一级缓存总结

mybatis 默认的 session 级别一级缓存,由于 springboot 中默认使用了 hikariCP,所以基本没用,需要开启事务才有用。但一级缓存作用域仅限同一 sqlSession 内,无法感知到其他 sqlSession 的增删改,所以极易产生脏数据。

二级缓存

默认情况下,mybatis 打开了二级缓存,但它并未生效,因为二级缓存的作用域是 namespace,所以还需要在 Mapper.xml 文件中配置一下才能使二级缓存生效


测试 单表二级缓存:

/*** 测试二级缓存效果* 需要*Mapper.xml开启二级缓存**/@Testpublic void testSecondCache(){userMapper.selectById(1);userMapper.selectById(1);}
console 2023-03-15 14:52:54.318 DEBUG [LoggingCache.java:60] : Cache Hit Ratio [com.example.canal.mybatis.mapper.UserMapper]: 0.5
console 2023-03-15 14:52:54.319 DEBUG [LoggingCache.java:60] : Cache Hit Ratio [com.example.canal.mybatis.mapper.UserMapper]: 0.6
User{userId=12, userName='Endo Riku', userSex='M', userAge=238}
User{userId=12, userName='Endo Riku', userSex='M', userAge=238}

这里可以看到,第二次查询直接命中了缓存,日志还打印了该缓存的命中率。读者可以自行关闭二级缓存查看效果,通过注掉对应 mapper.xml 的 cache 标签,或者 cache-enabled: false 均可

第一次调用mapper下的SQL去查询用户的信息,查询到的信息会存放代该mapper对应的二级缓存区域。 第二次调用namespace下的mapper映射文件中,相同的sql去查询用户信息,会去对应的二级缓存内取结果。

在这里插入图片描述

缓存的优先级

通过 mybatis 发起的查询,作用顺序为:二级缓存 -> 一级缓存 -> 数据库 ,其中任何一个环节查到不为空的数据,都将直接返回结果。

如果多表联查的二级缓存,user 表 left join user_order 表 on user.id = user_order.user_id
我们考虑这样一种情况,该联查执行两次,第二次联查前更新 user_order 表,如果只使用 cache 配置,将会查不到更新的 user_orderxi,因为两个 mapper.xml 的作用域不同,要想合到一个作用域,就需要用到 cache-ref

userOrderMapper.xml


userMapper.xml


二级缓存可通过 cache-ref 让多个 mapper.xml 共享同一 namespace,从而实现缓存共享,但多表联查时配置略微繁琐。所以生产环境建议将一级缓存设置为 statment 级别(即关闭一级缓存),如果有必要,可以开启二级缓存

17、MyBatis 是否支持延迟加载,如果支持,它的实现原理是什么

MyBatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。在 MyBatis 配置文件中,可以配置是否启用延迟加载 。

什么是延迟加载

就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。

局部延迟加载

在association和collection标签中都有⼀个fetchType属性,通过修改它的值,可以修改局部的加载策略。






全局延迟加载

在Mybatis的核⼼配置⽂件中可以使⽤setting标签修改全局的加载策略。



局部的加载策略的优先级高于全局的加载策略

延迟加载原理实现

它的原理是,使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用 a.getB().getName() ,拦截器 invoke() 方法发现 a.getB() 是 null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 a.getB().getName() 方法的调用。这就是延迟加载的基本原理。

18、MyBatis 是如何防止sql注入的

Mybatis也是用的预编译进行防护SQL注入。其实在框架底层,是JDBC中的PreparedStatement类在起作用。

@Test
public void test02() throws SQLException {Connection con = JdbcUtil.getConn();String sql = "SELECT * FROM test WHERE ID = ? AND NAME = ?";// 实例化PreparedStatement pstmt = con.prepareStatement(sql);// 装载占位符pstmt.setString(1, "6");pstmt.setString(2, "bb");// 执行sql语句ResultSet rs = pstmt.executeQuery();System.out.println(rs);while (rs.next()) {System.out.println(rs.getInt("id") + " " + rs.getString("name"));}JdbcUtil.close(con);
}

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...