Spring部分:
1、Spring框架的七大模块:
Spring Core:框架的最基础部分,提供 IoC 容器,对 bean 进行管理。
Spring Context:继承BeanFactory,提供上下文信息,扩展出JNDI、EJB、电子邮件、国际化等功能。
Spring DAO:提供了JDBC的抽象层,还提供了声明性事务管理方法。
Spring ORM:提供了JPA、JDO、Hibernate、MyBatis 等ORM映射层。
Spring AOP:集成了所有AOP功能。
Spring Web:提供了基础的 Web 开发的上下文信息,现有的Web框架,如JSF、Tapestry、Structs等,提供了集成。
Spring Web MVC:提供了 Web 应用的 Model-View-Controller 全功能实现。
Bean定义5种作用域:
singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
prototype : 每次请求都会创建一个新的 bean 实例。
request : 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
session : 每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。
global-session:全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经没有了。Portlet是能够生成语义代码(例如:HTML)片段的小型Java Web插件。它们基于portlet容器,可以像servlet一样处理HTTP请求。但是,与 servlet 不同,每个 portlet 都有不同的会话.
spring ioc初始化流程:
1、resource定位:即寻找用户定义的bean资源,由 ResourceLoader通过统一的接口Resource接口来完成 beanDefinition载入。
2、BeanDefinitionReader读取解析Resource定位的资源成BeanDefinition 载入到ioc中(通过HashMap进行维护BD)。
3、BeanDefinition注册 即向IOC容器注册这些BeanDefinition, 通过BeanDefinitionRegistery实现。 BeanDefinition加载流程: 定义BeanDefinitionReader解析xml的document BeanDefinitionDocumentReader解析document成beanDefinition
**DI依赖注入流程? **(实例化,处理Bean之间的依赖关系)
过程在Ioc初始化后,依赖注入的过程是用户第一次向IoC容器索要Bean时触发
1、如果设置lazy-init=true,会在第一次getBean的时候才初始化bean, lazy-init=false,会容器启动的时候直接初始化(singleton bean);
2、调用BeanFactory.getBean()生成bean的;
3、生成bean过程运用装饰器模式产生的bean都是beanWrapper(bean的增强);
Spring 中的单例 bean 的线程安全问题了解吗?
大部分时候我们并没有在系统中使用多线程,所以很少有人会关注这个问题。单例 bean 存在线程问题,主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。常见的有两种解决办法:
1、在Bean对象中尽量避免定义可变的成员变量(不太现实)。
2、在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。
依赖注入怎么处理bean之间的依赖关系?
其实就是通过在beanDefinition载入时,如果bean有依赖关系,通过占位符来代替,在调用getbean时候,如果遇到占位符,从ioc里获取bean注入到本实例来。
Bean的生命周期:
1.实例化Bean
2.按照Spring上下文对实例化的Bean进行配置--也就是IOC注入
3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值。
4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以)
5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法)。
6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;
7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法; 注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton,这里我们不做赘述。
9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;
10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。 juejin.cn/post/684490…
IOC:控制反转
将对象的创建权,由Spring管理. DI(依赖注入):在Spring创建对象的过程中,把对象依赖的属性注入到类中。
Spring的IOC注入方式
构造器注入 setter方法注入 注解注入 接口注入
怎么检测是否存在循环依赖?
Bean在创建的时候可以给该Bean打标,如果递归调用回来发现正在创建中的话,即说明了循环依赖了。
Spring如解决Bean循环依赖问题?
Spring中循环依赖场景有:构造器的循环依赖,属性的循环依赖
singletonObjects:第一级缓存,里面放置的是实例化好的单例对象;
earlySingletonObjects:第二级缓存,里面存放的是提前曝光的单例对象;
singletonFactories:第三级缓存,里面存放的是要被实例化的对象的对象工厂。 创建bean的时候Spring首先从一级缓存singletonObjects中获取。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取,如果还是获取不到就从三级缓存singletonFactories中取(Bean调用构造函数进行实例化后,即使属性还未填充,就可以通过三级缓存向外提前暴露依赖的引用值(提前曝光),根据对象引用能定位到堆中的对象,其原理是基于Java的引用传递),取到后从三级缓存移动到了二级缓存完全初始化之后将自己放入到一级缓存中供其他使用,因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。
构造器循环依赖解决办法:
在构造函数中使用@Lazy注解延迟加载。在注入依赖时,先注入代理对象,当首次使用时再创建对象说明:一种互斥的关系而非层次递进的关系,故称为三个Map而非三级缓存的缘由 完成注入;
Spring 中使用了哪些设计模式?
工厂模式:spring中的BeanFactory就是简单工厂模式的体现,根据传入唯一的标识来获得bean对象;
单例模式:提供了全局的访问点BeanFactory;
代理模式:AOP功能的原理就使用代理模式(1、JDK动态代理。2、CGLib字节码生成技术代理。)
装饰器模式:依赖注入就需要使用BeanWrapper;
观察者模式:spring中Observer模式常用的地方是listener的实现。如ApplicationListener。
策略模式:Bean的实例化的时候决定采用何种方式初始化bean实例(反射或者CGLIB动态字节码生成)
AOP 核心概念
1、切面(aspect):类是对物体特征的抽象,切面就是对横切关注点的抽象
2、横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。
3、连接点(joinpoint):被拦截到的点,因为 Spring 只支持方法类型的连接点,所以在Spring 中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器。
4、切入点(pointcut):对连接点进行拦截的定义
5、通知(advice):所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类。
6、目标对象:代理的目标对象
7、织入(weave):将切面应用到目标对象并导致代理对象创建的过程
8、引入(introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加方法或字段。
解释一下AOP
传统oop开发代码逻辑自上而下的,这个过程中会产生一些横切性问题,这些问题与我们主业务逻辑关系不大,会散落在代码的各个地方,造成难以维护,aop思想就是把业务逻辑与横切的问题进行分离,达到解耦的目的,提高代码重用性和开发效率;
AOP 主要应用场景有: 记录日志、监控性能、权限控制、事务管理
AOP源码分析:
@EnableAspectJAutoProxy给容器(beanFactory)中注册一个AnnotationAwareAspectJAutoProxyCreator对象;
AnnotationAwareAspectJAutoProxyCreator对目标对象进行代理对象的创建,对象内部是封装JDK和CGlib两个技术,实现动态代理对象创建的(创建代理对象过程中,会先创建一个代理工厂,获取到所有的增强器(通知方法),将这些增强器和目标类注入代理工厂,再用代理工厂创建对象);
代理对象执行目标方法,得到目标方法的拦截器链,利用拦截器的链式机制,依次进入每一个拦截器进行执行。
AOP使用哪种动态代理?
当bean的是实现中存在接口或者是Proxy的子类,使用jdk动态代理;
不存在接口,spring会采用CGLIB来生成代理对象;
JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类:Proxy 和 InvocationHandler。Proxy 利用 InvocationHandler(定义横切逻辑) 接口动态创建 目标类的代理对象。
Spring AOP 和 AspectJ AOP 有什么区别?
Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。
Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。
Spring AOP 已经集成了AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。
AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单,如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,它比Spring AOP 快很多。
jdk动态代理:
通过bind方法建立代理与真实对象关系,通过Proxy.newProxyInstance(target)生成代理对象。代理对象通过反射invoke方法实现调用真实对象的方法。
动态代理与静态代理区别:
静态代理:程序运行前代理类的.class文件就存在了;
动态代理:在程序运行时利用反射动态创建代理对象<复用性,易用性,更加集中都调用
CGLIB与JDK动态代理区别:
Jdk必须提供接口才能使用;CGLIB不需要,只要一个非抽象类就能实现动态代理
Spring 事务中的隔离级别有哪几种?
TransactionDefinition 接口中定义了五个表示隔离级别的常量:
TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,Mysql 默认采用的REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别
TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
Spring 事务中哪几种事务传播行为?
支持当前事务的情况:
TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
不支持当前事务的情况:
TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。 其他情况:
TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
@Transactional的失效场景和使用注意
@Transactional 可以作用在接口、类、类方法,声明式事务:
1、用于类:当把@Transactional 注解放在类上时,表示所有该类的public方法都配置相同的事务属性信息。
2、作用于方法:当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息。
3、作用于接口:不推荐这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效。
@Transactional失效场景:
1、@Transactional 应用在非 public 修饰的方法上:Spring 默认通过动态代理的方式实现 AOP,对目标方法进行增强,protected/private 方法无法代理到,Spring 自然也无法动态增强事务处理逻辑。
2、同一个类中方法调用,导致@Transactional失效:A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。
3、@Transactional 注解属性 propagation 设置错误。
4、数据库引擎不支持事务:常用的MySQL数据库默认使用支持事务的innodb引擎。一旦数据库引擎切换成不支持事务的myisam,那事务就从根本上失效了。
SpringBoot部分
SpringBoot启动流程
1、SpringApplication.run中执行了两步操作,先封装了一个SpringApplication的实例,再执行该实例的重载run方法。
2、SpringApplication封装实例时,利用spi机制读取了classpath下所有的MTEA-INF/spring.factories xml配置文件的ApplicationContextInitializer(容器初始化器)还有ApplicaiontListener(侦听器),将这两者封装到SpringApplication实例中
3、执行SpringApplication实例的run方法,run方法中默认初始化了Annotation配置的容器AnnotationConfigApplicationContext,并加载应用上下文(applicationContext)。通过lister实现创建spring容器, refreshContext() ,实现starter自动化配置,spring.factories文件加载, bean实例化。
4、执行上面ApplicationContextInitializer的initial方法,然后加载到Bean容器中。
SpringBoot自动配置的原理:
@EnableAutoConfiguration找到META-INF/spring.factories(需要创建的bean在里面)配置文件。读取每个starter中的spring.factories文件
-
SpringBoot 启动会加载大量的自动配置类
-
我们看我们需要的功能有没有 SpringBoot 默认写好的自动配置类;
-
我们再来看这个自动配置类中到底配置了哪些组件 ( 只要我们要用的组件有,我们就不需要再来配置,若没有,我们可能就要考虑自己写一个配置类让 SpringBoot 扫描了)
-
给容器中自动配置类添加组件的时候,会从 properties 类中获取某些属性。我们就可以在配置文件中指定这些属性的值;
xxxxAutoConfigurartion:自动配置类的作用就是给容器中添加组件
xxxxProperties:的作用就是封装配置文件中相关属性
Spring Boot 的核心注解
核心注解是@SpringBootApplication 由以下三种组成
@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:打开自动配置的功能。
@ComponentScan:Spring组件扫描。
SpringBoot常用starter都有哪些
spring-boot-starter-web - Web 和 RESTful 应用程序;
spring-boot-starter-test - 单元测试和集成测试;
spring-boot-starter-jdbc - 传统的 JDBC;
spring-boot-starter-security - 使用 SpringSecurity 进行身份验证和授权
spring-boot-starter-data-jpa - 带有 Hibernate 的 Spring Data JPA;
spring-boot-starter-data-rest - 使用 Spring Data REST 公布简单的 REST 服务。
Spring Boot 的核心配置文件
1、Application.yml 一般用来定义单个应用级别的,如果搭配 spring-cloud-config 使用。
2、Bootstrap.yml(先加载) 系统级别的一些参数配置,这些参数一般是不变的。
springMVC流程:
1、用户请求发送给DispatcherServlet,DispatcherServlet调用HandlerMapping处理器映射器;
2、HandlerMapping根据xml或注解找到对应的处理器,生成处理器对象返回给DispatcherServlet;
3、DispatcherServlet会调用相应的HandlerAdapter;
4、HandlerAdapter经过适配调用具体的处理器去处理请求,生成ModelAndView返回给DispatcherServlet
5、DispatcherServlet将ModelAndView传给ViewReslover解析生成View返回给DispatcherServlet;
6、DispatcherServlet根据View进行渲染视图;
->DispatcherServlet->HandlerMapping->Handler ->DispatcherServlet->HandlerAdapter处理handler->ModelAndView ->DispatcherServlet->ModelAndView->ViewReslover->View ->DispatcherServlet->返回给客户
Zookeeper+eureka+Springcloud部分
Zuul与Gateway区别
1:zuul则是netflix公司的项目集成在spring-cloud中使用而已, Gateway是spring-cloud的一个子项目;
2:zuul不提供异步支持流控等均由hystrix支持, gateway提供了异步支持,提供了抽象负载均衡,提供了抽象流控;理论上gateway则更适合于提高系统吞吐量(但不一定能有更好的性能),最终性能还需要通过严密的压测来决定
3:两者底层实现都是servlet,但是gateway多嵌套了一层webflux框架
4:zuul可用至其他微服务框架中,内部没有实现限流、负载均衡;gateway只能用在springcloud中。
Zuul原理分析
(1):请求给zuulservlet处理(HttpServlet子类) zuulservlet中有一个zuulRunner对象,该对象中初始化了RequestContext(存储请求的数据),RequestContext被所有的zuulfilter共享;
(2):zuulRunner中有 FilterProcessor(zuulfilter的管理器),其从filterloader 中获取zuulfilter;
(3):有了这些filter之后, zuulservelet执行的Pre-> route-> post 类型的过滤器,如果在执行这些过滤器有错误的时候则会执行error类型的过滤器,执行完后把结果返回给客户端.
Gateway原理分析
(1):请求到达DispatcherHandler, DispatchHandler在IOC容器初始化时会在容器中实例化HandlerMapping接口。
(2):用handlerMapping根据请求URL匹配到对应的Route,然后有对应的filter做对应的请求转发最终response返回去。
Zookeeper 工作原理(待查)
Zookeeper 的核心是原子广播,这个机制保证了各个 server 之间的同步。实现这个机制的协议叫做 Zab 协议。Zab 协议有两种模式,它们分别是恢复模式和广播模式。
zoo与eur区别
1、zookeeper保证cp(一致性):zoo在选举期间注册服务瘫痪,期间不可用。eureka保证ap(可用性):eur各个节点平等关系,只要有一台就可保证服务可用,而查询到的数据可能不是最新的,可以很好应对网络故障导致部分节点失联情况
2、zoo有leader和follower角色,eur各个节点平等。
3、zoo采用半数存活原则(避免脑裂),eur采用自我保护机制来解决分区问题
4、eur本质是个工程,zoo只是一个进程
ZooKeeper基于CP,不保证高可用,如果zookeeper正在选主,或者Zookeeper集群中半数以上机器不可用,那么将无法获得数据。Eureka基于AP,能保证高可用,即使所有机器都挂了,也能拿到本地缓存的数据。作为注册中心,其实配置是不经常变动的,只有发版(发布新的版本)和机器出故障时会变。对于不经常变动的配置来说,CP是不合适的,而AP在遇到问题时可以用牺牲一致性来保证可用性,既返回旧数据,缓存数据。所以理论上Eureka是更适合做注册中心。而现实环境中大部分项目可能会使用ZooKeeper,那是因为集群不够大,并且基本不会遇到用做注册中心的机器一半以上都挂了的情况。所以实际上也没什么大问题。
Hystrix原理(待查) 通过维护一个自己的线程池,当线程池达到阈值的时候,就启动服务降级,返回fallback默认值 为什么需要hystrix熔断 防止雪崩,及时释放资源,防止系统发生更多的额级联故障,需要对故障和延迟进行隔离,防止单个依赖关系的失败影响整个应用程序;
微服务优缺点 1、每个服务高内聚,松耦合,面向接口编程;
2、服务间通信成本,数据一致性,多服务运维难度增加,http传输效率不如rpc
eureka自我保护机制
1、eur不移除长时间没收到心跳而应该过期的服务
2、仍然接受新服务注册和查询请求,但是不会同步到其它节点(高可用)
3、当网络稳定后,当前实例新注册信息会同步到其它节点(最终一致性)
Mybatis部分
什么是 Mybatis? Mybatis 是一个半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时 只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。程序员直接编写原生态 sql,可以严格控制 sql 执行性 能,灵活度高。MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。通过 xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过java 对象和 statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。(从执行 sql 到返回 result 的过程)。
Mybaits 的优点
1、基于 SQL 语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL 写在 XML 里,解除 sql 与程序代码的耦合,便于统一管理;提供 XML标签,支持编写动态 SQL 语句,并可重用。
2、与 JDBC 相比,减少了 50%以上的代码量,消除了 JDBC 大量冗余的代码,不需要手动开关连接;很好的与各种数据库兼容(因为 MyBatis 使用 JDBC 来连接数据库,所以只要 JDBC 支持的数据库 MyBatis 都支持)。
3、能够与 Spring 很好的集成;
4、提供映射标签,支持对象与数据库的 ORM 字段关系映射;提供对象关系映射 标签,支持对象关系组件维护。
MyBatis 框架的缺点
1、SQL 语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL 语句的功底有一定要求。
2、SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
MyBatis 框架适用场合
1、MyBatis 专注于 SQL 本身,是一个足够灵活的 DAO 层解决方案。
2、对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis 将是 不错的选择。
MyBatis 与 Hibernate 有哪些不同?
1、Mybatis 和 hibernate 不同,它不完全是一个 ORM 框架,因为 MyBatis 需要 程序员自己编写 Sql 语句
2、Mybatis 直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高,非常 适 合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需 求变化要求迅速输出成果。但是灵活的前提是 mybatis 无法做到数据库无关性, 如果需要实现支持多种数据库的软件,则需要自定义多套 sql 映射文件,工作量大。
3、Hibernate 对象/关系映射能力强,数据库无关性好,对于关系模型要求高的 软件,如果用 hibernate 开发可以节省很多代码,提高效率。
#{}和${}的区别是什么?
1、#{}是预编译处理,${}是字符串替换。
2、Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值;
3、Mybatis 在处理时 , 就 是 把 {}时,就是把时,就是把{}替换成变量的值。
4、使用#{}可以有效的防止 SQL 注入,提高系统安全性。
当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
第 1 种: 通过在查询的 sql 语句中定义字段名的别名,让字段名的别名和实体类 的属性名一致
<select id=”selectorder” parametertype=”int” resultetype=” me.gacl.domain.order”> select order_id id, order_no orderno ,order_price price form orders where order_id=#{id};
第 2 种: 通过来映射字段名和实体类属性名的一一对应的关系。
select * from orders where order_id=#{id}<!–用 id 属性来映射主键字段–>
<!–用 result 属性来映射非主键字段,property 为实体类属性名,column 为数据表中的属性–>
模糊查询 like 语句该怎么写?
第 1 种:在 Java 代码中添加 sql 通配符。
string wildcardname = “%smi%”;
list names = mapper.selectlike(wildcardname); select * from foo where bar like #{value}
第 2 种:在 sql 语句中拼接通配符,会引起 sql 注入
string wildcardname = “smi”;
list names = mapper.selectlike(wildcardname); select * from foo where bar like "%"#{value}"%"
Mapper 接口的工作原理是什么?Mapper 接口里的方法,参数不同时,方法能重载吗?
Dao 接口即 Mapper 接口。接口的全限名,就是映射文件中的 namespace 的值;接口的方法名,就是映射文件中 Mapper 的 Statement 的 id 值;接口方法内的参数,就是传递给 sql 的参数。
Mapper 接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个 MapperStatement。在 Mybatis 中,每一个 、、、标签,都会被解析为一个MapperStatement 对象。 举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到 namespace 为com.mybatis3.mappers.StudentDao 下面 id 为findStudentById 的 MapperStatement。 Mapper 接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。Mapper 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK动态代理为 Mapper 接口生成代理对象 proxy,代理对象会拦截接口方法,转而执行 MapperStatement 所代表的 sql,然后将 sql 执行结果返回。 Mybatis 是如何进行分页的?分页插件的原理是什么? Mybatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页。可以在 sql 内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。 分页插件的基本原理是使用 Mybatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。 Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式? 第一种是使用标签,逐一定义数据库列名和对象属性名之间的映射关系。 第二种是使用 sql 列的别名功能,将列的别名书写为对象属性名。 有了列名与属性名的映射关系后,Mybatis 通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。 如何执行批量插入? 首先,创建一个简单的 insert 语句: insert into names (name) values (#{value}) 然后在 java 代码中像下面这样执行批处理插入: list < string > names = new arraylist(); names.add(“fred”); names.add(“barney”); names.add(“betty”); names.add(“wilma”); // 注意这里 executortype.batch sqlsession sqlsession = sqlsessionfactory.opensession(executortype.batch); try { namemapper mapper = sqlsession.getmapper(namemapper.class); for (string name: names) { mapper.insertname(name); } sqlsession.commit(); } catch (Exception e) { e.printStackTrace(); sqlSession.rollback(); } finally { sqlsession.close(); } 如何获取自动生成的(主)键值? insert 方法总是返回一个 int 值 ,这个值代表的是插入的行数。如果采用自增长策略,自动生成的键值在 insert 方法执行完后可以被设置到传入 的参数对象中。 示例: insert into names (name) values (#{name}) name name = new name(); name.setname(“fred”); int rows = mapper.insertname(name); // 完成后,id 已经被设置到对象中 system.out.println(“rows inserted = ” + rows); system.out.println(“generated key value = ” + name.getid()); 在 mapper 中如何传递多个参数? 1、第一种: public UserselectUser(String name,String area); 对应的 xml,#{0}代表接收的是 dao 层中的第一个参数,#{1}代表 dao 层中第二参数,更多参数一致往后加即可。 <select id="selectUser"resultMap="BaseResultMap"> select * fromuser_user_t where user_name = #{0} and user_area=#{1}
2、第二种: 使用 @param 注解:
public interface usermapper { user selectuser(@param(“username”) string username,@param(“hashedpassword”) string hashedpassword); } 然后,就可以在 xml 像下面这样使用(推荐封装为一个 map,作为单个参数传递给mapper) select id, username, hashedpassword from some_table where username = #{username} and hashedpassword = #{hashedpassword}
3、第三种:多个参数封装成 map
try {
//映射文件的命名空间.SQL 片段的 ID,就可以调用对应的映射文件中的SQL //由于我们的参数超过了两个,而方法中只有一个 Object 参数收集,因此 我们使用 Map 集合来装载我们的参数
Map < String, Object > map = new HashMap();
map.put("start", start);
map.put("end", end);
return sqlSession.selectList("StudentID.pagination", map);
}catch (Exception e) {
e.printStackTrace();
sqlSession.rollback();
throw e;
} finally {
MybatisUtil.closeSqlSession();
}
Mybatis 动态 sql 有什么用?执行原理?有哪些动态 sql?
Mybatis 动态 sql 可以在 Xml 映射文件内,以标签的形式编写动态 sql,执行原理 是根据表达式的值 完成逻辑判断并动态拼接 sql 的功能。
Mybatis 提供了 9 种动态 sql 标签:trim | where | set | foreach | if | choose | when | otherwise | bind。
Xml 映射文件中,除了常见的 select|insert|updae|delete 标签之外,还有哪些标签?
、、、、,加上动态 sql 的 9 个标签,其中为 sql 片段标签,通过标签引入 sql 片段,为不支持自增的主键生成策略标签。
Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复?
不同的 Xml 映射文件,如果配置了 namespace,那么 id 可以重复;如果没有配置 namespace,那么 id 不能重复;
原因就是 namespace+id 是作为 Map<String, MapperStatement>的 key使用的,如果没有 namespace,就剩下 id,那么,id 重复会导致数据互相覆盖。有了 namespace,自然 id 就可以重复,namespace 不同,namespace+id 自然也就不同。
为什么说 Mybatis 是半自动 ORM 映射工具?它与全自动的区别在哪里?
Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而 Mybatis在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映射工具。 一对一、一对多的关联查询 ?
select * from class c,teacher t where c.teacher_id=t.t_id and c.c_id=#{id}
select * from class c,teacher t,student s where c.teacher_id=t.t_id and c.c_id=s.class_id and c.c_id=#{id}MyBatis 实现一对一有几种方式?具体怎么操作的?
有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次, 通过在resultMap 里面配置 association 节点配置一对一的类就可以完成。
嵌套查询是先查一个表,根据这个表里面的结果的 外键 id,去再另外一个表里面查询数据,也是通过 association 配置,但另外一个表的查询通过 select 属性配置。
Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?
Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加 载,association 指的就是一对一,collection 指的就是一对多查询。在 Mybatis 配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false。
它的原理是,使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用 a.getB().getName(),拦截器 invoke()方法发现 a.getB()是null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 a.getB().getName()方法的调用。这就是延迟加载的基本原理。 当然了,不光是 Mybatis,几乎所有的包括 Hibernate,支持延迟加载的原理都是一样的。
Mybatis 的一级、二级缓存
1、一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就 将清空,默认打开一级缓存。
2、二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现 Serializable 序列化接口(可用来保存对象的状态),可在它的映射文件中配置 。
3、对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。
什么是 MyBatis 的接口绑定?有哪些实现方式?
接口绑定,就是在 MyBatis 中任意定义接口,然后把接口里面的方法和 SQL 语句绑定, 我们直接调用接口方法就可以,这样比起原来了 SqlSession 提供的方法我们可以有更加灵活的选择和设置。
接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上@Select、@Update 等注解,里面包含 Sql 语句来绑定;
另外一种就是通过 xml里面写 SQL 来绑定, 在这种情况下,要指定 xml 映射文件里面的 namespace 必须为接口的全路径名。当 Sql 语句比较简单时候,用注解绑定, 当 SQL 语句比较复杂时候,用 xml 绑定,一般用 xml 绑定的比较多。
使用 MyBatis 的 mapper 接口调用时有哪些要求?
1、Mapper 接口方法名和 mapper.xml 中定义的每个 sql 的 id 相同;
2、Mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的parameterType 的类型相同;
3、Mapper 接口方法的输出参数类型和 mapper.xml 中定义的每个 sql 的resultType 的类型相同;
4、Mapper.xml 文件中的 namespace 即是 mapper 接口的类路径。
简述 Mybatis 的插件运行原理,以及如何编写一个插件
答:Mybatis 仅可以编写针对 ParameterHandler、ResultSetHandler、StatementHandler、Executor 这 4 种接口的插件,Mybatis 使用 JDK 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler 的 invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
编写插件:实现 Mybatis 的 Interceptor 接口并复写 intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。