Spring、SpringMVC、Mybatis自己理解

(一)Spring

为什么要使用Spring

  • 方便解耦,便于开发(Spring就是一个大工厂,可以将所有对象的创建和依赖关系维护都交给spring管理)
  • spring支持aop编程(spring提供面向切面编程,可以很方便的实现对程序进行权限拦截和运行监控等功能)
  • 声明式事务的支持(通过配置就完成对事务的支持,不需要手动编程)
  • 方便程序的测试,spring 对junit4支持,可以通过注解方便的测试spring 程序
  • 方便集成各种优秀的框架
  • 降低javaEE API的使用难度(Spring 对javaEE开发中非常难用的一些API 例如JDBC,javaMail,远程调用等,都提供了封装,是这些API应用难度大大降低)

spring 有哪些主要模块

  • Spring AOP 面相切面编程
  • Spring ORM Hibernate|mybatis|JDO
  • Spring Core 提供bean工厂 IOC
  • Spring Dao JDBC支持
  • Spring Context 提供了关于UI支持,邮件支持等
  • Spring Web 提供了web的一些工具类的支持
  • Spring MVC 提供了web mvc , webviews , jsp ,pdf ,export

解释一下什么是 aop

  • 这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

  • AOP是Spring提供的关键特性之一。AOP即面向切面编程,是OOP编程的有效补充。

  • 使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统。从而避免了在业务逻辑的代码中混入很多的系统相关的逻辑——比如权限管理,事物管理,日志记录等等。这些系统性的编程工作都可以独立编码实现,然后通过AOP技术切入进系统即可。从而达到了将不同的关注点分离出来的效果。

  • Spring AOP是基于动态代理实现的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用jdk proxy,去创建代理对象。如果要代理的对象没有实现某个接口,就不能使用jdk proxy,而是使用cjlib生成一个被代理对象的子类作为代理。

  • cjlib,是一个代码生成的类库,可以在运行时动态的生成某个类的子类,主要,cjlib是通过继承的方式做的动态代理,如果某个类被final修饰了,那么他是无法使用cjlib做动态代理的。

  • AOP相关的概念

    • Aspect :切面,切入系统的一个切面。比如事务管理是一个切面,权限管理也是一个切面;
    • Join point :连接点,也就是可以进行横向切入的位置;
    • Advice :通知,切面在某个连接点执行的操作
    • Pointcut :切点,符合切点表达式的连接点,也就是真正被切入的地方;
  • AOP 的实现原理

    • AOP分为静态AOP和动态AOP。
    • 静态AOP是指AspectJ实现的AOP,他是将切面代码直接编译到Java类文件中。
    • 动态AOP是指将切面代码进行动态织入实现的AOP。
      • Spring的AOP为动态AOP,实现的技术为: JDK提供的动态代理技术 和 CGLIB(动态字节码增强技术) 。

解释一下什么是 ioc

  • IOC是Inversion of Control的缩写,即控制反转,就是将原本在程序中手动创建对象的主动权交由spring框架来管理。ioc容器是spring用来实现ioc的载体,实际上就是map(key,value)。map中存放各种对象。

将对象之间的相互依赖关系交给ioc容器来管理,并由ioc容器来完成对象的注入。这样在很大程度上简化了应用开发。ioc容器就像是一个工厂,当我们需要创建一个对象的时候,只需要配置好文件或者注解即可,完全不用考虑对象是如何创建出来的。

依赖注入:就是把底层类作为参数传入到上层类中,实现了上层类对下层类的控制。

Spring提供了两种ioc容器,分别是BeanFactory和ApplicationContext。

  • 1.BeanFactory:它是基础类型的ioc容器,就是一个管理bean的工厂,主要负责初始化各种bean,并调用他们的生命周期方法。

  • 2.Application:是BeanFactory的子接口,不仅提供了BeanFactory的所有功能,还额外增加了国际化、资源访问、事件传播等功能。它有两种方式获得xml文件。分别是:

    • 1.ClassPathXmlApplicationContext :是从类路径中寻找指定的xml配置文件,找到并装载完成applicationContext的实例化工作。
    • 2.FileSystemXmlApplicationContext:从指定的文件系统路径中寻找指定的xml配置文件,找到并装载完成application的实例化工作。
  • 3.beanFactory和allication的区别是:
    Beanfactory在启动的时候不会去实例化Bean,而是有需要的时候才去实例化。而application在容器启动的时候,会实例化所有的Bean.还可以为bean配置lazy-init=true让bean延迟实例化。

  • 依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。

  • 对象A依赖于对象B,当对象 A需要用到对象B的时候,IOC容器就会立即创建一个对象B送给对象A。

  • IOC容器就是一个对象制造工厂,你需要什么,它会给你送去,你直接使用就行了,而再也不用去关心你所用的东西是如何制成的,也不用关心最后是怎么被销毁的,这一切全部由IOC容器包办。

  • IOC中最基本的技术就是 “反射(Reflection)” 编程

Spring bean的生命周期

Spring bean的完整生命周期从创建Spring容器开始,直到最终spring容器销毁Bean.

  • 1.首先是对bean进行实例化
  • 2.设置实例化Bean的属性,也就是ioc注入
  • 3.bean是否实现了aware相关接口,比如beanNameAware、BeanclassLoaderAware、BeanFactoryAware或者ApplicationContextAware接口;
  • 4.如果这个bean关联了BeanPostProcessor接口,就会调用postProcessBeforeInitialization方法
  • 5.执行初始化方法,首先检查这个bean是否实现InitializingBean的接口,调用bean的afterpropertiesset这个方法
  • 6.检查配置文件中init-method的设置
  • 7.注册Destruction相关接口并回调方法
  • 8.检查bean是否实现了disposableBean这个接口,调用destory方法
  • 9.检查配置文件中destory-method设置,如果没有设置,就调用特定的销毁方法。

spring 常用的注入方式有哪些

  • 构造方法注入
  • setter注入
  • 基于注解的注入
  • 静态工厂的方法注入
  • 实例工厂的方法注入

Spring的常用注解有哪些

  • @Conponent 是所有受spring管理组件的通用形式
  • @Controller 对应表现层的bean
  • @Service 对应业务层的bean
  • @Repository 对应数据访问层的bean
  • @Autowired 按类型注入 ,就是自动装配,为了消除代码中getter/setter与bean属性中的property
  • @Resource 默认通过name属性去匹配Bean,找不到再按tye去匹配
  • @Qualifier用来指定要注入的bean的名字

什么是Spring bean

Spring bean表示受到Spring管理的对象。具体来说,是被Spring框架容器初始化、配置和管理的对象。Spring bean可以在配置文件中定义,也可以通过注解定义,在Spring容器中初始化,然后注入到应用程序中的。

spring 中的 bean 是线程安全的吗

Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。

Spring如何解决线程并发问题

一般情况下,只有无状态的bean才可以被多线程共享。在Spring中,大多数的bean对象作用域是"singeton",Spring对一些Bean中非线程安全状态采取的是ThreadLocal进行处理,解决非线程安全问题。

ThreadLocal和同步机制都可以解决多线程访问相同变量所带来的冲突问题。同步机制采用了"时间交换空间",只有线程获取到锁的时候,才能够访问变量,而其他线程需要等待。ThreadLocal采用了“空间交换时间”,为每个线程中都复制了一份访问变量的副本,这样每个线程访问都是自己内部的变量。ThreadLocal提供了线程安全共享对象,在编写多线程代码时,可以将不安全的变量封装到threadLocal中。

Spring自动装配

Spring框架中,无需自己去管理或者创建各个对象之间的相互依赖关系,由容器统一管理,使用autowire来自动装配配置模式。

在Spring框架中xml文件共有5种自动装配方法:

  • 1.no :默认方式不进行自动装配,通过手动设置red属性进行bean装配
  • 2.byname:根据Bean的名称进行装配
  • 3.byType: 根据bean的类型进行装配
  • 4.byConstructor:根据构造器方法进行装配,而构造器方法中的参数是通过byType进行装配的
  • 5.autodetect:自动探测,如果有构造方法,就通过构造方法进行自动装配,如果没有,就通过byType自动装配

基于注解:

  • 使用@Autowired注解来自动装配指定的bean.在使用@autowired之前,需要在配置文件中,开启注解。在启动ioc时,容器就会自动装载一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowired、@Resource或者@Inject,就会在IOC容器中自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查找到相对应类型的bean:
  • 如果查询结果刚好一个,就将该Bean装配给@Autowired指定的数据
  • 如果查询结果有好多个,那么@Autowired会根据名称来查找
  • 如果上述查找的结果为空,那么就会抛出异常,解决方法时,使用required=false

@Autowire和@Resource注解的区别

  • @Autowire和@Resource都是Spring支持的注解方式动态装配bean。
  1. @Autowire:Spring注解,@Resource:JDK注解

  2. @Autowire默认按照类型(by-type)装配,默认情况下要求依赖对象必须存在。

    • 如果允许依赖对象为null,需设置required属性为false
    • 如果使用按照名称(by-name)装配,需结合@Qualifier注解使用
  3. @Resource默认按照名称(by-name)装配,名称可以通过name属性指定。

    • 如果没有指定name
      • 当注解在字段上时,默认取name=字段名称装配。
      • 当注解在setter方法上时,默认取name=属性名称装配。
    • 当按照名称(by-name)装配未匹配时,按照类型(by-type)装配
      • 当显示指定name属性后,只能按照名称(by-name)装配
  4. @Resoure装配顺序

    • 如果同时指定name和type属性,则找到唯一匹配的bean装配,未找到则抛异常;
    • 如果指定name属性,则按照名称(by-name)装配,未找到则抛异常;
    • 如果指定type属性,则按照类型(by-type)装配,未找到或者找到多个则抛异常;
    • 既未指定name属性,又未指定type属性,则按照名称(by-name)装配;如果未找到,则按照类型(by-type)装配。
  5. @Autowire + @qualifier("") = @Resource(name="")

Spring 有哪些设计模式

  • 1.简单工厂:BeanFactory是一种简单的工厂模式 ,根据传入一个唯一的表示来获得bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。

    • 实质:由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。
    • 实现原理:
      • bean容器的启动阶段:
        • 1.读取Bean的xml配置文件,将Bean元素分别转换成一个beandefinition对象。
        • 2.然后通过beanDefinitionRegistry将这些Bean注册到beanFactory中,保存在它的一个ConcurrentHashMap中。
        • 3.将beanDefinition注册到beanfactory之后,在这里Spring为我们提供了一个扩展的切口,允许我们通过实现beanFactoryPostProcessor在此处插入我们定义的代码。 典型的例子就是:PropertyPlaceholderConfigurer,一般在配置数据库的dataSource时使用到的占位符的值,就是它注入进去的。
      • 容器中bean的实例化阶段:
        实例化阶段主要是通过反射或者cglib对Bean进行实例化,在这个阶段spring又提供了很多扩展点:
        • 1.各种aware接口,比如BeanFactoryAware,对于实现了这些Aware接口的Bean,在实例化bean时spring 会帮助我们注入对应的beanFactory的实例。
          1. BeanPostProcessor接口,实现了BeanPostProcessor接口的bean,在实例化bean时Spring会帮我们调用接口中的方法。
        • 3.InitializingBean接口,实现了InitializingBean接口的bean,在实例化bean时Spring会帮我们调用接口中的方法。
        • 4.DisposableBean接口,实现了BeanPostProcessor接口的bean,在该bean死亡时Spring会帮我们调用接口中的方法。
    • 设计意义:
      • 1.松耦合。可以将原来硬编码的依赖,通过Spring这个beanFactory这个工厂来注入依赖,也就是说原来只有依赖方和被依赖方,现在我们引入了第三方——spring这个beanFactory,由它来解决bean之间的依赖问题,达到了松耦合的效果.
      • 2.额外的Bean处理。通过spring接口的暴露,在实例化Bean的阶段,我们可以进行一些额外的处理,这些额外的处理只需要让bean实现对应的接口即可,那么spring就会在bean的生命周期调用我们实现的接口来处理该bean。
  • 2.工厂方法

    • 实现方式:FactoryBean接口
    • 实现原理:实现了FactoryBean接口的bean是一类叫做factory的Bean,其特点是,spring会在使用getBean()调用获得该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个Bean,而是这个bean.getObject()方法的返回值。
    • 典型的例子有spring与mybatis的结合:

      说明,该bean,因为实现了factoryBean接口,所以返回的不是sqlSessionFactoryBean的实例,而是她的sqlSessionFactoryBean.getObjetct()的返回值。
  • 3.单例模式

      1. Spring依赖注入Beanh实例默认是单例的。
      1. Spring的依赖注入都是发生在AbstractBeanFactory的getBean里。getBean的doGetBean方法调用getSingleton进行Bean的创建。
      1. getSingleton(),在spring依赖注入时,使用了双重判断加锁的单例模式。
  • 3.代理模式: Spring中aop动态代理有两种方法: jdk proxy和cdlib proxy

    • 实现方式:AOP底层,就是动态代理模式实现的。
      • 动态代理:在内存中构建,不需要手动编写代理类
      • 静态代理:需要手工编写代理类,代理类引用被代理对象。
    • 实现原理:切面在应用运行的时刻被织入。一般情况下,在织入切面时,aop容器会为目标对象动态的创建一个代理对象。织入:把切面应用到目标对象并创建新的代理对象的过程。
  • 4.模板模式: 常用的有:JpaTemplate、ResTemplate、JdbcTemplate 解决重复代码的问题

    • 经典模板方法定义:

      • 父类定义了骨架(调用哪些方法及顺序),某些特定方法由子类实现
      • 最大的好处:代码复用,减少重复代码。除了子类要实现的特定方法,其他方法及方法调用顺序都在父类中预先写好了。
    • Spring模板方法模式实质:是模板方法和回调模式的结合。是Template Method不需要继承的另一种实现方式。Spring几乎所有的外界扩展都采用这种模式。

    • JDBCTemplate为例,采用模板方法模式是为了以一种统一而集中的方式来处理资源的获取和释放。
      public abstract class JdbcTemplate {
      public final Object execute(String sql){
      Connection con=null;
      Statement stmt=null;
      try{
      con=getConnection();
      stmt=con.createStatement();
      Object retValue=executeWithStatement(stmt,sql);
      return retValue;
      }catch(SQLException e){

      }finally{
      closeStatement(stmt);
      releaseConnection(con);
      }
      }
      protected abstract Object executeWithStatement(Statement stmt, String sql);
      }

    • 引入回调原因

      • jdbcTemplate是抽象类,不能够独立使用,每次就那些数据访问的时候都要给出一个相应的子类来实现,这样肯定就不方便了,所以就引入了回调。
      • 回调代码 public interface StatementCallback{
        Object doWithStatement(Statement stmt);
        }
    • 为什么jdbcTemplate没有使用继承?
      因为这个类的方法太多,但是我们还是想用到Jdbc Template已有的稳定的、公用的数据库连接,可以把变化的东西(代码)抽出来作为一个参数传入jdbc Template的方法中。使用回调对象,在这个回调对象中定义一个操作jdbcc template中变量的方法,去实现这个方法,把代码集中到这里了。然后再传入这个回调对象到jdbc Template,从而完成了调用。

  • 5.观察者模式

    • 实现方式:spring的事件驱动模型使用的是观察者模式,SPring中observer模式常用的地方是listener的实现。
    • 具体实现:事件机制的实现需要三个部分:事件源,事件,事件监听器
      • ApplicationEvent抽象类【事件】
        • 继承自jdk的eventObject,所有的事件都需要继承ApplicationEvent,并且通过构造器参数source得到事件源。
        • 该类的实现类ApplicationCOntextEvent表示ApplicationContext的容器事件。
      • ApplicationListener接口【事件监听器】
        • 继承自jdk的eventListener,所有的监听器都要实现这个接口
        • 这个接口只有一个onApplicationEvent()方法,该方法接受一个ApplicationEvent或其子类对象作为参数,在方法体中,可以通过不同对Event类的判断来进行相应的处理。
        • 当事件触发时所有的监听器都会收到消息。
      • ApplicationContext接口【事件源】
        • ApplicationContext是spring中的全局容器
        • 实现了ApplicationEventPublicsher接口
        • 职责:负责读取bean的配置文件,管理bean的加载。维护bean之间的依赖关系,可以说是负责Bean的整个生命,就是平时说的ioc容器。
      • ApplicationEventMulticaster抽象类【事件源中publishEvent方法需要调用其方法getApplicationEventMulticaster】
        • 属于事件广播器,它的作用是把Applicationcontext发布的Event广播给所有的监听器.

    定义对象键是一种一对多的依赖关系当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新。如Spring中listener的实现–ApplicationListener.

  • 7.策略模式(一个类的行为或其算法可以在运行时更改)

    • 实现方式:Spring框架的资源访问Resource接口 。该接口提供了更强的资源访问能力,Spring 框架本身大量使用了 Resource 接口来访问底层资源。
    • Resource接口介绍
      • source 接口是具体资源访问策略的抽象,也是所有资源访问类所实现的接口。
      • Resource 接口主要提供了如下几个方法:
        • getInputStream():定位并打开资源,返回资源对应的输入流。每次调用都返回新的输入流。调用者必须负责关闭输入流。
        • exists():返回 Resource 所指向的资源是否存在。
        • isOpen():返回资源文件是否打开,如果资源文件不能多次读取,每次读取结束应该显式关闭,以防止资源泄漏。
        • getDescription():返回资源的描述信息,通常用于资源处理出错时输出该信息,通常是全限定文件名或实际 URL。
        • getFile:返回资源对应的 File 对象。
        • getURL:返回资源对应的 URL 对象。
    • Resource 接口本身没有提供访问任何底层资源的实现逻辑,针对不同的底层资源,Spring 将会提供不同的 Resource 实现类,不同的实现类负责不同的资源访问逻辑。

Spring的通知有哪些

  • 1.前置通知:执行在方法之前
  • 2.异常通知:方法出现异常时,执行
  • 3.后置通知: 方法正常返回后执行的通知,如果没有正常返回,抛出了异常,就不会执行
  • 4.最终通知: 执行在方法之后,不管方法执行的结果如何,都会被执行
  • 5.环绕通知: 包围一个方法的通知。环绕通知可以在方法调用前后完成自定义的行为。也会选择是否继续执行方法或直接返回自己的返回值或跑出异常来结束执行。 环绕通知是最常用的一种通知类型,就是利用反射调用目标方法即可。环绕通知优先于其他普通通知。
    环绕前置----普通前置----目标方法执行--------环绕正常返回/出现异常---------环绕后置--------普通后置---------普通返回或者异常

Spring框架中如何有效使用jdbc

只需要关注statement和jquery从数据存储数据,也可以使用spring框架中提供的jdbctemplate


(二)SpringMVC

什么是SpringMVC?

Spring MVC是一个基于Java实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model、View、Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发、减少出错,方便组内开发人员之间的配合.

SpringMVC流程

  • 1.用户发送请求至前端控制器DispatcherServlet
  • 2.DispatcherServlet收到请求后,调用HandleMapping处理器映射器,请求获取Handle
  • 3.HandleMapping处理器映射器通过Url找到具体的处理器,生成处理器对象及处理器拦截器一并返回给DispatcherServlet
  • 4.DispatcherServlet调用HandleAdapter处理器适配器
  • 5.HandleAdapter经过适配器调用处理器(Handle,也叫后端控制器)
  • 6.Handler执行完返回ModelAndView
  • 7.HandleAdapter将Handler执行返回的结果ModelAndView返回给DispatcherServlet
  • 8.DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析
  • 9.ViewResolver解析后返回具体View
  • 10.DispatcherServlet对View进行视图渲染(即将模型数据填充到视图中)
  • 11.DispatcherServlet响应用户

SpringMVC怎么样设定重定向和转发的?

  • 1.转发:在返回值前面加“forwar”
  • 2.重定向:在返回值前面加"redirect"

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

  • 1.解决POST请求中文乱码问题

    • 在web.xml文件中配置一个CharacterEncodeFilter过滤器,设置成utf-8
  • 2.解决Get请求中文乱码问题

    • 可以修改Tomcate配置文件添加编码和工程编码一致
    • 可以对参数进行编码

SpringMVC处理异常的方法

  • 1.使用SpringMvc的简单异常处理SimpleMappingExceptionResolver
  • 2.实现Spring的异常处理接口HandlerExceptionResolver,自定义自己的异常处理器
  • 3.使用@ExceptionHandler注解实现异常处理

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

SpringMvc是基于方法开发的的,一个url对应一个方法,请求参数作为方法的形参,一般设计为单例或多例。但默认情况下是单例模式;只要不在@controller中定义属性,那么单例就是安全的,一旦定义了属性,就会在多线程中出现访问数据冲突问题,线程不安全,不建议使用同步机制,会牺牲时间换取空间,影响性能。解决方法就是,不要在控制器中设置属性。

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

一般可以用@Controller注解,也可以使用@RestController,@RestController注解相当于@ResponseBody和@Controller的结合,最大的区别就是,RestController可以将controller方法返回的java数据转为json数据,并响应给用户

(三)MyBatis

什么是MyBatis?

  • 定义
    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的过程)。

Mybaits的优点:

1. 基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
2. 与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;
3. 很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。
4. 能够与Spring很好的集成;
5. 提供映射标签,支持对象与数据库的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开发可以节省很多代码,提高效率。 

#{}和${}的区别是什么?

- #{}是预编译处理,${}是字符串替换。
- Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
- Mybatis在处理${}时,就是把${}替换成变量的值。
- 使用#{}可以有效的防止SQL注入,提高系统安全性。

当实体类中的属性名和表中的字段名不一样 ,怎么办 ?

1. 第1种: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。
2. 第2种: 通过<resultMap>来映射字段名和实体类属性名的一一对应的关系。

模糊查询like语句该怎么写?

1. 第1种:在Java代码中添加sql通配符。
```xml
string wildcardname = “%smi%”;
list<name> names = mapper.selectlike(wildcardname);
 
<select id=”selectlike”>
 select * from foo where bar like #{value}
</select>
```

2. 第2种:在sql语句中拼接通配符,会引起sql注入
```xml
string wildcardname = “smi”;
list<name> names = mapper.selectlike(wildcardname);
 
<select id=”selectlike”>
     select * from foo where bar like "%"${value}"%"
</select>
```

通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?

- Dao接口即Mapper接口。接口的全限名,就是映射文件中的namespace的值;接口的方法名,就是映射文件中Mapper的Statement的id值;接口方法内的参数,就是传递给sql的参数。
- Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MapperStatement。在Mybatis中,每一个<select>、<insert>、<update>、<delete>标签,都会被解析为一个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执行结果封装为目标对象并返回的?都有哪些映射形式?

1. 第一种是使用<resultMap>标签,逐一定义数据库列名和对象属性名之间的映射关系。
2. 第二种是使用sql列的别名功能,将列的别名书写为对象属性名。
- 有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

如何执行批量插入?

  • MyBatis的foreach标签
<insert id="insert" parameterType="list" >
    insert into user_word_importance_practice (user_id,word,create_time) 
    VALUES
    <foreach collection="list" item="wordImportance" separator=",">
        (#{wordImportance.userId},#{wordImportance.word},#{wordImportance.createTime})
    </foreach>
</insert> 
  • 下面处理方式影响性能
  • 首先创建insert语句
    <insert id=”insertname”>
         insert into names (name) values (#{value})
    </insert>
  • 在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(); 
     throw e; 
    }
     finally {
         sqlsession.close();
    }

如何获取自动生成的(主)键值?

  • insert 方法总是返回一个int值 ,这个值代表的是插入的行数。
  • 如果采用自增长策略,自动生成的键值在 insert 方法执行完后可以被设置到传入的参数对象中。

在mapper中如何传递多个参数?

  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>  
  1. 第二种: 使用 @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>
  1. 第三种:多个参数封装成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?

  • 传统的使用JDBC的方法,相信大家在组合复杂的的SQL语句的时候,需要去拼接,稍不注意哪怕少了个空格,都会导致错误。Mybatis的动态SQL功能正是为了解决这种问题, 其通过 if, choose, when, otherwise, trim, where, set, foreach标签,可组合成非常灵活的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版本的namespace是可选的,不过新版本的namespace已经是必须的了。

为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?

  • Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。

一对一、一对多的关联查询 ?

<mapper namespace="com.lcb.mapping.userMapper">  
    <!--association  一对一关联查询 -->  
    <select id="getClass" parameterType="int" resultMap="ClassesResultMap">  
        select * from class c,teacher t where c.teacher_id=t.t_id and c.c_id=#{id}  
    </select>  
 
    <resultMap type="com.lcb.user.Classes" id="ClassesResultMap">  
        <!-- 实体类的字段名和数据表的字段名映射 -->  
        <id property="id" column="c_id"/>  
        <result property="name" column="c_name"/>  
        <association property="teacher" javaType="com.lcb.user.Teacher">  
            <id property="id" column="t_id"/>  
            <result property="name" column="t_name"/>  
        </association>  
    </resultMap>  
 
 
    <!--collection  一对多关联查询 -->  
    <select id="getClass2" parameterType="int" resultMap="ClassesResultMap2">  
        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}  
    </select>  
 
    <resultMap type="com.lcb.user.Classes" id="ClassesResultMap2">  
        <id property="id" column="c_id"/>  
        <result property="name" column="c_name"/>  
        <association property="teacher" javaType="com.lcb.user.Teacher">  
            <id property="id" column="t_id"/>  
            <result property="name" column="t_name"/>  
        </association>  
 
        <collection property="student" ofType="com.lcb.user.Student">  
            <id property="id" column="s_id"/>  
            <result property="name" column="s_name"/>  
        </collection>  
    </resultMap>  
</mapper> 

MyBatis实现一对一有几种方式?具体怎么操作的?

  • 有联合查询和嵌套查询
  • 联合查询是几个表联合查询,只查询一次, 通过在resultMap里面配置association节点配置一对一的类就可以完成;
  • 嵌套查询是先查一个表,根据这个表里面的结果的 外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。

MyBatis实现一对多有几种方式,怎么操作的?

  • 有联合查询和嵌套查询。
  • 联合查询是几个表联合查询,只查询一次,通过在resultMap里面的collection节点配置一对多的类就可以完成;
  • 嵌套查询是先查一个表,根据这个表里面的 结果的外键id,去再另外一个表里面查询数据,也是通过配置collection,但另外一个表的查询通过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. 一级缓存是sqlSession级别的缓存,默认开启的 ,在操作数据库时需要构造SqlSession对象,在对象中HashMap用于存储缓存数据。不同的sqlSession之间的缓存数据区域是互相不影响的。 当在同一个sqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据写到缓存,第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。

一级缓存的范围有SESSION和STATEMENT两种,默认是SESSION,如果不想使用一级缓存,可以把一级缓存的范围指定为STATEMENT,这样每次执行完一个Mapper中的语句后都会将一级缓存清除。
<setting name="localCacheScope" value="STATEMENT"/>

  • 需要注意的是,如果sqlSession执行了增删改,并且提交到数据库,Mybatis则会清除sqlSession中的一级缓存,这样做的目的是为了保证缓存中存储的是最新消息,避免出现脏读现象。
  • 当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。

  • 关闭一级缓存后,再次访问,需要再一次获取一级缓存,然后才能查找数据,否则会抛出异常。

  • 存储数据的形式:相互关联的持久化对象

  • 缓存的范围:事务范围,每个事务都用于单独的一级缓存

  • 并发访问策略:由于每个事务都拥有单独的一级缓存不会出现并发问题,因此无需提供并发访问策略

  • 数据过期策略:处于一级缓存中的对象永远不会过期,除非应用程序显示清空或者清空特定对象

  • 物理介质:内存

  1. 二级缓存是mapper级别的缓存 ,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
  • 作用域是mapper的同一个namespace。不同的sqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中查询的数据写到缓存,第二次查询会从缓存中获取数据,不再去底层数据库查询,从而提高效率。

  • 在Mybatis配置文件中开启二级缓存

  • 存放数据的形式:对象的散装数据

  • 缓存的范围:进程范围或集群范围,缓存被同一个进行或集群范围内所有事务共享

  • 并发访问策略:由于多个事务会同时访问二级缓存中的相同数据,因此必须提供适当的并发访问策略,来保证特定的事务隔离级别。

  • 数据过期策略:必须提供数据过期策略,如基于内存的缓存中对象的最大数目,允许对象处于缓存中的最大时间,以及允许对象处于缓存中的最长空闲时间。

    • LRU 最近最少使用,移除最长时间不被使用的对象,为默认策略
    • FIFO 先进先出,按对象进入缓存的顺序来移除它们
    • SOFT 软引用,移除基于垃圾回收器状态和软引用规则的对象
    • WEAK 弱引用,更积极的移除基于垃圾收集器状态和弱引用规则对象
  • 物理介质:内存和硬盘,对象的散装数据首先存放到基于内存的缓存中,当内存中对象的数据达到数据过期策略的maxElementsInMemory值,就会把其余的对象写入到基于硬盘的缓存中。

什么样的数据适合存放到二级缓存中?

  • 很少被修改的数据
  • 不是很重要的数据,允许出现偶尔并发的数据
  • 不会被并发访问的数据

为什么多表操作一定不能使用缓存

首先不管多表操作写到哪个namespace下,都会存在某个表不在这个namespace下的情况。
例如两个表:role和user_role,如果我想查询出某个用户的全部角色role,就一定会涉及到多表的操作。不管是写到roleMapper.xml还是usermapper.xml或者是独立的mapper.xml文件中。如果使用了二级缓存,都会导致可能查询不正确。 如果对这个用户的角色修改, 这样会出现脏数据,那就失去了缓存的意义。

如何去解决这样的问题?

  • 通过拦截器判断执行的sql涉及到那些表,然后把相关表的缓存自动情况,但是这样的话效率是很低的。
  • 放弃二级缓存,在业务层使用可控制的缓存替代即可。
  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接口的类路径。

Mapper编写有哪几种方式?

  1. 第一种:接口实现类继承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>
  1. 第二种:使用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接口:
①mapper.xml中的namespace为mapper接口的地址
②mapper接口中的方法名和mapper.xml中的定义的statement的id保持一致
③Spring中定义
<bean id="" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface"   value="mapper接口地址" /> 
    <property name="sqlSessionFactory" ref="sqlSessionFactory" /> 
</bean>
  1. 第三种:使用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>
或者:在启动类中加上@MapperScan(“mapper类路径”)注解
(4)使用扫描器后从spring容器中获取mapper的实现对象。

简述Mybatis的插件运行原理,以及如何编写一个插件。

  • Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
  • 编写插件:实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。

MyBatis总体流程

  1. 加载配置并初始化
    • 触发条件:加载配置文件
    • 处理过程:将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
  2. 接收调用请求
    • 触发条件:调用Mybatis提供的API
    • 传入参数:为SQL的ID和传入参数对象
    • 处理过程:将请求传递给下层的请求处理层进行处理。
  3. 处理操作请求
    • 触发条件:API接口层传递请求过来
    • 传入参数:为SQL的ID和传入参数对象
    • 处理过程:
      A 根据SQL的ID查找对应的MappedStatement对象。
      B 根据传入参数对象解析MappedStatement对象,得到最终要执行的SQL和执行传入参数。
      C 获取数据库连接,根据得到的最终SQL语句和执行传入参数到数据库执行,并得到执行结果。
      D 根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果。
      E 释放连接资源。
  4. 返回处理结果将最终的处理结果返回。

Statement和PreparedStatement

1.使用Statement对象
使用范围:当执行相似SQL(结构相同,具体值不同)语句的次数比较少
优点:语法简单
缺点:采用硬编码效率低,安全性较差。
原理:硬编码,每次执行时相似SQL都会进行编译

2.预编译PreparedStatement
使用范围:当执行相似sql语句的次数比较多(例如用户登陆,对表频繁操作…)语句一样,只是具体的值不一样,被称为动态SQL
优点:语句只编译一次,减少编译次数。提高了安全性(阻止了SQL注入)
缺点: 执行非相似SQL语句时,速度较慢。
原理:相似SQL只编译一次,减少编译次数

3.使用PreparedStatement + 批处理
使用范围:一次需要更新数据库表多条记录
优点:减少和SQL引擎交互的次数,再次提高效率,相似语句只编译一次,减少编译次数。提高了安全性(阻止了SQL注入)
缺点:
原理:批处理: 减少和SQL引擎交互的次数,一次传递给SQL引擎多条SQL。
名词解释:
PL/SQL引擎:在oracle中执行pl/sql代码的引擎,在执行中发现标准的sql会交给sql引擎进行处理。
SQL引擎:执行标准sql的引擎。


jdbc编程步骤

  1. 加载数据库驱动
DriverManager.registerDriver(new Driver());//可不要?
Class.forName("com.mysql.jdbc.Driver");
  1. 创建并获取数据库链接
Properties properties  = new Properties();//配置文件要求里面必须有连接参数和配置参数	
properties.load(new FileInputStream("src\\druid.properties"));
DataSource ds =	DruidDataSourceFactory.createDataSource(properties);
Connection connection = ds.getConnection();
//或者
String url = "jdbc:mysql://localhost:3306/1221db";
String user = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, user, password);
  1. 设置sql语句
    String sql = "INSERT INTO t_employee (ename,tel,gender,salary,did) VALUES(?,?,?,?,?)";

  2. 准备一个PreparedStatement:预编译sql
    PreparedStatement pst = conn.prepareStatement(sql);

  3. 把?用具体的变量的赋值

pst.setString(3, gender);
pst.setDouble(4, salary);
pst.setInt(5, did);
  1. 通过statement执行sql并获取结果
    ResultSet rs = pst.executeQuery();
  2. 对sql执行结果进行解析处理
while (rs.next()) {
    int id = rs.getInt("eid");
    String ename = rs.getString("ename");
    String tel = rs.getString("tel");
    String gender = rs.getString("gender");
    double salary = rs.getDouble("salary");
    System.out.println(id + "\t" + ename + "\t" + tel + "\t" + gender + "\t" + salary);
}
  1. 释放资源(resultSet、preparedstatement、connection)
rs.close();
pst.close();
conn.close();

JDBC存在问题

  1. 数据库连接,使用时就创建,不使用立即释放,对数据库进行频繁连接开启和关闭,造成数据库资源浪费,影响数据库性能;如果未关闭,内存泄漏;不能设置连接对象数,连接过多,也可能导致内存泄漏,服务器崩溃。
  • 设想:使用数据库连接池管理数据库连接。
  1. 将sql语句硬编码到java代码中,如果sql语句修改,需要重新编译java代码,不利于系统维护。
  • 设想:将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译。
  1. 向preparedStatement中设置参数,对占位符号位置和设置参数值,硬编码在java代码中,不利于系统维护。
  • 设想:将sql语句及占位符号和参数全部配置在xml中。
  1. 从resutSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,,不利于系统维护。

Mybatis和jdbc的区别

MyBatis是一个支持***普通**SQL*查询*,***存储过程*高级映射*的优秀持久层框架。MyBatis是对JDBC的封装。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。

1. 优化获取和释放

在访问数据库时都是通过数据库连接池来操作数据库,数据库连接池有好几种,比如C3P0、DBCP,也可能采用容器本身的JNDI数据库连接池。我们可以通过DataSource进行隔离解耦,我们统一从DataSource里面获取数据库连接

2.SQL统一管理,对数据库进行存取操作

我们使用JDBC对数据库进行操作时,SQL查询语句分布在各个Java类中,这样可读性差,不利于维护,当我们修改Java类中的SQL语句时要重新进行编译。

Mybatis可以把SQL语句放在配置文件中统一进行管理,以后修改配置文件,也不需要重新就行编译部署。

3.生成动态SQL语句

我们在查询中可能需要根据一些属性进行组合查询,比如我们进行商品查询,我们可以根据商品名称进行查询,也可以根据发货地进行查询,或者两者组合查询。如果使用JDBC进行查询,这样就需要写多条SQL语句。

Mybatis可以在配置文件中通过使用标签进行SQL语句的拼接,生成动态SQL语句。

4.能够对结果集进行映射

我们在使用JDBC进行查询时,返回一个结果集ResultSet,我们要从结果集中取出结果封装为需要的类型

在Mybatis中我们可以设置将结果直接映射为自己需要的类型,比如:JavaBean对象、一个Map、一个List等等。

  • 设想:将查询的结果集,自动映射成java对象。(QueryRunner类?)
//JDBCTOOLS封装Druid连接池示例
import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;

public class JDBCTools {
	// 1、数据源,即连接池
	private static DataSource dataSource;
	
	// 2、ThreadLocal对象
	private static ThreadLocal<Connection> threadLocal;

	static {
		try {
			//1、读取druip.properties文件
			Properties pro = new Properties();
			pro.load(JDBCTools.class.getClassLoader().getResourceAsStream("druid.properties"));
			
			//2、连接连接池
			dataSource = DruidDataSourceFactory.createDataSource(pro);

			//3、创建线程池
			threadLocal = new ThreadLocal<>();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取连接的方法
	 * 
	 * @return
	 * @throws SQLException
	 */
	public static Connection getConnection() {
		// 从当前线程中获取连接
		Connection connection = threadLocal.get();
		if (connection == null) {
			// 从连接池中获取一个连接
			try {
				connection = dataSource.getConnection();
				// 将连接与当前线程绑定
				threadLocal.set(connection);
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return connection;
	}

	/**
	 * 释放连接的方法
	 * 
	 * @param connection
	 */
	public static void releaseConnection() {
		// 获取当前线程中的连接
		Connection connection = threadLocal.get();
		if (connection != null) {
			try {
				connection.close();
				// 将已经关闭的连接从当前线程中移除
				threadLocal.remove();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

4.Web组件

Servelt,Fliter,Listen

Servlet是用来处理客户端请求的动态资源,也就是当我们在浏览器中输入一个地址回车跳转后,请求就会被发送到对应的Servlet上进行处理。

Servlet的任务有:

1.接收请求数据:我们都知道客户端请求会被封装成HttpServletRequesr对象,里面包含了请求头、参数等各种信息。

2.处理请求:通常我们会在service,dopost,doget方法进行接收参数,并且调用业务层的方法来处理请求

3.完成响应“处理完请求后,我们一般会转发(forword)或者重定向(redirect)到某个页面,转发是HttpServletRequest中的方法,重定向是HttpServletResponse中的方法,两者是有很大区别的

Servlet的创建:Servlet可以在以第1次接收时被创建,也可以在在服务器启动时就被创建,这需要在web.xml的中添加一条配置信息,< load-on-startup>5< /load-on-startup>,当值为0或者大于0时,表示容器在应用启动时就加载这个servlet,当是一个负数时或者没有指定时,则指示容器在该servlet被请求时才加载。

servlet的生命周期方法

servlet的初始化,旨在servlet实例时候调用一次,Servlet是单例,整个服务器就只创建一个同类型Servlet

servlet的处理请求方法,在servlet被请求时,会被马上调用,每处理一次请求,就会被调用一次,

servlet销毁之前执行的方法,只执行一次,用于释放servlet占有的资源,通常servlet是没有什么可要释放的,所以该方法一般都是空

Fliter

filter与servlet在很多的方面极其相似,但是也有不同,例如filter和servlet一样都又三个生命周期方法,同时他们在web.xml中的配置文件也是差不多的、 但是servlet主要负责处理请求,而filter主要负责拦截请求,和放行。

filter四种拦截方式

1.REQUEST:直接访问目标资源时执行过滤器。包括:在地址栏中直接访问、表单提交、超链接、重定向,只要在地址栏中可以看到目标资源的路径,就是REQUEST

2.FORWOARD:转发访问执行过滤器。包括RequestDispatcher#forward()方法、< jsp:forward>标签都是转发访问;

3.INCLUDE:包含访问执行过滤器。包括RequestDispatcher#include()方法、< jsp:include>标签都是包含访问;

4.ERROR:当目标资源在web.xml中配置包括RequestDispatcher#include()方法、< jsp:include>标签都是包含访问;

url-mapping的写法
匹配规则有三种:

  • 精确匹配 —— 如/foo.htm,只会匹配foo.htm这个URL
  • 路径匹配 —— 如/foo/*,会匹配以foo为前缀的URL
  • 后缀匹配 —— 如.htm,会匹配所有以.htm为后缀的URL*
  • < url-pattern>的其他写法,如/foo/ ,/.htm ,/foo 都是不对的。

执行filter的顺序

如果有多个过滤器都匹配该请求,顺序决定于web.xml filter-mapping的顺序,在前面的先执行,后面的后执行

Listener

listener就是监听器,我们在JAVASE开发时,经常会给按钮加监听器,当点击这个按钮就会出发监听事件,调用onClick方法,本质是方法回掉。

在JavaWeb的Listener也是这么个原理,但是它监听的内容不同,它可以监听Application、Session、Request对象,当这些对象发生变化就会调用对应的监听方法。

应用域监听:
Ø ServletContext(监听Application)

¨ 生命周期监听:ServletContextListener,它有两个方法,一个在出生时调用,一个在死亡时调用;

¨ 属性监听:ServletContextAttributeListener,它有三个方法,一个在添加属性时调用,一个在替换属性时调用,最后一个是在移除属性时调用。

HttpSession(监听Session)

¨ 生命周期监听:HttpSessionListener,它有两个方法,一个在出生时调用,一个在死亡时调用;

¨ 属性监听:HttpSessioniAttributeListener,它有三个方法,一个在添加属性时调用,一个在替换属性时调用,最后一个是在移除属性时调用。

ServletRequest(监听Request)

¨ 生命周期监听:ServletRequestListener,它有两个方法,一个在出生时调用,一个在死亡时调用;

属性监听:ServletRequestAttributeListener,它有三个方法,一个在添加属性时调用,一个在替换属性时调用,最后一个是在移除属性时调用。

感知Session监听:
1:HttpSessionBindingListener监听
⑴在需要监听的实体类实现HttpSessionBindingListener接口
⑵重写valueBound()方法,这方法是在当该实体类被放到Session中时,触发该方法
⑶重写valueUnbound()方法,这方法是在当该实体类从Session中被移除时,触发该方法
2:HttpSessionActivationListener监听
⑴在需要监听的实体类实现HttpSessionActivationListener接口
⑵重写sessionWillPassivate()方法,这方法是在当该实体类被序列化时,触发该方法
⑶重写sessionDidActivate()方法,这方法是在当该实体类被反序列化时,触发该方法

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值