答:①IOC即控制反转,简单来说就是把对象的控制权委托给spring框架,作用是降低代码的耦合度。②DI即依赖注入,是IOC的一种具体实现方式。假设一个Car类需要Engine的对象,那么一般需要new一个Engine,利用IOC就是只需要定义一个私有的Engine引用变量,容器会在运行时创建一个Engine的实例对象并将引用自动注入给变量。
传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
答:①Spring对bean进行实例化。②Spring将值和bean的引用注入到其对应的属性中。③调用BeanNameAware的setBeanName方法。④调用BeanFactoryAware的setBeanFactory方法。⑤调用AppicationContxtAware的setApplicationContext方法。⑥调用BeanPostProcessor的post-ProcessBeforeInitialization方法。⑦调用InitializingBean的after-PropertiesSet方法。如果bean使用init-method声明了自定义初始化方法,该方法也会被调用。⑧调用BeanPostProcessor的post-ProcessAfterInitialization方法。⑨使用bean。⑩调用DisposableBean的destroy方法,如果bean使用destroy-method声明了自定义销毁方法,该方法也会被调用。
答:通过scope指定bean的作用范围,有==①singleton:单例的,每次容器返回的对象是同一个。②prototype :多例的,每次返回的对象是新创建的实例。==③request:仅作用于HttpRequest,每次Http请求都会创建一个新的bean。④session:仅作用于HttpSession,不同的Session使用不同的实例,相同的Session使用同一个实例。⑤global session :仅作用于HttpSession,所有的Session使用同一个实例。
补充懒汉和饿汉模式:
1.单例模式:(默认的是单例模式)在Bean标签中可以通过scope属性指定对象的作用域scope=“singleton” 表示当前bean是单例模式
(默认饿汉模式;Spring容器初始化阶段就会完成此对象的创建;
当在bean标签中设置lazy-init=“true” 变为懒汉模式Spring容器初始化阶段就不会完成此对象的创建)
2.非单例模式:scope=“prototype” 表示当前bean是非单例模式,每次通过Spring容器获取此bean的对象时都会创建一个新的对象,
没有懒汉和饿汉的说法
答:①BeanFactory是一个Factory接口,是用来管理bean的IOC容器或对象工厂,较为古老,不支持spring的一些插件。BeanFactory使用了延迟加载,适合多例模式。②FactoryBean是一个Bean接口,是一个可以生产或者装饰对象的工厂Bean,可以通过实现该接口自定义的实例化Bean的逻辑。③ApplicationConext是BeanFactory的子接口,扩展了其功能,ApplicationContext是立即加载,适合单例模式。一般推荐使用ApplicationContext。
BeanFactory和FactoryBean区别:
BeanFactory是个Factory,也就是IOC容器或对象工厂,负责生产和管理Bean,提供一个Spring Ioc容器规范。==FactoryBean是个Bean,能生产或者修饰对象生成的工厂Bean,==隐藏了实例化一些复杂Bean的细节,为IOC容器中Bean的实现提供了更加灵活的方式,它的实现与设计模式中的工厂模式和修饰器模式类似
BeanFactory和ApplicationContext的区别
BeanFactory采用延迟加载来注入bean,在启动的时候不会去实例化Bean,在使用到的时候容器才会去实例化,好处是节约内存,坏处是运行速度比较慢
ApplicationContext采用预加载的方式,在启动的时候就把所有的Bean全部实例化了,缺点是占用内存,当应用程序配置较多bean的时候,程序启动较慢
通常情况下使用ApplicationContext,因为BeanFactory能完成的事情,ApplicationContext都能完成,并且提供了更多的功能
答:①通过默认无参构造器。使用bean标签,只使用id和class属性,如果没有无参构造器会报错。②使用静态工厂,通过bean标签中的class指明静态工厂,factory-method指明静态工厂方法。③使用实例工厂,通过bean标签中的factory-bean指明实例工厂,factory-method指明实例工厂方法。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountService" class="com.aping.service.impl.IAccountServiceImpl">
bean>
<bean id="accountDao" class="com.aping.dao.impl.IAccountDaoImpl">
bean>
<bean id="instanceFactory" class="com.aping.factory.InstanceFactory">bean>
<bean id="accountService1" factory-bean="instanceFactory" factory-method="getService">bean>
<bean id="accuntService2" class="com.aping.factory.StaticFactory" factory-method="getService">bean>
<bean id="accountService3" class="com.aping.service.impl.IAccountServiceImpl" scope="prototype">bean>
<bean id="accountService4" class="com.aping.service.impl.IAccountServiceImpl" init-method="init" destroy-method="destroy" scope="singleton">bean>
beans>
package com.aping.factory;
import com.aping.service.IAccountService;
import com.aping.service.impl.IAccountServiceImpl;
public class InstanceFactory {
public IAccountService getService(){
return new IAccountServiceImpl();
}
}
package com.aping.factory;
import com.aping.service.IAccountService;
import com.aping.service.impl.IAccountServiceImpl;
public class StaticFactory {
public static IAccountService getService(){
return new IAccountServiceImpl();
}
}
答:①可以注入的数据类型有基本数据类型、String、Bean、以及集合等复杂数据类型。②有三种注入方式,第一种是通过构造器注入,通过constructor-arg标签实现,缺点是即使不需要该属性也必须注入;第二种是通过Set方法注入,通过property标签实现,优点是创建对象时没有明确限制,缺点是某个成员变量必须有值,在获取对象时set方法可能还没有执行;第三种是通过注解注入,利用@Autowired自动按类型注入,如果有多个匹配则按照指定bean的id查找,查找不到会报错;@Qualifier在自动按照类型注入的基础之上,再按照 Bean 的 id 注入,给成员变量注入时必须搭配@Autowired,给方法注入时可单独使用;@Resource直接按照 Bean 的 id 注入;@Value用于注入基本数据类型和String。
答:①@Component,把当前类对象存入spring容器中,相当于在 xml 中配置一个 bean。value属性指定 bean 的 id,如果不指定 value 属性,默认 id 是当前类的类名,首字母小写。②@Service,一般用于业务层。③@Controller:一般用于表现层。④@Repository:一般用于持久层。⑤@Controller @Service @Repository都是针对@Component的衍生注解,作用及属性都是一模一样的,只是提供了更加明确的语义化。
类注解,用于声明一个单例模式的bean是否为懒汉模式
@Lazy(true) 表示声明为懒汉模式,默认为饿汉模式
答:Aop即面向切面编程,简单地说就是将代码中重复的部分抽取出来,在需要执行的时候使用动态代理的技术,在不修改源码的基础上对方法进行增强。优点是可以减少代码的冗余,提高开发效率,维护方便。Spring会根据类是否实现了接口来判断动态代理的方式,如果实现了接口会使用JDK的动态代理,核心是InvocationHandler接口和Proxy类,如果没有实现接口会使用cglib的动态代理,cglib是在运行时动态生成某个类的子类,如果某一个类被标记为final,是不能使用cglib动态代理的。
基于动态代理,实现在不改变原有业务的情况下对业务逻辑进行增强
动态代理:
答:①Joinpoint(连接点):指那些被拦截到的点,在 spring 中这些点指的是方法,因为 spring 只支持方法类型的连接点。例如业务层实现类中的方法都是连接点。②Pointcut(切入点):指我们要对哪些 Joinpoint 进行拦截的定义。例如业务层实现类中被增强的方法都是切入点,切入点一定是连接点,但连接点不一定是切入点。③Advice(通知/增强):指拦截到 Joinpoint 之后所要做的事情。④Introduction(引介):引介是一种特殊的通知,在不修改类代码的前提下可以在运行期为类动态地添加一些方法或 Field。⑤Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。⑥Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类。⑦Target(目标):代理的目标对象。⑧Aspect(切面):是切入点和通知(引介)的结合。
AOP术语以及解释
1)连接点(Joinpoint)
程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些点中的特定点就称为“连接点”。
Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入通知。
连接点由两个信息确定:第一是用方法表示的程序执行点;第二是用相对点表示的方位。
2)切点(Pointcut)
每个程序类都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事物。
AOP通过“切点”定位特定的连接点。连接点相当于数据库中的记录,而切点相当于查询条件。
切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。
连接点是一个比较空泛的概念,就是定义了哪一些地方是可以切入的,也就是所有允许你通知的地方。
切点就是定义了通知被应用的位置 (配合通知的方位信息,可以确定具体连接点)
3)通知(Advice)
通知是织入到目标类连接点上的一段程序代码,在Spring中,通知除用于描述一段程序代码外,还拥有另一个和连接点相关的信息,这便是执行点的方位。
结合执行点方位信息和切点信息,我们就可以找到特定的连接点
Spring切面可以应用5种类型的通知:
前置通知(Before):在目标方法被调用之前调用通知功能
后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么
返回通知(After-returning):在目标方法成功执行之后调用通知
异常通知(After-throwing):在目标方法抛出异常后调用通知
环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
通知就定义了,需要做什么,以及在某个连接点的什么时候做。 上面的切点定义了在哪里做
4)目标对象(Target)
通知逻辑的织入目标类。如果没有AOP,目标业务类需要自己实现所有逻辑,而在AOP的帮助下,目标业务类只实现那些非横切逻辑的程序逻辑,而性能监视和事务管理等这些横切逻辑则可以使用AOP动态织入到特定的连接点上。
5)引介(Introduction)
引介是一种特殊的通知,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,我们可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
允许向现有类中添加方法和属性(通知中定义的)
6)织入(Weaving)
织入是将通知添加对目标类具体连接点上的过程。AOP像一台织布机,将目标类、通知或引介通过AOP这台织布机天衣无缝地编织到一起。根据不同的实现技术,AOP有三种织入的方式:
编译期织入: 这要求使用特殊的Java编译器。
类装载期织入: 这要求使用特殊的类装载器。
动态代理织入: 在运行期为目标类添加通知生成子类的方式。
把切面应用到目标对象来创建新的代理对象的过程,Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
7)代理(Proxy)
一个类被AOP织入通知后,就产出了一个结果类,它是融合了原类和通知逻辑的代理类。
根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用原类相同的方式调用代理类。
8)切面(Aspect)
切面由切点和通知组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
切点的通知的结合,切面知道所有它需要做的事:何时/何处/做什么
基于动态代理,实现在不改变原有业务的情况下对业务逻辑进行增强
答:@Before前置通知,@AfterThrowing异常通知,@AfterReturning后置通知,@After最终通知,@Around环绕通知。最终通知会在后置通知之前执行,为解决此问题一般使用环绕通知。
答:①aop:config用于声明开始 aop 的配置。②aop:aspect用于配置切面。属性:id给切面提供一个唯一标识。ref引用配置好的通知类 bean 的 id。③aop:pointcut用于配置切入点表达式,就是指定对哪些类的哪些方法进行增强。属性:expression用于定义切入点表达式,id用于给切入点表达式提供一个唯一标识。④aop:before用于配置前置通知,在切入点方法执行之前执行;aop:after-returning用于配置后置通知,在切入点方法正常执行之后执行,它和异常通知只能有一个执行;aop:after-throwing用于配置异常通知,在切入点方法执行产生异常后执行;aop:after用于配置最终通知,无论切入点方法执行时是否有异常,它都会在其后面执行。
切入点使用 aop:pointcut 元素声明
切入点必须定义在 aop:aspect 元素下, 或者直接定义在 aop:config 元素下.
– 定义在 aop:aspect 元素下: 只对当前切面有效
– 定义在 aop:config 元素下: 对所有切面都有效
基于 XML 的 AOP 配置不允许在切入点表达式中用名称引用其他切入点.
声明切入点的示例代码
==切面跟切面id,通知跟切入点id ==
切面类
import com.learn.spring.aspect_xml.LoggingAspect
public class LoggingAspect{
@AfterReturn
public void afterReturningMethod( ){
sout();
}
@Af
public void afterThrowingMethod( ){
sout();
}
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="loggingAspect" class="com.learn.spring.aspect_xml.LoggingAspect">bean>
<bean id="validatorAspect" class="com.learn.spring.aspect_xml.ValidatorAspect">bean>
<bean id="arithmeticCalculatorImpl" class="com.learn.spring.aspect_xml.ArithmeticCalculatorImpl">bean>
<aop:config>
<aop:pointcut expression="execution(* com.learn.spring.aspect_xml.*.*(..))"
id="myPointCut"/>
<aop:aspect ref="loggingAspect" order="1">
<aop:before method="beforeMethod" pointcut-ref="myPointCut"/>
<aop:after method="afterMethod" pointcut-ref="myPointCut"/>
<aop:after-returning method="afterReturningMethod" pointcut-ref="myPointCut" returning="result"/>
<aop:after-throwing method="afterThrowingMethod" pointcut-ref="myPointCut" throwing="ex"/>
aop:aspect>
aop:config>
beans>
package com.liguoqing.utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
//切面类
@Aspect
public class TransactionManager {
//切入点
@Pointcut("execution(* com.liguoqing.dao.*.*(..))")
//pc1相当于切入点的id
public void pc1(){
}
//通知
@Before("pc1()")
public void begin(){
System.out.println("~~~开启事务");
}
@After("pc1()")
public void commit(){
System.out.println("~~~提交事务");
}
@Around("pc1()")
public Object printExecuteTime(ProceedingJoinPoint point) throws Throwable {
long time1 = System.currentTimeMillis();
Object v = point.proceed();
long time2 = System.currentTimeMillis();
System.out.println("~~~time:" + (time2 - time1));
return v;
}
}
注意:注解使用虽然方便,但是只能在源码上添加注解,因此我们的自定义类提倡使用注解配置,但是如果使用到第三方提供的类则需要通过XML配置形式完成配置。
答:①springMVC是一种基于Java实现的mvc设计模型的请求驱动类型的轻量级Web层框架,作用包括:参数绑定(获取请求参数)、调用业务层 、进行请求响应。②mvc全名是model view controller模型视图控制器,model指数据模型,JavaBean的类,用来封装数据;view指jsp,html等用来展示数据给用户的界面;controller是整个流程的控制器,用来接收用户请求以及进行数据校验等功能。
答:
①在pom.xml中导入以下jar包:org.springframework下的spring-context)、spring-web、spring-webmvc、javax.servlet下的servlet-api、javax.servlet.jsp下的jsp-api以及测试用的junit包。
②创建一个springmvc.xml的springconfig配置文件,开启包扫描,注册视图解析器,配置视图的前缀和后缀。
③在web.xml中配置核心控制器,servlet和servlet-mapping的映射等。
答:①浏览器发送请求,被 DispatherServlet 捕获,该 Servlet 并不处理请求,而是把请求转发出去(控制器类),转发的路径是根据请求 URL,匹配@RequestMapping 中的内容
②根据执行方法的返回值和视图解析器(InternalResourceViewResolver),去指定的目录下查找指定名称的视图文件,Tomcat服务器渲染页面,做出响应。
答:①DispatcherServlet:前端控制器,用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。
②HandlerMapping:处理器映射器,负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
③Handler:处理器,它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理。
④HandlAdapter:处理器适配器,通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行
⑤View Resolver:视图解析器,负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
⑥View:视图,SpringMVC 提供了很多 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
SpringMVC的请求响应步骤如下:
具体步骤:
前端发过来一个请求给到前端控制器,前端控制器给handlermapping处理请求映射器看有没有符合的映射,有就叫处理器适配器去执行handler,处理后返回一个modealandveiw给视图解析器,视图解析器处理后就会返回数据,页面等等到前端
第一步:(发起)发起请求到前端控制器(DispatcherServlet)
第二步:(查找)前端控制器请求处理器映射器HandlerMapping查找Handler(可以根据xml配置,注解进行查找) 第三步:(返回)处理器映射器HandlerMapping向前端控制器返回Handler,HandlerMapping会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略 第四步:(调用)前端控制器调用处理器适配器HandlerAdapter去执行Handler 第五步:(执行)处理器适配器HandlerAdapter将会根据适配的结果去执行Handler 第六步:(返回)Handler执行完成给适配器返回ModelAndView
第七步:(接收)处理器适配器向前端控制器返回ModelAndView(ModelAndView是SpringMVC框架的一个底层对象,包括Model和View)
第八步:(解析)前端控制器请求视图解析器去进行视图解析(根据逻辑视图名解析成真正的视图(jsp),通过这种策略很容易更换其他视图技术,只需要更换视图解析器即可)
第九步:(返回)视图解析器向前端控制器返回View
第十步:(渲染)前端控制器进行视图渲染(视图渲染将模型数据(在ModelAndView对象中)填充到request域)
第十一步:(响应)前端控制器向用户响应结果
以下是对出现的一些组件的介绍:
前端控制器DispatcherServlet(不需要程序员开发)
作用:接收请求,响应结果,相当于转发器,中央处理器。有了DispatcherServlet减少了其它组件之间的耦合度。
处理器映射器HandlerMapping(不需要程序员开发)
作用:根据请求的url查找Handler
处理器适配器HandlerAdapter(不需要程序员)
作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler
处理器Handler(需要程序员开发)
注意:编写Handler时,按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler
视图解析器ViewResolver(不需要程序员开发)
作用:进行视图解析,根据逻辑视图名解析成真正的的视图(view)
视图View(需要程序员开发jsp)
注意:View是一个接口,实现类支持不同的View类型(jsp,freemarker,pdf…)
ps:不需要程序员开发的,需要程序员自己做一下配置即可。
答:①作用:建立请求url和处理方法之间的对应关系。
②作用位置:类,请求url的第一级访问目录,如果不写相当于根目录,需要以/开头;方法,请求url的第二级访问目录,可以不以/开头。
③属性:value/path,绑定路径,支持多个路径,一般只配置一个路径;method 指定访问方式,可配置多个允许的访问方式,默认任何方法都支持,例如POST、GET等。
答:①基本数据类型和String,要求请求参数的参数名必须和控制器中方法的形参名一致,例如请求参数为name,控制器方法的形参也必须为name。
②可以是Java对象,要求请求属性必须和对应的Java类中的成员变量名一致,例如input标签的name属性值为id,类中也必须有id这一个成员变量。也可以是Java对象中的List或Map集合。
默认支持类型
1.@RequestParam 接受简单的参数类型
主要用来接收GET请求拼接在URL后的参数,或者是POST传递,且Content-type为x-www-form-urlencoded方式。
因为不管是GET方式还是用x-www-form-urlencoded方式传递,参数都是以键值对方式拼接的,然后经过URLencoded编码,传递给服务端。
@RequestParam只能接收简单参数类型,复杂的参数类型要用@RequestBody来接收,或者不加注解来接收。
2.@RequestBody 接受json数据
使用@RequestBody该注解,前端请求只能为POST,因为该注解是从请求体中获得对象的。且请求头中的Content-type一般为application/json方式。所以使用该注解能够接收JSON格式的数据,并且能把接收到的JSON数据绑定到JAVA对象中。复杂对象包括List,实体类,Map对象等。
在用该注解的时候有两个注意事项:
1. 一个方法中只能有一个@RequestBody注解,但是@RequestBody注解可以和@RequestParam注解一起使用,而且@RequestParam注解一个方法中可以有多个。
2. @RequestBody注解的参数类型可以是复杂对象类。
3.不加注解接收 和和requestBody类似 只可以接受非json的数据
不加注解接收参数,参数类型可以为简单类型,也可以为复杂类型(JAVA对象等,前端传递的参数会和类中的属性名对应并且绑定)。也就是两种类型都可接收。而且GET请求和POST请求也都能接收到参数。但是POST请求时,和@RequestParam注解一样,Content-type只能为x-www-form-urlencoded。不加注解可以接收复杂对象,但是不能接收Map类型的对象。
关于map接受参数总结 无注解 @RequestParam注解 @RequestBody注解
SpringMVC处理请求用Map类型接收参数时,如果参数无注解
,则会传入BindingAwareModelMap类型,等价于Model、ModelMap参数;
参数添加@RequestParam注解时
,会将参数包装称LinkedHashMap对象,参数的key为Map的key,参数值为Map的key,支持Get、Post方法(应该支持Put、Delete,没有测,俩方法与Post类似);
添加@RequestBody注解时
,接收Json类型数据,也会包装成LinkedHashMap对象,该注解不支持Get请求,Get请求没有请求体不能传Json。
答:在web.xml中配置一个过滤器,配置一个filter标签,使用org.springframework.web.filter包下的CharacterEncodingFilter类实现,将中的设置为encoding,对应的设置为UTF-8即可。然后配置对应的fiter-mapping标签,fiter-name和之前的一样,设置为/*,表示对所有视图都进行编码过滤。
答:一共有9个,包括①HttpServletRequest,指客户端的请求。
②HttpServletResponse,指服务器端的响应。
③HttpSession,Java平台对session机制的实现规范。
④Principal,此接口表示主体的抽象概念,它可以用来表示任何实体,例如,个人、公司或登录id。⑤Locale,用于国际化操作的类。
⑥InputStream,字节输入流。
⑦OutputStream,字节输出流。
⑧Reader,字符输入流。
⑨Writer,字符输出流。
答:①@RequestParam:作用是将请求参数和控制器中方法形参绑定(请求参数名和形参名不再要求相同)。属性包括:name/value,当和请求参数名一致可省略;required指定请求参数是否必填项;defaultValue是未提供请求参数时的默认值。
②@RequestBody:作用是用于获取请求体的内容,直接使用得到的是key=value形式的字符串,把获取的json数据转换成pojo对象(get方式不可用)。
③@RequestBody:作用是将控制器中方法返回的对象通过适当的转换器转换为指定的格式之后进行响应,通常用来返回JSON数据或者是XML。
④@PathVariable:作用是绑定url中的占位符,例如请求url中/delete/{id},{id}就是url占位符。url支持占位符是spring3.0后加入的,是springmvc支持rest风格url的一个重要标志。属性包括name/value 指定url中占位符名称;required指定是否必须提供占位符。
⑤@RequestHeader:作用是获取指定请求头的值。属性:value代表请求头的名称。
⑥@CookieValue:作用是用于把指定 cookie 名称的值传入控制器方法参数。属性包括value:指定 cookie 的名称。required:是否必须有此 cookie。
⑦@ModelAttribute:是 SpringMVC4.3 版本以后加入的,它可以修饰方法和参数,出现在方法上表示当前方法会在控制器的方法执行之前先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。出现在参数上,获取指定的数据给参数赋值。属性value用于获取数据的 key。key 可以是 POJO 的属性名称,也可以是 map 结构的 key。
答:①字符串,控制器中的方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址,例如返回"success"时可解析为success.jsp页面。
②返回值为空,默认访问视图解析器前缀+requestmapping路径+视图解析器后缀的视图。
③ModelandView,可以通过setViewName()方法设置视图名实现指定视图的跳转。
答:①前提是控制器方法返回值类型必须是String类型。
②转发到页面return"forward:+绝对地址"。转发到控制器其他方法:return的是"forward:+类上requestmapping的地址+方法上requestmapping的地址"。
③重定向到页面:return的是"redirect:+绝对地址",注意不能重定向访问WEB-INF下的资源。重定向到控制器其他方法:return的是"redirect:+类上requestmapping的地址+方法上requestmapping的地址"。重定向到外部链接:return的是"redirect:+链接地址(http://www.qq.com)"。
④转发和重定向的区别是转发只是一次请求,重定向是两次请求;转发地址栏不变,重定向地址栏将改变;转发只能到内部资源,重定向可以到内部或外部资源;转发可以到WEB-INF下资源,重定向不可以。
答:①浏览器端要求:表单提交方式为post(get有文件大小限制)。提供文件上传框对应的标签:。表单的entype属性必须为multipart/form-data。②服务器端要求:使用**request.getInputStream()**获取数据。springmvc底层封装了commons-fileupload文件上传工具包。
答:Dao层发生的异常会向上抛出到Service层、Service层的异常会向上抛出到Controller层,Controller层的异常会继续向上抛出到SpringMVC的前端控制器,由前端控制器将异常交给SpringMVC的异常处理器进行处理。如果是自定义的异常处理器,需要实现HandlerExceptionResolver接口,并使用@Component注解配置或在对应的springconfig配置文件中注册。
答:①Spring MVC 的拦截器用于对处理器进行预处理和后处理,用户可以自己定义一些拦截器来实现特定的功能。拦截器链就是将拦截器按一定的顺序联结成一条链,在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
②它和过滤器的区别是:过滤器是 servlet 规范中的一部分,任何 java web 工程都可以使用,拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用;过滤器在 url-pattern 中配置了/*之后,可以对所有要访问的资源拦截,拦截器它是只会拦截访问的控制器方法,如果访问的是 jsp、html、css、image 或者 js 是不会进行拦截的;它也是 AOP 思想的具体应用,如果要想自定义拦截器, 要求必须实现HandlerInterceptor 接口。
答:①preHandle:按拦截器定义顺序调用,只要配置了都会调用。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回 true,如果不需要再调用其他的组件去处理请求,则返回 false。
②postHandle:按拦截器定义逆序调用,在拦截器链内所有拦截器返回成功时调用。在业务处理器处理完请求后,但是 DispatcherServlet 向客户端返回响应前被调用, 在该方法中对用户请求 request 进行处理。③afterCompletion:按拦截器定义逆序调用,只有 preHandle 返回 true时才调用。在 DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。
答:①Mybatis支持延迟加载。实际开发过程中很多时候我们并不需要总是在加载某些信息时就一定要加载其关联信息,例如在加载用户信息时不是用户关联的账户信息不是必需的,此时就可以采用延迟加载。
②延迟加载就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。
③好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
④坏处:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
答:Mybatis的缓存分为:
①一级缓存:指的是Mybatis中SqlSession对象的缓存,当我们执行完查询结果后,查询的结果会同时存入到SqlSession为我们提供的一块区域中,该区域的结构是一个Map,当我们再次查询同样数据时,Mybatis会先去SqlSession中查询是否有,有的话直接拿出来用。当SqlSession对象消失时,Mybatis的一级缓存也就消失了。一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。 一级缓存的生命周期与SqlSession相同,如果你对SqlSession不熟悉,你可以把它类比为JDBC编程中的Connection,即数据库的一次会话。
②二级缓存:二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
但是一级缓存是基于sqlSession的,而二级缓存是基于mapper文件的namespace的,也就是说多个sqlSession可以共享一个mapper中的二级缓存区域,并且如果两个mapper的namespace 相同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中。
答:①在springconfig配置文件中,将setting标签的cacheEnabled值设置为true(默认值为true,所以这一步可省略)。
②配置相关的 Mapper 映射文件,使用标签表示当前这个 mapper 映射将使用二级缓存。
③配置 statement 上面的 useCache 属性,设置 useCache=”true”代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false。针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。
④如果是使用注解的方式,可以省略第二步,只需要在dao层接口上加上注解@CacheNamespace(blocking=true)。
答:
1、Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。
2、MyBatis可以使用XML或注解来配置和映射原生信息,将POJO映射成数据库中的记录,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
3、通过xml文件或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。(从执行sql到返回result的过程)。
答:
1、基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
2、与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;
3、很好的与各种数据库兼容(因为 MyBatis 使用 JDBC 来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。
4、能够与Spring很好的集成;
5、提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
答:
1、SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。
2、SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
答:
1、MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。
2、对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。
答:
1、Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句。
2、Mybatis直接编写原生态sql,可以严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需求变化要求迅速输出成果。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件,则需要自定义多套sql映射文件,工作量大。
3、Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用hibernate开发可以节省很多代码,提高效率。
答:
#{}是预编译处理,${}是字符串替换。
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理时 , 就 是 把 {}时,就是把时,就是把{}替换成变量的值。
使用#{}可以有效的防止SQL注入,提高系统安全性
答:
第1种: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。
第2种: 通过来映射字段名和实体类属性名的一一对应的关系。
答:
第1种:在Java代码中添加sql通配符。
第2种:在sql语句中拼接通配符,会引起sql注入
A:% 表示零个或多个字符的任意字符串:
1. LIKE'Mi%' 将搜索以字母 Mi开头的所有字符串(如 Michael)。
2. LIKE'%er' 将搜索以字母 er 结尾的所有字符串(如 Worker、Reader)。
3. LIKE'%en%' 将搜索在任何位置包含字母 en 的所有字符串(如 When、Green)。
答:
Dao接口就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名就是映射文件中MappedStatement的id值,接口方法内的参数就是传递给sql的参数。
接口里的方法是不能重载的,因为是全限名+方法名的保存和寻找策略。
Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行接口方法所对应的MappedStatement所代表的sql,然后将sql执行结果返回。
MappedStatement解释 : MappedStatement维护了一条
<select id="selectAuthorLinkedHashMap" resultType="java.util.LinkedHashMap">
select id, username from author where id = #{value}
select>
答:
Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。 分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
答:
第一种是使用标签,逐一定义列名和对象属性名之间的映射关系。第二 种是使用sql列的别名功能,将列别名书写为对象属性名,比如T_NAME AS NAME,对象属性名一般是name,小写,但是列名不区分大小写,Mybatis会忽略列名大小 写,智能找到与之对应对象属性名,你甚至可以写成T_NAME AS NaMe,Mybatis一样可以正常工作。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
方式一、 标签使用 resultType 参数,传递 Java 类,sql 中 select 的字段名保持与 Java 类属性名称一致
方式二、使用 标签,定义数据库列名和对象属性名之间的映射关系
方式三、使用注解 select 的字段名保持与接口方法返回的 Java 类或集合的元素类的属性名称一致
答:
使用foreach标签
foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach标签的属性主要有item,index,collection,open,separator,close。
1.item 表示集合中每一个元素进行迭代时的别名,随便起的变量名;
2.index 指定一个名字,用于表示在迭代过程中,每次迭代到的位置,不常用;
3.open 表示该语句以什么开始,常用“(”;
4.separator 表示在每次进行迭代之间以什么符号作为分隔符,常用“,”;
5.close 表示以什么结束,常用“)”。
在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是
在不同情况下,该属性的值是不一样的,主要有一下3种情况:
如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Map的,map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key
具体用法如下:
list
)dao
int insertMultipleActivity (List<ClueActivityRelation> ClueActivityRelations);
mapper.xml insert插入多条数据
separator=“,” 是对
标签中的内容遍历一次进行一次 分割 ,
parameterType="com.bjpowernode.crm.workbench.domain.ClueActivityRelation">
insert into tbl_clue_activity_relation (id, clue_id, activity_id)
values
<foreach collection="list" item="obj" separator=",">
(#{obj.id}, #{obj.clueId}, #{obj.activityId} )
foreach>
insert>
sql语句 insert插入多条数据
INSERT INTO tbl_clue_activity_relation (id, clue_id, activity_id)
VALUES
(1245,21,21),
(2455,2,2)
string数组
)注意:where a.id in 要写a.id,要不然不知到id是谁的id
mapper.xml
<select id="selectActivityByActivityArrayId" parameterType="string" resultMap="BaseResultMap">
select a.id, a.name, a.start_date, a.end_date, u.name as owner
from tbl_activity a
join tbl_user u on a.owner = u.id
where in a.id= //效果就是(xx,xx,xx)//
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
foreach>
select>
没遍历一个id就加一个
,
的分隔符 遍历完后就 在前面后面分别加上( )
答:
insert方法总是返回一个int值 ,这个值代表的是插入的行数。
如果采用自增长策略,自动生成的键 值在insert方法执行完后可以被设置到传入的参数对象中的id属性中 。
后台数据插入时获得主键值也分为两种情况:支持主键自增数据库和不支持主键自增。
一、对于支持自动生成主键的数据库,如Mysql、sqlServer,可以通过 Mybatis元素useGeneratedKeys返回当前插入数据主键值到输入类中。
具体应用
<insert id="insertTest" useGeneratedKeys="true" keyProperty="id"
parameterType="com.kq.domain.IdentityTest">
insert into identity_test(name)
values(#{name,jdbcType=VARCHAR})
示例 :
答:
1、第一种:
DAO 层的函数
public UserselectUser(String name,String area);
对应的 xml,#{0}代表接收的是 dao 层中的第一个参数,#{1}代表 dao 层中第二
参数,更多参数一致往后加即可。
<select id="selectUser"resultMap="BaseResultMap"> select * fromuser_user_t whereuser_name = #{0} anduser_area=#{1} select>
2、第二种: 使用 @param 注解
public interface usermapper { user selectuser(@param(“username”) string username,@param(“hashedpassword”) string hashedpassword); }
然后,就可以在 xml 像下面这样使用(推荐封装为一个 map,作为单个参数传递给mapper
<select id=”selectuser” resulttype=”user”> select id, username, hashedpassword from some_table where username = #{username} and hashedpassword = #{hashedpassword} select>
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动态sq可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根 据表达式的值完成逻辑判断并动态拼接sql的功能。
Mybatis提供了9种动态sql标签 :trim | where | set | foreach | if | choose| when | otherwise | bind。
属性介绍:
id :唯一的标识符.
parameterType:传给此语句的参数的全路径名或别名 例:com.test.poso.User 或 user
resultType :语句返回值类型或别名。注意,如果是集合,那么这里填写的是集合的泛型,而不是集合本身(resultType 与 resultMap 不能并用)
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="Object">
select * from student where id=#{id}
select>
属性介绍:
id :唯一的标识符
parameterType:传给此语句的参数的全路径名或别名 例:com.test.poso.User
<insert id="insert" parameterType="Object">
insert into student
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="name != null"> NAME, if>
trim>
<trim prefix="values(" suffix=")" suffixOverrides=",">
<if test="name != null"> #{name}, if>
trim>
insert>
属性同 insert
<delete id="deleteByPrimaryKey" parameterType="Object">
delete from student where id=#{id}
delete>
基本作用:
建立 SQL 查询结果字段与实体属性的映射关系信息
查询的结果集转换为 java 对象,方便进一步操作。
将结果集中的列与 java 对象中的属性对应起来并将值填充进去
!注意:与 java 对象对应的列不是数据库中表的列名,而是查询后结果集的列名
<resultMap id="BaseResultMap" type="com.online.charge.platform.student.model.Student">
<id property="id" column="id" />
<result column="NAME" property="name" />
<result column="HOBBY" property="hobby" />
<result column="MAJOR" property="major" />
<result column="BIRTHDAY" property="birthday" />
<result column="AGE" property="age" />
resultMap>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="Object">
select id,name,hobby,major,birthday,age from student where id=#{id}
select>
标签说明:
主标签:
id:该 resultMap 的标志
type:返回值的类名,此例中返回 Studnet 类
子标签:
id:用于设置主键字段与领域模型属性的映射关系,此处主键为 ID,对应 id。
result:用于设置普通字段与领域模型属性的映射关系
if 标签通常用于 WHERE 语句、UPDATE 语句、INSERT 语句中,通过判断参数值来决定是否使用某个查询条件、判断是否更新某一个字段、判断是否插入某个字段的值。
<if test="name != null and name != ''">
and NAME = #{name}
if>
foreach 标签主要用于构建 in 条件,可在 sql 中对集合进行迭代。也常用到批量删除、添加等操作中。
insert into t_user values (12,13,14 ) 用于构建values
<select id="selectIn" resultMap="BaseResultMap">
select name,hobby from student where id in
<foreach item="item" index="index" collection="list" open="(" separator="," close=")">
#{item}
foreach>
select>
属性介绍:
collection:collection 属性的值有三个分别是 list、array、map 三种,分别对应的参数类型为:List、数组、map 集合。
item :表示在迭代过程中每一个元素的别名
index :表示在迭代过程中每次迭代到的位置(下标)
open :前缀
close :后缀
separator :分隔符,表示迭代时每个元素之间以什么分隔
choose 标签
有时候我们并不想应用所有的条件,而只是想从多个选项中选择一个。MyBatis 提供了 choose 元素,按顺序判断 when 中的条件出否成立,如果有一个成立,则 choose 结束。当 choose 中所有 when
的条件都不满则时,则执行 otherwise 中的 sql。类似于 Java 的 switch 语句,choose 为 switch,when 为 case,otherwise 则为 default。
if 是与(and)的关系,而 choose 是或(or)的关系。
<select id="getStudentListChoose" parameterType="Student" resultMap="BaseResultMap">
SELECT * from STUDENT WHERE 1=1
<where>
<choose>
<when test="Name!=null and student!='' ">
AND name LIKE CONCAT(CONCAT('%', #{student}),'%')
when>
<when test="hobby!= null and hobby!= '' ">
AND hobby = #{hobby}
when>
<otherwise>
AND AGE = 15
otherwise>
choose>
where>
select>
当 if 标签较多时,这样的组合可能会导致错误。 如下:
<select id="getStudentListWhere" parameterType="Object" resultMap="BaseResultMap">
SELECT * from STUDENT WHERE
<if test="name!=null and name!='' ">
NAME LIKE CONCAT(CONCAT('%', #{name}),'%')
if>
<if test="hobby!= null and hobby!= '' ">
AND hobby = #{hobby}
if>
select>
当 name 值为 null 时,查询语句会出现 “WHERE AND” 的情况,解决该情况除了将"WHERE"改为“WHERE 1=1”之外,还可以利用 where
标签。这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会剔除掉。
<select id="getStudentListWhere" parameterType="Object" resultMap="BaseResultMap">
SELECT * from STUDENT
<where>
<if test="name!=null and name!='' ">
NAME LIKE CONCAT(CONCAT('%', #{name}),'%')
if>
<if test="hobby!= null and hobby!= '' ">
AND hobby = #{hobby}
if>
where>
select>
没有使用 if 标签时,如果有一个参数为 null,都会导致错误。当在 update 语句中使用 if 标签时,如果最后的 if 没有执行,则或导致逗号多余错误。使用 set 标签可以将动态的配置 set
关键字,和剔除追加到条件末尾的任何不相关的逗号。
<update id="updateStudent" parameterType="Object">
UPDATE STUDENT
SET NAME = #{name},
MAJOR = #{major},
HOBBY = #{hobby}
WHERE ID = #{id};
update>
<update id="updateStudent" parameterType="Object">
UPDATE STUDENT SET
<if test="name!=null and name!='' ">
NAME = #{name},
if>
<if test="hobby!=null and hobby!='' ">
MAJOR = #{major},
if>
<if test="hobby!=null and hobby!='' ">
HOBBY = #{hobby}
if>
WHERE ID = #{id};
update>
使用 set+if 标签修改后,如果某项为 null 则不进行更新,而是保持数据库原值。
<update id="updateStudent" parameterType="Object">
UPDATE STUDENT
<set>
<if test="name!=null and name!='' ">
NAME = #{name},
if>
<if test="hobby!=null and hobby!='' ">
MAJOR = #{major},
if>
<if test="hobby!=null and hobby!='' ">
HOBBY = #{hobby}
if>
set>
WHERE ID = #{id};
update>
trim标记是一个格式化的标记,主要用于拼接sql的条件语句(前缀或后缀的添加或忽略),可以完成set或者是where标记的功能。
trim属性主要有以下四个
prefix:在trim标签内sql语句加上前缀
suffix:在trim标签内sql语句加上后缀
prefixOverrides:指定去除多余的前缀内容,如:prefixOverrides=“AND | OR”,去除trim标签内sql语句多余的前缀"and"或者"or"。
suffixOverrides:指定去除多余的后缀内容。
<update id="updateByPrimaryKey" parameterType="Object">
update student set
<trim suffixOverrides=",">
<if test="name != null">
NAME=#{name},
if>
<if test="hobby != null">
HOBBY=#{hobby},
if>
trim>
where id=#{id}
update>
如果name和hobby的值都不为空的话,会执行如下语句
update student set NAME=‘XX’,HOBBY=‘XX’ /,/ where id=‘XX’
会忽略最后一个“,” ;
在select中
<select id="selectByNameOrHobby" resultMap="BaseResultMap">
select * from student
<trim prefix="WHERE" prefixOverrides="AND | OR">
<if test="name != null and name.length()>0">
AND name=#{name}
if>
<if test="hobby != null and hobby.length()>0">
AND hobby=#{hobby}
if>
trim>
select>
如果name和hobby的值都不为空的话,会执行如下语句
select * from student WHERE /*and*/ name = ‘xx’ and hobby= ‘xx’
会为片段添加 “WHERE” 前缀,并忽略第一个 “and” ;
当然,避免出现“WHERE AND”还有其他方法,如下
select * from student where 1=1
<trim suffixOverrides=",">
<if test="name != null and name != ''">
and NAME = #{name}
if>
<if test="hobby != null and hobby != ''">
and HOBBY = #{hobby}
if>
trim>
用在insert中
<insert id="insert" parameterType="Object">
insert into student
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="name != null">
NAME,
if>
<if test="hobby != null ">
HOBBY,
if>
trim>
<trim prefix="values(" suffix=")" suffixOverrides=",">
<if test="name != null ">
#{name},
if>
<if test="hobby != null ">
#{hobby},
if>
trim>
insert>
当多种类型的查询语句的查询字段或者查询条件相同时,可以将其定义为常量,方便调用。为求 结构清晰也可将 sql 语句分解。
<sql id="Base_Column_List">
ID,MAJOR,BIRTHDAY,AGE,NAME,HOBBY
sql>
<sql id="Example_Where_Clause">
where 1=1
<trim suffixOverrides=",">
<if test="id != null and id !=''">
and id = #{id}
if>
<if test="major != null and major != ''">
and MAJOR = #{major}
if>
<if test="birthday != null ">
and BIRTHDAY = #{birthday}
if>
<if test="age != null ">
and AGE = #{age}
if>
<if test="name != null and name != ''">
and NAME = #{name}
if>
<if test="hobby != null and hobby != ''">
and HOBBY = #{hobby}
if>
<if test="sorting != null">
order by #{sorting}
if>
<if test="sort!= null and sort != ''">
order by ${sort} ${order}
if>
trim>
sql>
用于引用定义的常量
<insert id="insert" parameterType="Object">
insert into student
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="name != null">
NAME,
if>
<if test="hobby != null ">
HOBBY,
if>
trim>
<trim prefix="values(" suffix=")" suffixOverrides=",">
<if test="name != null ">
#{name},
if>
<if test="hobby != null ">
#{hobby},
if>
trim>
insert>
<insert id="insert" parameterType="Object">
insert into student
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="name != null">
NAME,
if>
<if test="hobby != null ">
HOBBY,
if>
trim>
<trim prefix="values(" suffix=")" suffixOverrides=",">
<if test="name != null ">
#{name},
if>
<if test="hobby != null ">
#{hobby},
if>
trim>
insert>
<delete id="deleteByEntity" parameterType="java.util.Map">
DELETE FROM student
<include refid="Example_Where_Clause" />
delete>
答:
注:这道题出自京东面试官。 还有很多其他的标签 , 加上动态sql的9个标签 。
trim|where|set|foreach|if|choose|when|otherwise|bind 等,其中为sql片段标签,通过标签引sql片段,为不支持自增的主键生成策略标签。
答:
Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。
答:
连接了两张表
<select id="selectActivityByActivityArrayId" parameterType="string" resultMap="BaseResultMap">
select a.id, a.name, a.start_date, a.end_date, u.name as owner
from tbl_activity a
join tbl_user u on a.owner = u.id
where a.id= in //效果就是(xx,xx,xx)//
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
foreach>
select>
连接了三张表 效率低
<select id="selectActivityForDetailByClueId" parameterType="string" resultMap="BaseResultMap">
select a.id, a.name, a.start_date, a.end_date, u.name as owner
from tbl_activity a
join tbl_user u on a.owner = u.id
join tbl_clue_activity_relation car on a.id = car.activity_id
where car.clue_id = #{clueId}
select>
答:
有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次, 通过在resultMap里面配置association节点配置一对一的类就可以完成;嵌套查询是先查一个表,根据这个表里面的结果的 外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。
连接查询,子查询 能使用延迟加载
答:
有联合查询和嵌套查询。联合查询是几个表联合查询,只查询一次,通过在resultMap 里面的collection节点配置一对多的类就可以完成;嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是通过配置collection,但另外一个表的查询通过select节点配置。
答:
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,支持延迟加载的原理都是一样的。
比如在一对一查询中使用子查询映射,就会在resultMap中先查询到教室名,将教室名赋值到教室类中,然后遇到collection,就会先对collection中的内容进行查询将其赋值到对应的属性中,于是将所有的内容都查询出来了
答:
接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定, 我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。
接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上@Select、@Update等注解,里面包含Sql语句来绑定;另外一种就是通过xml里面写SQL来绑定,在这种情况下,要指定xml 映射文件里面的namespace必须为接口的全路径名。当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多。
接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定, 我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。
student user = (student) session.selectOne(“test.studentMapper.selectUserByID”, 1);
接口绑定有两种实现方式:
一种是通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;
//这种方式不用写mapper.xml
@Select("select * from `tb_Teacher` where id = #{id}")
public teacher selectTeacherByID(int id);
另外一种就是通过xml里面写SQL来绑定, 在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名。
<select id="selectUserByID" parameterType="int" resultType="test.student">
select id,name,age,stuCountry stu_country from `tb_Student` where id = #{id}
select>
两种方式选择:
当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多。
答:
1、Mapper接口方法名和mapper.xml中定义的每个sql的id相同;
2、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同;
3、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同;
4、Mapper.xml文件中的namespace即是mapper接口的类路径。
答: .
第一种:接口实现类继承SqlSessionDaoSupport:使用此种方法需要编写mapper接口,mapper接口实现类、mapper.xml文件。
1、在sqlMapConfig xml中配置mapper xml的位置
<mappers>
<mapper resource="mapper.xml 文件的地址" />
<mapper resource="mapper.xml 文件的地址" />
mappers>
2、定义mapper接口
3、实现类集成SqlSessionDaoSupport mapper方法中可以this getSqlSession()进行数据增删改查。
4、spring配置
<bean id=" " class="mapper 接口的实现">
<property name="sqlSessionFactory"
ref="sqlSessionFactory">property>
bean>
第二种:使用org.mybatis.spring.mapper.MapperFactoryBean:
1、在 sqlMapConfig.xml中配置mapper.xml的位置,如果mapper.xml 和mappre接口的名称相同且在同一个目录,这里可以不用配置
<mappers>
<mapper resource="mapper.xml 文件的地址" />
<mapper resource="mapper.xml 文件的地址" />
</mappers>
2、定义mapper接口:
1、mapper.xml中的namespace为mapper接口的地址
2、mapper接口中的方法名和mapper.xml中的定义的statement的id保持一致
3、Spring中定义
<bean id="" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="mapper 接口地址" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
第三种:使用mapper扫描器:
1、mapper.xml文件编写:
mapper.xml中的namespace为mapper接口的地址;
mapper接口中的方法名和mapper.xml中的定义的statement的id保持一致;
如果将mapper.xml和mapper接口的名称保持一致则不用在sqlMapConfig.xml中进行配置。
2、定义mapper接口:
注意mapper.xml的文件名和mapper的接口名称保持一致,且放在同一个目录
3、配置mapper扫描器:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="mapper 接口包地址"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
4、使用扫描器后从spring 容器中获取mapper的实现对象。
答:
Mybatis仅可以编写针对 ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
编写插件:实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。
答:
1、 创建SqlSessionFactory
2、 通过SqlSessionFactory创建SqlSession
3、 通过sqlsession执行数据库操作
4、 调用session.commit()提交事务
5、 调用session.close()关闭会话
答:
在学习MyBatis程序之前,需要了解一下MyBatis工作原理,以便于理解程序。MyBatis的工作原理如下图:
读取MyBatis配置文件:mybatis-config.xml为MyBatis的全局配置文件,配置了MyBatis的运行环境等信息,例如数据库连接信息。
加载映射文件。映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在MyBatis配置文件mybatis-config.xml中加载。mybatis-config.xml文件可以加载多个映射文件,每个文件对应数据库中的一张表。
构造会话工厂:通过MyBatis的环境等配置信息构建会话工厂SqlSessionFactory。
创建会话对象:由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有方法。
Executor执行器:MyBatis底层定义了一个Executor 接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。
MappedStatement 对象:在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。
输入参数映射:输入参数类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输入参数映射过程类似于 JDBC对preparedStatement对象设置参数的过程。
输出结果映射:输出结果类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输出结果映射过程类似于 JDBC对结果集的解析过程。
32、MyBatis的功能架构是怎样的?
答:
我们把Mybatis的功能架构分为三层:
API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
答:
这张图从上往下看。MyBatis的初始化,会从mybatis-config.xml配置文件,解析构造成Configuration这个类,就是图中的红框。
加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。
SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。
结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,并将最终结果返回。
答:
DBMS:数据库管理系统(database management system)是一种操纵和管理数据库的大型软件,用于建立、使用和维护数zd据库,简称dbms。它对数据库进行统一的管理和控制,以保证数据库的安全性和完整性。
用户通过dbms访问数据库中的数据,数据库管理员也通过dbms进行数据库的维护工作。它可使多个应用程序和用户用不同的方法在同时版或不同时刻去建立,修改和询问数据库。
DBMS提供数据定义语言DDL(Data Definition Language)与数据操作语言DML(DataManipulation Language),供用户定义数据库的模式结构与权限约束,实现对数据的追加权、删除等操作。
答:
定义:
SQL预编译指的是数据库驱动在发送SQL语句和参数给DBMS之前对SQL语句进行编译,这样DBMS执行SQL时,就不需要重新编译。
为什么需要预编译
JDBC中使用对象 PreparedStatement 来抽象预编译语句,使用预编译。预编译阶段可以优化SQL的执行。预编译之后的SQL多数情况下可以直接执行,DBMS不需要再次编译,越复杂的SQL,编译的复杂度将越大,预编译阶段可以合并多次操作为一个操作。同时预编译语句对象可以重复利用。
把一个SQL预编译后产生的 PreparedStatement 对象缓存下来,下次对于同一个SQL,可以直接使用这个缓存的PreparedState对象。Mybatis默认情况下,将对所有的SQL进行预编译。还有一个重要的原因,复制SQL注入
答:
Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。
SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map
BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。
作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。
答:
1.在Mybatis配置文件中,在设置(settings)可以指定默认的ExecutorType执行器类型,也可以手动DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数,如SqlSession openSession(ExecutorType execType)。
2.配置默认的执行器。SIMPLE就是普通的执行器;REUSE执行器会重用预处理语句(preparedstatements); BATCH 执行器将重用语句并执行批量更新。
答:
1.Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。
2.它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
3.当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。
首先我们要知道搭建一个SSM项目都需要做哪些
首先创建一个项目名字为ssm(这个是我的项目名字,大家可以随意),然后添加pom依赖
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>ssmartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
<SPRING.VERSION>5.2.6.RELEASESPRING.VERSION>
<ASPECTJWEAVER>1.9.5ASPECTJWEAVER>
properties>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>${SPRING.VERSION}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>${SPRING.VERSION}version>
<scope>compilescope>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>${ASPECTJWEAVER}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>${SPRING.VERSION}version>
<scope>compilescope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-ormartifactId>
<version>${SPRING.VERSION}version>
<scope>compilescope>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.6version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.5version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.21version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.30version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-coreartifactId>
<version>2.10.3version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.10.3version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-annotationsartifactId>
<version>2.10.3version>
dependency>
dependencies>
project>
依赖的库文件大部分都有注释,就不多说了
然后需要添加web支持
在项目名上右键,点击Add Framework Support,然后添加web支持,这时我们的web.xml是不是就出来了。
然后配置我们的web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-core.xmlparam-value>
context-param>
<servlet>
<servlet-name>dispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-mvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>dispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>characterEncodingfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
<init-param>
<param-name>forceEncodingparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>characterEncodingfilter-name>
<servlet-name>dispatcherServletservlet-name>
filter-mapping>
<filter>
<filter-name>hiddenHttpMethodfilter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilterfilter-class>
filter>
<filter-mapping>
<filter-name>hiddenHttpMethodfilter-name>
<servlet-name>dispatcherServletservlet-name>
filter-mapping>
web-app>
然后配置db.properties
mysql.username=root
mysql.password=root1234
mysql.url=jdbc:mysql://192.168.3.100:3306/mall?useSSL=false
mysql.driverClassName=com.mysql.jdbc.Driver
然后配置spring-mvc.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="cn.study" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
context:component-scan>
<mvc:annotation-driven>mvc:annotation-driven>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<property name="prefix" value="/WEB-INF/views">property>
bean>
<mvc:default-servlet-handler>mvc:default-servlet-handler>
beans>
然后配置spring-core.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://mybatis.org/schema/mybatis-spring
http://mybatis.org/schema/mybatis-spring.xsd">
<context:component-scan base-package="cn.study">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
context:component-scan>
<context:property-placeholder location="classpath:db.properties">context:property-placeholder>
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="username" value="${mysql.username}">property>
<property name="password" value="${mysql.password}">property>
<property name="url" value="${mysql.url}">property>
<property name="driverClassName" value="${mysql.driverClassName}">property>
bean>
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<tx:annotation-driven transaction-manager="transactionManager">tx:annotation-driven>
<aop:config>
<aop:pointcut id="transactionCut" expression="execution(* cn.study.service.impl.*.*(..))"/>
aop:config>
<tx:advice id="advice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*"/>
<tx:method name="update*"/>
<tx:method name="delete*"/>
<tx:method name="get*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="query*" read-only="true"/>
tx:attributes>
tx:advice>
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
<property name="dataSource" ref="dataSource">property>
<property name="configLocation" value="classpath:mybatis-config.xml">property>
<property name="mapperLocations" value="classpath:cn/study/mapper/*.xml">property>
bean>
<mybatis:scan base-package="cn.study.mapper">mybatis:scan>
beans>
然后配置mybatis-config.xml
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
<typeAliases>
<package name="cn.study.pojo"/>
typeAliases>
configuration>
然后配置logback.xml ```xml %d{HH:mm:ss.SSS} [%thread] %-5level %logger{100} - %msg%n org.apache.ibatis.transaction ```
接下来就可以构建一下我们的三层Controller、service、dao
Controller:
package cn.study.controller;
import cn.study.pojo.User;
import cn.study.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
@RequestMapping("/app")
public class UserController {
@Autowired
UserService userService;
@RequestMapping("/test")
@ResponseBody
public List<User> test()
{
List<User> user = userService.queryAllUser();
return user;
}
}
service:
package cn.study.service;
import cn.study.pojo.User;
import java.util.List;
public interface UserService {
List<User> queryAllUser();
}
package cn.study.service.impl;
import cn.study.mapper.UserMapper;
import cn.study.pojo.User;
import cn.study.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
@Override
public List<User> queryAllUser() {
return userMapper.selectEmp();
}
}
dao:
package cn.study.mapper;
import cn.study.pojo.User;
import java.util.List;
public interface UserMapper {
List<User> selectEmp();
}
UserMapper.xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.study.mapper.UserMapper">
<select id="selectEmp" resultType="cn.study.pojo.User">
SELECT * FROM USER
select>
mapper>
pojo实体类
package cn.study.pojo;
public class User {
private Integer id;
private String username;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
由于我使用的是IDEA,所以需要将依赖的jar包添加到打包的项目中,如果使用的是eclips的略过
MyBatis想必大家都比较熟悉了吧,它底层封装了JDBC,用来与数据库进行连接。
这里我主要想讲一下MyBatis和MyBatis-plus,主要是想表达一下,据我的了解,这两个在公司中都有所使用,我只简略的学过MyBatis-plus。最直观的就是通用Mapper,为你省略了dao层的添加。当然,一些复杂的逻辑仍然需要你自定义Mapper。但我感觉plus对数据的操作的api相对以前更加复杂。整体而已,推荐大家深度学习MyBatis,在源码级别的学习中更加直白,简易。对于 MyBatis-plus大家只需要熟练使用常用方法。当然这只是我的一点意见。
MyBatis是一款半自动化的ORM(对象关系映射)的软件。内部封装了JDBC,使得程序员在开发过程中省略了创建连接,加载驱动等操作,开发人员只需要关注sql语句本身。 MyBatis通过配置文件或者注解即可将数据库数据与pojo实体类联系起来
解释一下什么是对象关系映射
是一种为了解决关系型数据库数据与简单java对象的映射关系的技术
首先,他俩都是为了动态传递参数而存在的,是MyBatis实现动态sql的基础
#{}是预编译处理,将这部分先用?代替,调用PreparedStatement的set方法来赋值
${}是字符串替换,直接将这部分用括号内的实际内容代替
我们在实际开发过程中,都写过MyBatis的配置文件,对于 MyBatis的底层具体实现,就是从这里开始的。
1)读取MyBatis配置文件:mybatis-config.xml为MyBatis的全局配置文件,包含了MyBatis行为的设置和属性信息,例如数据库连接信息和映射文件
2)加载mapper.xml(SQL映射文件):该文件配置了操作数据库的SQL语句
3)构建会话工厂:创建会话工厂SqlSessionFactory
4)创建会话对象:由会话工厂生产sqlSession对象,该对象包含了执行sql语句的所有方法
5)Executor执行器:它将跟据sqlSession传递的参数动态的生成需要执行的sql语句,同时负责查询缓存的维护
6)MappedStatement对象:该对象是对映射信息的封装,用于存储要映射的SQL语句的id,参数等信息
7)输入参数映射:类似于JDBC对preparedStatement对象设置参数的过程
8)输出结果映射:类似于JDBC对结果集的解析过程
通过RowBounds进行逻辑分页。
物理分页:通过数据库limit
逻辑分页:通过代码实现。先查询出所有的数据,再跟据代码块的所需进行分页
还可以通过插件,例如Pagehepler
支持。
这个举例子再生活形象不过,就像用户与订单的关系,我们在用户中包含了订单的集合,但在显示时不会直接显示出来,但当用户点击订单时,再去查询,显示出来订单。这样可以减少数据库的压力。
实现原理:使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,把关联对象查询出来赋值。
一级缓存:sqlSession级别的缓存,sqlSession里的HashMap对查询过一次的数据进行存储,如果发出数据库增上改查的操作则取消存储
二级缓存:Mapper级别的,多个sqlSession去操作同一个Mapper的sql语句,多个sqlSession可以共用二级缓存
重要:其实解释到这里,对一些没看过源码的人来说并不友好,下面我对这些东西尽量给大家通俗的讲出来:
首先,session大家应该都了解,我们在Java Web的学习过程中接触过,它代表的是一次浏览器和服务器的交互的会话
sqlSession也是一个会话,每次连接一个数据库就会产生一个sqlSession会话。
下面截取了sqlSession的部分源码,sqlSession里封装了操作数据库的所有操作,还包括了Mapper代理对象
一级缓存:sqlSession级别的,也就是参数和sql语句完全一样的情况下,不需要再次查询数据库,那具体该如何判断呢
如果sqlSession调用了close()方法,会释放掉一级缓存
二级缓存:多个sqlSession可以共享一个mapper中的二级缓存区域,并且如果两个mapper的namespace相同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域
下面我们以开启二级缓存进行测试
public class TestStudent extends BaseTest {
public static void selectAllStudent() {
SqlSessionFactory sqlSessionFactory = getSession();
SqlSession session = sqlSessionFactory.openSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> list = mapper.selectAllStudents();
System.out.println(list);
//一级缓存测试
List<Student> list2 = mapper.selectAllStudents();
System.out.println(list2);
session.commit();
//创建了第二个sqlSession
//二级缓存测试
SqlSession session2 = sqlSessionFactory.openSession();
StudentMapper mapper2 = session2.getMapper(StudentMapper.class);
List<Student> list3 = mapper2.selectAllStudents();
System.out.println(list3);
System.out.println("第二次执行");
List<Student> list4 = mapper2.selectAllStudents();
System.out.println(list4);
session2.commit();
}
public static void main(String[] args) {
selectAllStudent();
}
}
我们调用了4次 selectAllStudents() ,但sql语句只执行了一次,两次为一级缓存,一次为二级缓存
1)创建sqlSessionFactory对象
2)生产sqlSession对象
3)获取Mapper代理对象
4)执行数据库操作
5)执行成功,提交事务
6)执行失败,回滚事务
7)关闭会话
联合查询和嵌套查询
联合查询:几个表联合查询,只查询一次,通过在resultMap里面的association,collection节点配置一对一,一对多的类可以完成
嵌套查询:先查一个表,跟据这个表里面的结果的外键id,再去另一个表里面查询数据,也是通过配置association,collection,但另外一个表的查询通过select节点配置
MyBatis动态sql可以让我们在xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能
实现原理:使用OGNL从sql参数对象中的计算表达式的值,跟据表达式的值动态拼接sql,以此来完成动态sql的功能
下面是我动态SQL的一些练习,方便大家理解
//1.if
//跟据username和sex来查询数据。如果username为空,则按sex查询,反之相同
我们先按原版写
select * from user where username = #{username} and sex = #{sex}
这样的话如果出现空值就没法搞了
select * from user where username=#{username}
<if test="sex != null">
and sex=#{sex}
if>
//2.where
//如果出现以and或or开头的他会剔除掉
select * from user
username=#{username}
<if test="sex != null">
and sex=#{sex}
if>
where>
//3.choose
//choose标签,挺猛的,类似于java的switch,总能利用到一个搜索条件
select * from user
id=#{id}
and username=#{username}
and sex=#{sex}
按照顺序:
如果id不为空,sql语句为:select * from user where id=?
如果id为空,username不为空:select * from user where username=?;
//4.trim
//trim是一个格式化的标记,可以完成set或where标记的功能
select * from user
and username=#{username}
and sex=#{sex}
prefix:前缀
prefixOverrides:去掉第一个and或or
//5.foreach
select * from user
#{id}
1)API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理
2)数据处理层:负责具体的SQL查找,SQL解析,SQL执行和执行结果映射处理。它主要的目的是跟据调用的请求完成一次数据库操作
3)基础支撑层:负责最基础的功能支撑,包括连接管理,事务管理,配置加载和缓存处理,这些都是公用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑
4)引导层:加载xml配置和java配置
insert into user( user_name, user_password, create_time) values(#{userName}, #{userPassword} , #{createTime, jdbcType= TIMESTAMP})
select * from orders where order_id=#{id}
Dao接口就是映射文件中的namespace的值,接口的方法名,就射映射文件MappedStatement的id值,接口方法内的参数,就是传递给sql的参数
Dao接口的工作原理就是JDK动态代理,MyBatis运行时会使用JDK动态代理为Dao接口生成代理对象proxy,代理对象会拦截接口方法调用,转而执行方法对应的sql语句,然后将sql执行结果返回
Mybatis仅可以编写针对Executor、StatementHandler、ParameterHandler、ResultSetHandler这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
实现Mybatis的Interceptor接口并重写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,最后在配置文件中配置你编写的插件。