有需要互关的小伙伴,关注一下,有关必回关,争取今年认证早日拿到博客专家
Spring事务失效的场景
-
没有加@Transactional注解。
-
异常未被正确捕获:默认情况下只在遇到RuntimeException及其子类时进行回滚,其他异常不回滚。
指定特定异常回滚
@Transactional(rollbackFor = { CustomException.class }) public void myTransactionalMethod() { // 在这个方法中抛出CustomException异常时,事务将回滚 }
指定特定异常不回滚
@Transactional(noRollbackFor = { CustomException.class }) public void myTransactionalMethod() { // 在这个方法中抛出CustomException异常时,事务将不会回滚 }
-
不通过代理对象调用(通过目标对象调用)。
-
事务方法是私有的或final的(动态代理需要继承)。
-
使用不支持事务的存储引擎。
使用 Spring 框架的好处是什么?
ioc/aop/快速集成其他框架/扩展性
- 松耦合和依赖注入:Spring 提供了依赖注入(Dependency Injection)功能,使得对象之间的依赖关系更加松耦合。通过依赖注入,对象的依赖关系由容器负责管理,提高了代码的可维护性和可测试性。
- 面向切面编程(AOP)的支持:Spring 支持面向切面编程,可以将与核心业务逻辑无关的横切关注点(如事务管理、日志记录等)从业务逻辑中分离出来。这样可以提高代码的模块化程度,并实现横向的关注点复用。
- 可以快速集成其他框架和库:Spring 提供了对其他框架和库的集成支持,例如集成持久化框架(如 Hibernate、MyBatis)、集成消息队列(如 RabbitMQ、Kafka)、集成缓存框架(如 Redis、Ehcache)等。通过 Spring 的集成支持,可以简化框架和库的使用和配置。
- 提供了一致的编程模型:Spring 提供了一致的编程模型,使得开发者可以使用统一的方式来处理不同的技术细节。无论是处理 Web 请求、数据库操作、事务管理还是其他功能,都可以通过 Spring 提供的模块和API来实现。
- 提供了丰富的功能和扩展性:Spring 框架提供了丰富的功能和扩展点,可以满足各种应用场景的需求。例如,Spring MVC 提供了强大的 Web 开发功能,Spring Security 提供了安全认证和授权功能,Spring Boot 提供了快速构建和配置应用的能力。
- 良好的生态系统和社区支持:Spring 框架具有广泛的应用和活跃的社区支持。有许多开源项目和第三方库与 Spring 框架紧密集成,可以提供更多功能和扩展选项。同时,Spring 社区也提供了丰富的文档、教程和支持资源,便于开发者学习和使用。
总而言之,使用 Spring 框架可以
- 提高代码的可维护性、可测试性和扩展性
- 简化应用开发和集成过程
- 拥有强大的功能和丰富的生态系统支持。
解释下什么是 AOP?
OOP将业务封装为对象(对象的属性与行为/方法),横切关注点跨越了对象的边界(多个对象之间有共同的行为)
横切关注点:多个模块或组件共享的功能(方法),例如日志记录,事务管理,安全等
AOP 的代理有哪几种方式?
怎么实现 JDK 动态代理?
PersonServiceImpl target = new PersonServiceImpl();
// UserInterface接口的代理对象
// Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
Object proxy = Proxy.newProxyInstance(PersonService.class.getClassLoader(), new Class[]{PersonService.class}, (proxy1, method, args1) -> {
System.out.println("before...");
Object result = method.invoke(target, args1);
System.out.println("after...");
return result;
});
PersonService userService = (PersonService) proxy;
userService.addPerson(new Person("张三三"));
怎么实现CGLIB的动态代理?
PersonServiceImpl target = new PersonServiceImpl();
// 通过cglib技术 /ɪnˈhɑːnsə(r)/ 增强器
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonServiceImpl.class);
// 定义额外逻辑,也就是代理逻辑
enhancer.setCallbacks(new Callback[]{(MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("before...");
Object result = methodProxy.invoke(target, objects);
System.out.println("after...");
return result;
}});
// 动态代理所创建出来的UserService对象
PersonServiceImpl personService = (PersonServiceImpl) enhancer.create();
// 执行这个userService的test方法时,就会额外会执行一些其他逻辑
personService.addPerson(new Person("张三三"));
AOP 的基本概念:切面、连接点、切入点等?
通知类型(Advice)型(Advice)有哪些?
- 前置通知 @Before
- 后置通知 @After
- 返回通知 @AfterReturning
- 异常通知 @AfterThrowing
- 环绕通知 @Around
通知的执行属性
- 环绕通知方法前
- 前置通知
- 方法
- 环绕通知方法后
- 后置通知
- 返回通知/异常通知
谈谈你对 IOC 的理解?
Bean 的生命周期?
- 实例化前
- 实例化
- 实例化后
- 初始化前
- 初始化后
- 使用
- 销毁
Bean 的作用域?
- Singleton
- Prototype
- Request
- Session
Spring 中的单例 Bean 的线程安全问题了解吗?
Bean包含可变状态(例如实例变量),那么就存在线程安全问题。
为了解决单例Bean的线程安全问题,可以采取以下几种方式:
- 避免共享可变状态:尽量避免在单例Bean中使用可变实例变量,或者确保对可变状态的访问是线程安全的。可以使用不可变对象或使用线程安全的数据结构,如
ConcurrentHashMap
。 - 同步访问:使用同步机制(例如
synchronized
关键字或锁)来确保对共享状态的访问是互斥的。这样一次只能有一个线程访问该Bean,但可能会导致性能下降。 - 使用线程安全的Bean:对于需要共享可变状态的情况,可以使用线程安全的Bean,如
@Scope("prototype")
作用域的Bean或使用@RequestScope
、@SessionScope
等与线程相关的作用域。 - 使用ThreadLocal:可以使用
ThreadLocal
来为每个线程提供独立的实例。这样每个线程都可以独立地访问和修改自己的实例,避免了线程安全问题。
谈谈你对 Spring 中的事务的理解?
通过动态代理实现,方法前开启事务,方法结束后提交事物,发生异常时回归事物.
Spring 中的事务隔离级别?
与数据库事务隔离界别一致
- READ_UNCOMMITTED
- READ_COMMITTED
- REPEATABLE_READ(默认)
- SERIALIZABLE
isolation /ˌaɪsəˈleɪʃn/ 隔离
@Transactional(isolation = Isolation.READ_COMMITTED)
public enum Isolation {
DEFAULT(-1),
READ_UNCOMMITTED(1),
READ_COMMITTED(2),
REPEATABLE_READ(4),
SERIALIZABLE(8);
private final int value;
private Isolation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
Spring 中的事物传播行为?
propagation /ˌprɒpə’ɡeɪʃ(ə)n/ 传播
@Transactional(propagation = Propagation.REQUIRED)
public enum Propagation {
REQUIRED(0),
SUPPORTS(1),
MANDATORY(2),
REQUIRES_NEW(3),
NOT_SUPPORTED(4),
NEVER(5),
NESTED(6);
private final int value;
private Propagation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
- REQUIRED 如果当前线程所在环境没有事务,就创建一个事务,在事务里执行,如果当前线程所在环境有事务,则加入当前事务执行.
- REQUIRES_NEW 不存在事务,就创建一个事务,以事务的形式运行,如果有事务,则挂起原来的事务.
- NESTED 如果不存在事务,就创建一个事务,如果存在事务,则嵌套到存在的事务当中
- SUPPORTS 存在事务,则加入当前事务,不存在则以非事务的方式运行
- NOT_SUPPORTED 存在事务,就将当前事务挂起,以非事务的形式运行
- MANDATORY 如果当前没有事务,就报错
- NEVER 如果当前有事务就报错
测试代码见[Spring之事务的传播行为]
Spring 常用的注入方式有哪些?
- 构造函数注入(Constructor Injection)
- Setter 方法注入(Setter Injection)
- 字段注入(Field Injection)
Spring 框架中用到了哪些设计模式?
- 模板方法 org.springframework.context.support.AbstractApplicationContext#onRefresh
- 代理模式 aop、@Lookup 、@Lazy、事务等
- 工厂模式
- 观察者模式(Observer Pattern):Spring 中的事件机制使用观察者模式。应用程序可以发布事件,而事件监听器可以订阅这些事件并执行相应的操作。
- 适配器模式(Adapter Pattern):Spring MVC 中的处理器适配器就是使用适配器模式实现的,它将请求适配到处理器方法。
- 策略模式(Strategy Pattern):Spring 的资源访问策略和验证策略等功能使用策略模式。通过定义不同的策略实现类,并将其注入到相应的组件中,可以根据需要选择合适的策略。
ApplicationContext 通常的实现有哪些?
- ClassPathXmlApplicationContext:从类路径下的 XML 配置文件中加载上下文。
- FileSystemXmlApplicationContext:从文件系统中的 XML 配置文件中加载上下文。
- AnnotationConfigApplicationContext:通过 JavaConfig 类或注解配置加载上下文。
谈谈你对 MVC 模式的理解?
SpringMVC 的工作原理/执行流程?
- 获取处理器适配器 getHandlerAdapter
- 获取处理器适配器 getHandlerAdapter
- 执行handler
- 解析并渲染视图
SpringMVC 的核心组件有哪些?
- DispatcherServlet(调度器):DispatcherServlet 是 Spring MVC 的前端控制器,负责接收所有的客户端请求并将它们分派给相应的处理程序。
- HandlerMapping(处理程序映射器):HandlerMapping 用于将请求映射到相应的处理程序(也称为控制器)。它根据请求的 URL 或其他条件决定选择哪个处理程序来处理请求。
- Controller(控制器):控制器是一个组件,负责处理请求并生成响应。它通常是一个带有注解的 Java 类,可以通过方法级别的映射来处理特定的请求。
- Model(模型):模型表示应用程序中的数据和业务逻辑。它可以是一个简单的 Java 对象(POJO)或通过数据访问层与数据库交互。
- View(视图):视图是用户界面的呈现方式。它可以是一个 JSP(JavaServer Pages)、Thymeleaf 模板、Freemarker 模板等。视图负责将模型中的数据呈现给用户。
- ViewResolver(视图解析器):ViewResolver 用于解析视图的逻辑名称并将其转换为实际的视图对象。它根据配置的规则查找适当的视图,并将其返回给 DispatcherServlet。
- HandlerInterceptor(处理程序拦截器):处理程序拦截器用于在请求处理的不同阶段进行拦截和处理。它可以在请求到达控制器之前或之后执行一些共享的任务,例如身份验证、日志记录等。
- ModelAndView(模型和视图的容器):ModelAndView 是一个容器,用于封装控制器处理方法的模型数据和视图信息。它允许控制器设置模型数据并指定要呈现的视图。
SpringMVC 常用的注解有哪些?
- @Component
- @Controller
- @RestController
- @Service
- @Repository
- @RequestMapping
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @PathVariable
- @RequestParam
- @ResponseBody
- @RequestBody
- @Autowired
- @Qualifier
- @Value
- @Configuration
- @Bean
@RequestMapping 的作用是什么?
将请求映射到处理器类上或者处理器方法上
如何解决 POST 请求中文乱码问题,GET 的又如何处理呢?
post请求:设置字符编码过滤器来实现
get请求:Spring MVC会使用URL编码来传输参数,可以在Controller中手动进行解码操作。
import java.net.URLDecoder;
...
@RequestMapping(value = "/example", method = RequestMethod.GET)
public String handleGetRequest(@RequestParam("param") String param) {
String decodedParam = URLDecoder.decode(param, "UTF-8");
// 处理解码后的参数
...
}
springboot中可以这么配置(基本都是默认配置)
# POST请求中文乱码处理
spring.http.encoding.force-request=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.force=true
# GET请求中文乱码处理
server.tomcat.uri-encoding=UTF-8
SpringMVC 的控制器是不是单例模式,如果是会有什么问题,怎么解决?
默认是的
并发问题
解决方案:
- 不使用成员变量或使用线程安全的成员变量例如ConcurrentHashMap
- 使用@Scope(“prototype”)
SpringMVC 怎么样设定重定向和转发的?
在springboot中的重定向
@RestController
public class HelloController {
@RequestMapping("/hello")
public Object hello(String name) {
String hello = "hello " + System.currentTimeMillis() + " " + name;
System.out.println(hello);
return hello;
}
}
@RestController
public class RedirectController {
@RequestMapping("/redirect0")
public RedirectView redirect0(RedirectAttributes redirectAttributes) {
// RedirectAttributes是Spring MVC提供的一个工具类,用于将参数添加到重定向URL中,类似于get请求
redirectAttributes.addAttribute("name", "张三");
RedirectView redirectView = new RedirectView();
redirectView.setUrl("hello");
System.out.println("redirect0");
return redirectView;
}
@RequestMapping("/redirect1")
public String redirect1(HttpServletResponse response, RedirectAttributes redirectAttributes) throws IOException {
// 参数转不过去
// redirectAttributes.addAttribute("name", "张三");
// response.sendRedirect("hello?name=lisi");
// 中文乱码
// response.sendRedirect("hello?name=李四");
response.sendRedirect("hello?name=" + URLEncoder.encode("李四", "utf-8"));
// 因为是浏览器实现的,会将此次请求执行完成,因此下面这行代码会执行
System.out.println("redirect1");
return "okk";
}
@RequestMapping("/redirect2")
public String redirect2() {
System.out.println("redirect2");
// @RestController 不会实现重定向的效果,使用@Controller可以
return "redirect0:hello";
}
}
@RestController
public class ForwardController {
@GetMapping("/forward0")
public ModelAndView forward0(String name) {
System.out.println("利用 ModelAndView 转发前 " + name);
ModelAndView modelAndView = new ModelAndView();
// 会将name继续传递给hello请求
modelAndView.setViewName("forward:hello");
System.out.println("利用 ModelAndView 转发后 " + name);
return modelAndView;
}
@RequestMapping("/forward1")
public String forward1(String name, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
System.out.println("利用 request 转发前 " + name);
// 会将name继续传递给hello请求
request.getRequestDispatcher("hello").forward(request, response);
System.out.println("利用 request 转发后 " + name);
return "okk";
}
@GetMapping("/forward2")
public String forward2() {
// 只能在@Controller下使用,@RestController当正常请求返回字符串
return "forward:hello";
}
}
SpringMVC 里面拦截器是怎么写的?
@Component
@WebFilter(urlPatterns = {"/*"})
public class CustomFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 在请求进入容器后 servlet前
System.out.println("CustomFilter doFilter 前");
// 放行请求
chain.doFilter(request, response);
// 请求离开servlet后
System.out.println("CustomFilter doFilter 后");
}
}
SpringMVC 和 Struts2 的区别有哪些?
- 框架结构:
- Spring MVC是基于Spring框架的一部分,它使用了依赖注入和面向切面编程等Spring的核心特性。Spring MVC采用前端控制器模式,使用DispatcherServlet来处理请求,并通过处理器映射器、处理器适配器和视图解析器来实现请求的处理和响应的生成。
- Struts2是一个独立的MVC框架,它是在Apache Struts的基础上进行重写和改进的。Struts2采用了拦截器的概念来处理请求,并通过配置文件来定义请求的处理和视图的生成。
- 编程模型:
- Spring MVC更加注重面向接口的编程,它支持使用接口来定义控制器和服务层的逻辑,并通过依赖注入来实现组件的解耦和可测试性。
- Struts2则更加注重基于类的编程模型,它使用基于配置的方式来定义控制器和拦截器,通过继承和注解来实现请求处理和功能扩展。
- 配置方式:
- Spring MVC的配置通常采用注解和Java配置的方式,可以使用
@Controller
注解来标识控制器类,使用@RequestMapping
注解来定义请求映射等。 - Struts2的配置主要采用XML配置文件的方式,通过
struts.xml
文件来定义控制器、拦截器、结果视图等。
- Spring MVC的配置通常采用注解和Java配置的方式,可以使用
- 其他特性:
- Spring MVC提供了更灵活的测试支持,可以通过MockMvc等工具进行单元测试和集成测试。
- Struts2提供了更强大的表单处理和校验支持,包括数据绑定、表单标签等。
谈谈你对 MyBatis 的理解?
MyBatis是一个开源的Java持久层框架,它简化了与关系型数据库的交互过程,通过将SQL语句与Java代码进行解耦,提供了一种优雅而灵活的方式来进行数据库访问。
以下是我对MyBatis的一些理解:
- SQL映射:MyBatis通过XML文件或注解的方式将SQL语句与Java方法进行映射。在XML文件中,我们可以编写SQL语句,并通过参数映射来传递数据。这种将SQL与Java代码分离的方式,使得SQL语句的维护和管理变得更加容易。
- 对象关系映射(ORM):MyBatis提供了对象关系映射的功能,可以将查询结果映射到Java对象中。通过配置映射规则,我们可以将数据库表的列与Java对象的属性进行映射,从而方便地操作和处理数据。
- 动态SQL:MyBatis支持动态SQL,可以根据不同的条件生成不同的SQL语句。通过使用if、choose、foreach等标签,我们可以根据需要拼接SQL语句,使得SQL的编写更加灵活和可扩展。
- 缓存机制:MyBatis内置了一级缓存和二级缓存机制,可以减少数据库访问的次数,提升性能。一级缓存是在同一个会话中共享的缓存,而二级缓存是在多个会话中共享的缓存。通过配置合适的缓存策略,我们可以根据需求来提高系统的性能。
- 插件机制:MyBatis提供了插件机制,可以通过自定义插件来扩展和修改MyBatis的行为。通过插件,我们可以在SQL执行前后进行拦截和处理,实现例如日志记录、性能监控等功能。
总的来说,MyBatis是一个功能强大且灵活的持久层框架,它与传统的ORM框架相比,更加贴近SQL,提供了更细粒度的控制和优化数据库访问的能力。它的简洁性、可扩展性和高性能使其成为Java开发中常用的数据库访问框架之一。
MyBaits 的优缺点有哪些?
MyBatis作为一个持久层框架,具有以下优点和缺点:
优点:
- 灵活性:MyBatis允许开发人员直接编写SQL语句,可以灵活地控制和优化SQL查询,适应各种复杂的数据库操作需求。
- 性能优化:MyBatis采用了一级缓存和二级缓存机制,可以减少数据库的访问次数,提高系统性能。
- 易于集成:MyBatis与其他Java框架(如Spring)的集成非常方便,可以与现有的应用程序无缝集成。
- 易于维护:通过将SQL语句与Java代码解耦,MyBatis的配置文件和SQL映射文件相对独立,易于维护和管理。
- 动态SQL支持:MyBatis提供了强大的动态SQL功能,可以根据条件动态生成SQL语句,使查询更加灵活和可扩展。
- 可扩展性:MyBatis提供了插件机制,允许开发人员自定义插件来扩展和修改MyBatis的行为。
缺点:
- 学习曲线:相对于一些ORM框架,MyBatis需要开发人员更加熟悉SQL语言和数据库操作,因此学习曲线可能相对较陡峭。
- 需要手动编写SQL:与完全自动化的ORM框架相比,MyBatis需要开发人员手动编写和管理SQL语句,可能增加了一定的开发工作量。
- XML配置的复杂性:MyBatis的配置文件通常使用XML格式,对于一些开发人员而言,可能对XML的熟悉程度不高,配置的编写和理解可能会有一定的难度。
- 编写错误可能导致安全问题:由于MyBatis允许直接编写SQL语句,如果编写不当或存在安全漏洞,可能导致SQL注入等安全问题。
总体而言,MyBatis是一个功能强大、灵活性高的持久层框架,它适用于需要对数据库操作进行精细控制和优化的项目。然而,它也需要开发人员对SQL和数据库操作有一定的了解,对于简单的CRUD操作,可能使用ORM框架更为便捷。
MyBatis 与 Hibernate 有哪些不同?
- 编程模型:
- MyBatis更接近于传统的SQL编程模型,需要开发人员手动编写SQL语句,并使用映射文件将结果映射到Java对象中。
- Hibernate则是一个全面的ORM框架,通过对象关系映射将Java对象与数据库表进行映射,开发人员无需编写SQL语句,直接操作Java对象进行持久化操作。
- SQL控制:
- MyBatis允许开发人员直接编写和控制SQL语句,提供了灵活性和可优化性,适用于对SQL细节有更高要求的场景。
- Hibernate自动处理SQL语句的生成和优化,隐藏了底层SQL语句的细节,开发人员可以专注于对象操作,适用于快速开发和简化ORM操作的场景。
- 缓存机制:
- MyBatis提供了一级缓存和二级缓存的支持,可以减少数据库访问,提高性能。
- Hibernate也提供了一级缓存和二级缓存的支持,但其缓存更加细粒度,包括实体对象缓存、集合缓存等。
- 映射配置:
- MyBatis使用XML或注解来进行映射配置,开发人员需要显式地指定SQL语句与Java对象之间的映射关系。
- Hibernate通过注解、XML或JPA标准进行对象与数据库表的映射配置,提供了更多的灵活性和选择。
- 社区和生态系统:
- Hibernate拥有更广泛的社区和更丰富的生态系统,具有更多的集成和扩展支持。
- MyBatis虽然社区相对较小,但其文档和教程资源也较为丰富,可以满足大部分的需求。
MyBatis 中 #{} 和 ${}的区别是什么?
- 语法解析:
#{}
:使用#{}
表示的参数是一个预编译的SQL参数,会被MyBatis解析为一个占位符,并自动进行参数值的安全转义和类型转换。这样可以防止SQL注入攻击,并保证参数值的正确性。${}
:使用${}
表示的参数是一个简单的字符串替换,会直接将参数值拼接到SQL语句中。在解析阶段,不会对参数值进行任何处理,它是一种简单的字符串替换方式。
- SQL注入防范:
#{}
:由于#{}
会将参数值进行预编译和安全转义处理,因此可以有效防止SQL注入攻击。${}
:由于${}
是简单的字符串替换,不进行预编译和安全转义处理,如果参数值不经过严格的验证和处理,可能会存在SQL注入的风险。
- 数据类型转换:
#{}
:使用#{}
时,MyBatis会根据参数类型自动进行数据类型转换,将参数值转换为正确的数据类型,然后传递给数据库执行。${}
:使用${}
时,MyBatis不会进行任何数据类型转换,参数值会按照字符串形式直接拼接到SQL语句中。如果参数类型不匹配,可能会导致SQL执行错误。
综上所述,#{}
是更安全和可靠的参数注入方式,能够有效防止SQL注入攻击,并进行参数值的类型转换。建议在编写MyBatis的SQL语句时,优先使用#{}
来处理参数,除非有特殊需求需要使用${}
进行字符串替换。
MyBatis 是如何进行分页的?分页插件的原理是什么?
通过sql的limit 子句
分页插件的原理:拦截查询自己,修改成分页的形式,然后再执行
PageHelper
MyBatis 有几种分页方式?
-
基于
RowBounds
的分页方式RowBounds rowBounds = new RowBounds(offset, limit); List<User> userList = sqlSession.selectList("getUserList", null, rowBounds);
-
基于
SELECT
语句参数的分页方式<select id="getUserList" parameterType="map" resultMap="userResultMap"> SELECT * FROM user LIMIT #{offset}, #{limit} </select>
-
分页插件的方式
// 设置分页参数 PageHelper.startPage(pageNum, pageSize); // 执行查询 List<User> userList = userDao.getUserList(); // 获取分页信息 PageInfo<User> pageInfo = new PageInfo<>(userList);
MyBatis 逻辑分页和物理分页的区别是什么?
- 逻辑分页是在查询结果集中进行切片,通过
OFFSET
和LIMIT
来限制返回的数据量,适用于数据量较小的情况。 - 物理分页是在数据库查询时进行限制,只返回符合条件的指定数量的记录,适用于数据量较大的情况。
MyBatis 是否支持延迟加载?如果支持,它的实现原理是什么?
支持,动态代理
https://juejin.cn/post/6844904062362583054
https://blog.csdn.net/friggly/article/details/124686876
MyBatis的延迟加载通过代理模式来实现。在查询时,MyBatis会返回一个代理对象而不是完整的实体对象。当访问代理对象的延迟加载属性时,MyBatis会根据需要执行额外的查询来加载相关数据。
延迟加载的实现原理如下:
- 代理对象生成:在查询操作中,当配置了延迟加载的属性时,MyBatis会生成一个代理对象,该代理对象持有一个对真实对象的引用。
- 属性访问触发:当应用程序访问代理对象的延迟加载属性时,触发代理对象的相应方法。
- 延迟加载执行:代理对象的方法会检查相关属性是否已加载。如果未加载,则执行额外的查询操作,从数据库中获取相关数据,并将其设置到真实对象中。
- 数据返回:获取到数据后,MyBatis会将数据填充到真实对象中,并返回给应用程序使用。
需要注意的是,延迟加载只适用于关联关系的属性,即存在一对一或一对多的关系。对于非关联关系的普通属性,延迟加载无效。
为了实现延迟加载,MyBatis提供了两种配置方式:
- 基于动态代理的延迟加载:通过配置MyBatis的XML映射文件,可以设置延迟加载属性,并在需要的时候通过动态代理实现延迟加载。
- 基于CGLIB的延迟加载:除了动态代理,MyBatis还支持使用CGLIB库生成子类来实现延迟加载。
通过使用延迟加载,可以减少不必要的数据库查询,提高查询效率和性能,特别是在处理复杂关联关系和大量数据的情况下,具有重要的优化意义。
说一下 MyBatis 的一级缓存和二级缓存?
-
一级缓存是SqlSession级别的缓存,作用域是一个SqlSession。在同一个SqlSession中,执行相同的查询sql,第一次会先去查询数据库,并写入缓存。第二次再执行时,则直接从缓存中取数据。如果两次执行查询sql的中间执行了增删改操作,则会清空该SqlSession的缓存。
-
二级缓存是mapper级别的缓存。作用域是mapper的同一个namespace下的sql语句。第一次执行查询SQL时,会将查询结果存到二级缓存区域内。第二次执行相同的查询SQL,则直接从缓存中取出数据。如果两次执行查询sql的中间执行了增删改操作,则会清空该namespace下的二级缓存。
Mybatis 有哪些执行器(Executor)?
MyBatis框架提供了三种执行器(Executor)来执行SQL语句和映射语句:
- SimpleExecutor(简单执行器):这是MyBatis默认的执行器。每执行一次SQL语句,就会创建一个新的Statement对象,并立即执行。它不会进行二级缓存的查询,也不会进行懒加载。适用于简单的查询场景。
- ReuseExecutor(重用执行器):在执行多次相同SQL语句时,会重用已经创建的Statement对象。如果查询语句存在于一级缓存中,将直接从缓存中获取结果。适用于多次重复执行相同SQL语句的场景。
- BatchExecutor(批处理执行器):用于批量操作,例如批量插入或更新数据。它会将多个SQL语句放入批处理中执行,以提高性能。它也支持一级缓存和懒加载。