SSM常见面试知识点

整理不易,重在分享,欢迎转发收藏,助力面试上岸,学编程找宇哥

文章目录

Spring

1 Spring框架介绍

1、Spring是轻量级的开源的JavaEE框架
2、Spring可以解决企业应用开发的复杂性
3、Spring有两个核心部分,IOC和AOP
      IOC:控制反转,把创建对象的过程交给spring容器进行管理
      AOP:面向切面,不修改源代码的基础上进行功能的增强
4、Spring的特点
     方便解耦,简化开发
     支持AOP编程
     方便程序测试
     方便和其他框架进行整合
     方便进行事务操作
     降低API开发难度

2 什么是IOC,IOC原理

什么是IOC:
    控制反转:inversion of control
    把对象的创建和对象之间的调用都交给spring管理
    使用IOC的目的,为了降低耦合度
    入门案例就是IOC的实现

IOC的底层原理:
     底层技术:XML解析技术,设计模式,反射
     见图过程

在这里插入图片描述

3 IOC接口-BeanFactory

IOC思想基于IOC容器实现,IOC容器底层就是对象工厂
Spring提供IOC容器实现两种方式-两个接口
   BeanFactory:
     概念:是IOC容器的基本实现,是Spring内部的接口,
     	  一般不提供给开发人员使用
     特点:在加载配置文件的时候,不会创建对象,
     	  在获取对象或者使用对象的时候才会创建
   ApplicationContext:
     概念:是BeanFactory的子接口,提供了更多更强大的功能,
     	  一般开发人员使用
     特点:在加载配置文件的时候就会配置文件中的对象进行创建

4 什么是bean管理

什么是bean管理
    bean管理指的是两个操作:Spring创建对象和Spring注入属性
    Spring创建对象两种方式:基于xml配置文件和基于注解方式

5 IOC操作bean管理–基于xml方式创建对象

<bean id="user" class="cn.diautowried.User"></bean>
在Spring配置文件中:
	使用bean标签,标签里面添加对应属性,就可以实现对象的创建
    bean标签中的常用属性
           id属性:唯一标识,表示给对象其标识或者别名
           class属性:类的全路径(包类路径)
    创建对象的时候,默认使用的是无参构造

6 IOC操作bean管理–基于xml方式注入属性–set注入属性

在spring配置文件中配置对象创建,配置属性注入
创建book对象,set方法进行属性注入:如下
		property:表示属性的标签
		name:表示实体类中的属性名
		value:表示为属性名赋值

<bean id="book" class="cn.diautowried.Book">
 <!--在bean标签中使用property标签进行属性注入-->
   <property name="bookName" value="围城"></property>
   <property name="bookAauthor" value="钱钟书"></property>
</bean>

7 IOC操作bean管理–基于xml方式注入属性–有参构造注入属性

在spring配置文件中进行配置
使用有参构造注入属性:
	constructor-arg:构造-参数
	name:表示实体类中的属性名
	value:表示为属性名赋值

<bean id="order" class="cn.diautowried.Order">
	<constructor-arg name="orderName" value="华为手机"></constructor-arg>
	<constructor-arg name="orderAdddress" value="山西太原"></constructor-arg>
</bean>

8 IOC操作bean管理–基于xml方式注入属性–p标签注入属性

1 配置文件中添加p名称空间约束
	xmlns:p="http://www.springframework.org/schema/p"
	
2:进行属性注入,在bean标签中进行操作
<!--p名称空间注入 set注入简化版-->
<bean id="book1" class="cn.diautowried.Book" p:bookName="我们仨" p:bookAauthor="杨绛"></bean>

3 属性说明:
	p名称空间属性注入--set注入简化版本
	p:属性名=“属性值”

9 IOC操作bean管理–注入属性–外部bean

1 创建UserService 和 UserDao 对象
2 将UserDao注入到UserService中
	name属性值:类中的属性的名称
    ref属性值:是UserDao对象的bean标签的属性值

                          
<bean id="userService" class="cn.zzz.service.UserService">
     <property name="userDao" ref="userDaoImpl"></property>
</bean>

外部bean:
<bean id="userDaoImpl" class="cn.jdkproxy.UserDaoImpl"></bean>

10 IOC操作bean管理–注入属性–内部bean

1 创建Dept类 和 Emp类,一对多关系:部门和员工,
  一个部门有多个员工,一个员工属于一个部门
  将部门对象注入到员工员工对象中


<bean id="emp" class="cn.innertbean.Emp">
  <!--设置两个普通属性-->
  <property name="eName" value="Anny"></property>
  <property name="eGender" value=""></property>
   <!--设置对象属性-->
  <property name="dept">
       <bean id="dept" class="cn.innertbean.Dept">
           <property name="dName" value="开发部"></property>
       </bean>
   </property>
</bean>

11 IOC操作bean管理–内部bean的级联赋值

1 创建bean:emp

<bean id="emp" class="cn.innertbean.Emp">
   <!--设置两个普通属性-->
   <property name="eName" value="Jack"></property>
   <property name="eGender" value=""></property>
   <!--级联赋值-->
   <property name="dept" ref="dept"></property>
 </bean>
 
<!--创建bean dept-->
<bean id="dept" class="cn.innertbean.Dept">
   <property name="dName" value="理发部"></property>
</bean>

12 IOC操作bean管理–注入集合属性

3 配饰spring.xml

在spring配置文件中进行配置

<!--完成集合类型属性的注入-->
<bean id="stu" class="cn.pojo.Stu">

         <!--数组类型的属性注入-->
         <property name="courses">
               <array>
                   <value>java课程</value>
                   <value>数据库课程</value>
               </array>
         </property>

         <!--list类型属性注入-->
         <property name="list">
              <list>
                  <value>IDEA</value>
                  <value>Eclispse</value>
                  <value>HbuilderX</value>
              </list>
         </property>

         <!--map类型属性注入-->
         <property name="map">
              <map>
                 <entry key="mysql" value="免费"></entry>
                 <entry key="oracle" value="收费"></entry>
              </map>
          </property>

           <!--set类型属性注入-->
           <property name="set">
              <set>
              	<value>SpringAlibaba</value>	
              	<value>SpringCloud</value>
              </set>
          </property>
</bean>

13 IOC操作bean管理–在集合里面设置对象类型值

使用Stu和Course
配置spring的配置文件

需求:在集合list里面设置对象类型值
<bean id="stu" class="cn.pojo.Stu">
   <!--注入list集合类型,值是对象-->
   <property name="courseList">
        <list>
           <ref bean="course1"></ref>
           <ref bean="course2"></ref>
           <ref bean="course3"></ref>
        </list>
   </property>
</bean>

创建多个course对象:
<bean id="course1" class="cn.pojo.Course">
      <property name="courseName" value="前端课程"></property>
</bean>
<bean id="course2" class="cn.pojo.Course">
      <property name="courseName" value="后端课程"></property>
</bean>
<bean id="course3" class="cn.pojo.Course">
      <property name="courseName" value="数据库课程"></property>
</bean>

14 IOC操作bean管理-FactoryBean

1 Spring有两种类型的bean
    一种是普通bean
    一种是工厂bean,也就是FactoryBean
2 普通bean:在spring配置文件中定义bean的类型,
		返回类型就是此类型
  工厂bean:在spring配置文件中定义bean的类型,
  		返回类型可以不一致(可以返回其他类型)
3、工厂bean案例
   步骤1:创建类,设置这个类为工厂bean。实现接口FactoryBean
   步骤2:实现接口中的方法,在此方法中定义返回的bean的类型

15 IOC操作bean管理–bean的作用域

在spring中,设置创建bean实例是单实例还是多实例
   在默认情况下,spring容器创建的对象是单实例
   如何设置单实例和多实例:
   在spring配置文件中bean标签里有属性scope,用于设置单实例和多实例
      第一个值:默认值singleton,表示的是单实例对象
      第二个值:设置protoType。表示的是多实例对象
          <!--测试作用域 单实例 和 多实例-->
          <bean id="user1" class="cn.pojo.User" scope="singleton">
                <property name="username" value="张三"></property>
          </bean>
          <bean id="user2" class="cn.pojo.User" scope="prototype">
                <property name="username" value="张三"></property>
          </bean>
                   
singleton和protoType的区别:
       singleton是单实例对象,
        	设置scope的属性值是singleton,
        	单实例对象会在spring配置文件加载的时候就创建
       prototy是多实例对象
            设置scope的属性值是prototype,
            不是在加载spring配置文件的时候创建对象,
            而是在调用getBean方法的时候创建多实例对象

拓展:scope的其他属性值:
     request:将创建好的对象放在request域对象中,
              一次请求共享数据
     session:将创建好的对象放在session域对象中,
              一次会话共享数据

16 IOC操作bean管理–bean的生命周期

1、生命周期:
    从对象创建到对象销毁的过程
2、bean的生命周期---简化五步
    (1)、通过构造器创建bean实例,也就是执行无参构造
    (2)、为bean的属性设置值,以及对其他bean的调用
    	(调用set方法)
    (3)、调用bean的初始化方法,(初始化的方法需要进行配置)
    (4)、bean可以使用,对象可以获取
    (5)、当容器在关闭的时候,会调用bean的销毁方法
    	 (销毁的方法需要进行配置)

17 IOC操作bean管理–bean的生命周期的后置处理器

1 后置处理器介绍
	Bean的后置处理器的作用:
		Bean的设计模式用到了模板方法设计模式。
		在Bean的各个阶段都会有一些功能增强,
		这些功能都是由Bean后处理器提供的
		
	实现接口BeanPostProcessor,
	重写方法
		在初始化方法执行之前执行:
		postProcessBeforeInitialization
		在初始化方法执行之后执行:
		postProcessAfterInitialization

2 bean的生命周期---详细七步
     (1)、通过构造器创建bean实例,也就是执行无参构造
     (2)、为bean的属性设置值,以及对其他bean的调用
     		(调用set方法)
     (3)、把bean实例传递到bean后置处理器的方法
     	  即:postProcessBeforeInitialization()
     (4)、调用bean的初始化方法,(初始化的方法需要进行配置)
     (5)、把bean实例传递到bean后置处理器的方法
     	  即:postProcessAfterInitialization()
     (6)、bean可以使用,对象可以获取
	 (7)、当容器在关闭的时候,会调用bean的销毁方法
	 		(销毁的方法需要进行配置)

18 IOC操作bean管理-自动装配

1、什么是自动装配?什么是手动装配
	手动装配:
		是程序员在配置文件中手动的进行属性的赋值,
		即value属性值和ref属性值
	自动装配:
		根据指定的装配规则,根据属性名称或者属性类型,
		Spring自动将匹配的属性值进行注入

在这里插入图片描述

19 常用创建bean对象的注解

 1、什么是注解
		注解是代码的特殊标记:
			格式:@注解名(属性名=属性值,属性名=属性值)
		使用注解:注解作用在类上面,方法上面,属性上面
		使用注解的目的:简化xml设置
		
2、spring针对Bean管理中创建对象提供注解
        @Component:笔试普通的bean
        @Service:表示业务层
        @Controller:表示控制层
        @Repository:表示持久层
        
     以上四个注解功能是一样的,都可以用来创建bean实例
     注解中的value值可以自定义,相当于bean标签中的id属性值
		<bean id="userService" class=""/>
		value值可以省略不写,默认值是类名称的首字母小写

20 基于注解方式–实现属性注入

@AutoWired :根据属性类型进行注入
   @Autowired注解是按照类型(byType)装配依赖对象,
   默认情况下它要求依赖对象必须存在,
   	如果允许 null 值,可以设置它的required 属性为 false。

@Qualifier:根据属性名称进行注入
	在@AutoWired注解通过类型注入的前提下,
		如果被注入的接口有多个实现类对象,
			那么就可以按照名称(byName)来装配,
			可以结合@Qualifier 注解一起使用
		如果不使用@Qualifier注解进行value属性的标识,
			那么容器会为此接口的多个实现类创建bean对象
			此时,容器不知道改注入哪个实现类的bean对象,
			会报异常
			NoUniqueBeanDefinitionException: 
			No qualifying bean of type 'cn.anno.dao.UserDao'
			 available: expected single matching bean 
			 but found 3: userDaoImpl1,userDaoImpl2,userDaoImpl3


@Resource: 可以根据类型注入,可以根据名称注入
	@Resource不是spring的注解,而是javax的注解。
	@Resource默认按照byName自动注入
	@Resource 有两个重要的属性:name和type
        Spring将@Resource注解的:
        		name属性解析为bean的名字,
        	    type属性则解析为bean的类型。
        	    
		如果使用 name 属性,则使用 byName 的自动注入策略,
		如果使用 type 属性时则使用 byType 自动注入策略。
		如果既不制定 name 也不制定 type 属性,
			这时将通过反射机制使用 byName 自动注入策略。

21 @Value 注入普通类型属性

@Value注解:
		 注入普通类型属性,为普通属性赋值
		 属性value的值,会赋值给name
		 
          @Value(value = "武磊")
          private String name;

22 基于注解方式–实现完全注解开发

1 创建配置类,替代spring.xml文件
	@Configuration:注解 
			表示标识此类是配置类
	@ComponentScan("cn.spring")注解:
			表示开启注解扫描
			括号中的值,表示开启扫描此包下的所有注解
			
@Configuration//作为配置类。替代xml核心配置文件
@ComponentScan("cn.spring")
public class SpringConfig {}
2 编写测试类
	
	@Test
    public void test1(){
        //1、创建核心容器,加载配置类
        AnnotationConfigApplicationContext ccontex =
                new AnnotationConfigApplicationContext(SpringConfig.class);
        //2、通过容器获取bean对象
        UserService userService = ccontex.getBean("userService", UserService.class);
        //3、调用方法
        userService.add();
        System.out.println(userService);
    }

23 什么是AOP(概念)

什么是AOP(概念)
	官方:面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离
		 从而使得业务逻辑各部分之间的耦合度降低,
		 提高程序的可重用性,同时提高开发效率
    通俗:不修改源代码的方式,在主干功能里添加新的功能

在这里插入图片描述

24 AOP底层使用动态代理,有两种情况的动态代理

AOP底层使用动态代理,有两种情况的动态代理
  第一种:有接口情况--使用JDK动态代理
         创建接口实现类的代理对象,增强类的方法
  第二种:没有接口情况--使用CGLIB动态代理
         创建子类的代理对象,增强类的方法

JDK动态代理在这里插入图片描述
CGLIB动态代理
在这里插入图片描述

25 JDK动态代理和CGLIB动态代理的区别

JDK动态代理和CGLIB动态代理的区别

概念:
   JDK代理:使用的是反射机制生成一个实现代理接口的匿名类,
           在调用具体方法前调用InvokeHandler来处理。
   CGLIB代理:使用字节码处理框架asm,
   			 对代理对象类的class文件加载进来,
   			 通过修改字节码生成子类。
   			 
效率:
     JDK创建代理对象效率较高,执行效率较低;
     CGLIB创建代理对象效率较低,执行效率高。
     
关系:
   JDK动态代理机制是委托机制,只能对实现接口的类生成代理,
   		通过反射动态实现接口类;
   CGLIB则使用的继承机制,针对类实现代理,
   		被代理类和代理类是继承关系,
   		所以代理类是可以赋值给被代理类的,
   		因为是继承机制,不能代理final修饰的类。
   		
实现:
    JDK代理是不需要依赖第三方的库,只要JDK环境就可以进行代理,
    	需要满足以下要求:
         1.实现InvocationHandler接口,重写invoke()
         2.使用Proxy.newProxyInstance()产生代理对象
         3.被代理的对象必须要实现接口
    CGLIB 必须依赖于CGLIB的类库,需要满足以下要求:
         1.实现MethodInterceptor接口,重写intercept()
         2.使用Enhancer对象.create()产生代理对象
         
使用场景:
	1)如果目标对象实现了接口,
		默认情况下会采用JDK的动态代理实现AOP,
		可以强制使用CGLIB实现AOP
	2)如果目标对象没有实现了接口,
		必须采用CGLIB库,
		spring会自动在JDK动态代理和CGLIB之间转换

26 AOP的相关术语

1 AOP的先关术语 
	连接点:类里面的那些方法可以增强,这些方法就被成为连接点
	切入点:    实际被增强的方法,称为切入点
	通知(增强):
          实际增强的逻辑部分,被称为通知(增强)
          通知有多种昂类型:5种类
                           前置通知,
                           后置通知、
                           环绕通知,
                           异常通知,
                           最终通知:
    切面:把通知应用到切入点的过程,就是切面

在这里插入图片描述

27 Spring和AspectJ的关系

Spring和AspectJ的关系
       1、Spring框架一般基于AspectJ实现AOP操作
       2、什么是AspectJ
          Aspect不是Spring的组成部分,独立的AOP框架,
          一般把Aspect和Spring框架一起使用,进行AOP操作

28 切入点表达式作用和语法

切入点表达式
     切入点表达式的作用:知道对哪个类里面的哪个方法进行增强
     切入点表达式语法:
 	 execution([权限修饰符][返回类型][类的路径][方法名称][参数列表])
       举例1:对cn.aop.zzy包中的UserDao类里面的add方法做增强
             execution(* cn.aop.zzy.UserDao.add(..))
       举例2:对cn.aop.zzy包中的UserDao类里面的所有方法做增强
             execution(* cn.aop.zzy.UserDao.*(..))
       举例3:对cn.aop.zzy包中的所有类,以及里面的所有方法做增强
             execution(* cn.aop.zzy.*.*(..))

29 通知的执行顺序

目标方法无异常时,通知的执行顺序:
	around 前。。。目标方法执行之前执行
	before。。。在目标方法执行之前执行
	我是目标方法,也就是被增强的方法。。。。我执行了
	around 后。。目标方法执行之后执行
	after。。。在目标方法执行之后执行
	afterReturning。。。在目标方法执行返回之后执行

目标方法发生异常时,通知的执行顺序:
	around 前。。。目标方法执行之前执行
	before。。。在目标方法执行之前执行
	after。。。在目标方法执行之后执行
	afterThrowing。。。在目标方法执行发生异常执行

30 通知的优先级

@Order(value = 3):
	设置增强的优先级(同一个目标方法有多个增强的情况)
	在增强类上面添加注解@Order(数字类型值),
		数字类型值越小,优先级越高

31 什么是事务

什么是事务
	事务是数据库操作的最基本的单元,
		是逻辑上的一组操作,要么都成功,
		如果有一个失败,所有操作都失败
		
	典型场景。银行转账

32 事务的四大特性ACID

事务的四个也行ACID
	原子性:
		原子性意味着数据库中的事务执行是作为原子。
		即不可再分,整个语句要么执行,要么不执行
	一致性:
		一致性即在事务开始之前和事务结束以后,
		数据库的完整性约束没有被破坏。
	隔离性:
		事务的执行是互不干扰的,
		一个事务不可能看到其他事务运行时,中间某一时刻的数据
	持久性:
		意味着在事务完成以后,
		该事务所对数据库所作的更改便持久的保存在数据库之中,
		并不会被回滚

33 Spring事务管理操作

事务添加到JavaEE三层架构里的Service层,也就是业务逻辑层

在Spring中进行事务管理--两种方式
        编程时事务:不建议使用,代码臃肿,不易维护
        声明式事务:使用方便
        
声明式事务:
	1、基于注解方式
	2、基于xml配置文件方式
	3、在Spring进行声明式事务管理。底层用到了AOP原理
	4、提供可一个接口,代表事务管理器,
		这个接口针对不同的框架提供了不同的实现类

34 Spring声明式事务–注解方式–@Transactional

步骤1、在Spring配置文件中引入事务的名称空间tx
      xmlns:tx="http://www.springframework.org/schema/tx"
      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
      
步骤2、在Spring配置文件中配置事务管理器,并且注入数据源
     <!--创建事务管理器-->
      <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!--指定数据源-->
            <property name="dataSource" ref="dataSource"></property>
      </bean>
      
步骤3:在配置文件中开启事务注解
       <!--开启事务注解 指明事务管理器-->
       <tx:annotation-driven transaction-manager="dataSourceTransactionManager"></tx:annotation-driven>

步骤4、在service类上或者方法上添加事务注解--@Transactional
      1、@Transactional,这个注解可以添加到类上面,也可以添加到方法上面
      2、如果把这个注解添加到类上面,这个类里面的所有方法都添加事务
      3、如果把这个方法添加到方法上,只为这个方法添加事务
      
           @Service
           @Transactional
           public class AccountService {
                  @Autowired
                  private AccountDao accountDao;

                  //转账的方法
                  public void accountMoney(){
                        //luncy少100
                        accountDao.reduceMoney();
                        //模拟意向
                        int a = 1/0;
                        //maey多100
                        accountDao.addMoney();
                   }
		    }

步骤5、测试结果
      测试过程中发生了异常。事务的提交失败,
      那么事务就会进行回滚,数据库中的lucy和mary的money值,
      没有发生变化

35 @Transactional注解事务管理的参数配置

@Transactional注解源码

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
     @AliasFor("transactionManager")
     String value() default "";
     @AliasFor("value")
     String transactionManager() default "";
     Propagation propagation() default Propagation.REQUIRED;
     Isolation isolation() default Isolation.DEFAULT;
     int timeout() default -1;
     boolean readOnly() default false;
     Class<? extends Throwable>[] rollbackFor() default {};
     String[] rollbackForClassName() default {};
     Class<? extends Throwable>[] noRollbackFor() default {};
     String[] noRollbackForClassName() default {};
}

属性1:propagation。事务的传播行为
	多事务方法直接进行调用,这个过程中的事务管理有七种方式,
	前两种行为常用:REQUIRED和REQUIRED_NEW
	@Transactional(propagation = Propagation.REQUIRED)
	
属性2:ioslation、事务的隔离级别
	在多事务操作之间不会互相产生影响,
	不考虑隔离会产生很多问题,问题如下:
		问题:
	        脏读:一个未提交的事务读取到了另一个未提交事务的数据
	        幻读:一个事务读取到另一个提交事务的添加数据
	        不可重复读:一个未提交的事务读取到了另一个已提交事务修改的数据
		解决方案:
            通过设置事务的隔离级别,就能解决读的问题
                 读未提交:
                 读已提交
                 可重复读:
                 串行化:
                 //事务                      事务的隔离级别:可重复度
                 @Transactional(isolation = Isolation.REPEATABLE_READ)
                 
属性3:timeou。超时时间
        事务需要在一定的时间内提交,如果超过设定时间,事务就会回滚
        默认值是-1.设置时间以单位秒计算
属性4:readOnly、是否只读
        读:表示查询,写:表示增删改
        readOnly默认值是false,表示可以增删改查
        设置readOnly值是true,只能进行查询操作
属性5:rollbackFor、回滚
        设置出现纳西异常,进行回滚操作
属性6:norollbackFor、不回滚
        设置出现哪些异常,不进行回滚操作

36 事务的隔离级别作用

数据库事务的隔离级别有 4 个,由低到高依次为 
	Read uncommitted (读未提交)、 
	Read committed( 读提交 ) 、 
	Repeatable read (重复读)、 
	Serializable( 序列化 )。
	这四个级别可以逐个解决脏读、不可重复读、幻读这几 类问题

在这里插入图片描述

37 事务的传播行为

事务传播行为(propagation behavior)指的就是:
	当一个事务方法被另一个事务方法调用时,
	这个事务方法应该如何进行。
	
	例如:methodA事务方法调用methodB事务方法时,
		 methodB是继续在调用者methodA的事务中运行呢,
		 还是为自己开启一个新事务运行,
		 这就是由methodB的事务传播行为决定的。

在这里插入图片描述

SpringMVC

1 什么是MVC

MVC是一种软件架构的思维,将软件按照模型,视图,控制器来换分

M:Model,模型层,指工程中的JavaBean,作用是处理数据
	JavaBean分为两类:
		一类成为实体类Bean,专门存储数据业务,如POJO实体类
		一类成为业务处理Bean,指Service或者Dao对象,专门用于处理业务逻辑或和数据访问
V:View,视图层,指工程中的html或者jsp页面,作用是与用户进行交互,展示数据

C:Controller,控制层,指工程中的Servlet,作用是接收请求和响应浏览器

MVC的工作流程:
	用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,
	Controller调用相应的Model层处理请求,处理完毕将结果返回给Controller。
	Controller再根据请求的结果找到相应的View视图,渲染数据后最终响应给浏览器

2 什么是Spring MVC

SpringMVC是Spring的一个后续产品,,是Spring的一个子项目
SpringMVC是Spring为表述层开发提供的一套完备的解决方案,
目前普遍使用SpringMVC作为javaEE项目表述层的首选方案
备注:三层架构分为
			表述层,表述层是表示前台页面和后台servlet
			业务逻辑层,
			数据访问层,

3 Spring MVC的特点

SpringMVC是Spring框架的一个子项目,
	与IOC容器无缝对接,基于原生的Servlet,
	通过功能强大的前端控制器DispatcherServlet,
	对请求和响应进行统一处理
代码清晰简洁,大幅度提升开发效率
内部组件化程度高,可插拔式组件,即插即用
性能卓著,尤其适合大型互联网项目的要求

4 请求的处理和响应流程

1 浏览器发送请求,若请求地址符合前端控制器的url-pattern,
2 该请求会被前端控制器DispatcherServlet处理,
3 前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器
4 将请求地址和控制器中的@RequestMapping的value属性值进行匹配
5 若匹配成功,该注解所标识的控制器方法就是处理请求的方法
6 处理请求的方法需要返回一个字符串类型的视图名称,
7 该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,
8 通过thymeleaf对视图进行渲染,最终转发到视图所对应的页面

5 @RequestMapping注解的功能

从注解的名称上可以看到,@RequestMapping注解的作用是:
	将请求和处理请求的控制器方法关联起来,建立映射关系。
	SpringMVC接收到指定的请求,
	就回来找到在映射关系中对应的控制器方法来处理这个请求

6 @RequestMapping注解的位置

@RequestMapping标识一个类,设置映射请求的请求路径的初始信息
@RequestMapping标识一个方法,设置映射请求路径的具体信息

7 thymeleaf中的@{}的含义:

thymeleaf中的@{}:表示获取应用的上下文路径
	等同于Jsp 的老方法:
	${pageContext.request.contextPath} = @{}
	
	jsp中获取上下文路径拼接资源路径:
		th:action= "${pageContext.request.contextPath}/upload
	thymeleaf中获取上下文路径拼接资源路径:
		th:action= "@{/upload}

8 为什么 Tomcat 运行时默认打开 index.html,或者 index.jsp

Tomcat 安装目录下的 conf\web.xml 包含所有项目的配置信息
	如果 IDEA 中的 web.xml 没有相关的配置信息,
	就运行 Tomcat 安装目录下的 conf\web.xml。
	<welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

	如果需要修改模块的欢迎页面也是可以的,
	只需要在当前模块的 web.xml 文件配置即可
	<welcome-file-list>
        <welcome-file>love.html</welcome-file>
    </welcome-file-list>

9 @RequestMapping注解的value属性

@RequestMapping注解的value属性:
	通过请求的请求地址匹配请求映射
	是一个字符串类型的数组,
		表示该请求映射能够匹配多个请求地址所对应的请求
		必须设置,至少通过请求地址匹配请求映射

10 @RequestMapping注解的method属性

@RequestMapping注解的method属性:
	通过请求的请求方式(GET/POST)匹配请求映射
	是一个RequestMethod类型的数组,
		表示请求映射能能够匹配多种请求方式的请求
	若当前请求的请求地址满足请求映射的value属性,
	但是请求方式不满足method属性,
		则浏览器报405.Request method ‘POST’ not support
	method中可以声明多个请求方式,
            在满足value属性表的前提下,
            只要满足任意一种请求方式即可

11 处理指定请求方式的控制器映射

SpringMVC中提供了@RequestMapping的派生注解
  	处理GET请求的映射:GetMapping
  	处理POST请求的映射:PostMapping
  	处理PUT请求的映射:PutMapping
  	处理DELETE请求的映射:DeleteMapping
  	
常用的请求方式有get post put delete:
	但目前浏览器只支持get和post,若在form表单提交时,
	为method设置了其他请求方式的字符串,put或者delete,
	则按照默认的GET请求方式处理
	
	若要发送put和delete请求,
	则需要通过spring提供的过滤器HiddenHttpMethodFilter

12 @RequestMapping注解的params属性

@RequestMapping注解的headers属性通过请求的请求参数匹配请求映射
@RequestMapping注解的headers属性是一个字符串类型的数组,
	可以通过四种表达式设置请求参数和请求映射的匹配关系
	
‘param’:要求请求映射所匹配的请求必须携带param请求参数
‘!param’:要求请求映射所匹配的请求不能携带param请求参数
‘param=value’:要求请求映射所匹配的请求必须携带param请求参数,且param=value
‘param!=value’:要求请求映射所匹配的请求必须携带param请求参数,但是param!=value

13 @RequestMapping注解的headers属性

@RequestMapping注解的headers属性:
	通过请求的请求头信息匹配请求映射
	是一个字符串类型的数组,
	
可以通过四种表达式设置请求头信息和请求映射的匹配关系
‘header’,
	要求请求映射所匹配的请求必须携带header请求头信息
‘!header’:
	要求请求映射所匹配的请求不能携带header请求头信息
‘header=value’:
	要求请求映射所匹配的请求必须携带header请求头信息,
	且header=value
‘header!=value’:
	要求请求映射所匹配的请求必须携带header请求头信息,
	且header!=value
	
若当前请求满足@RequestMapping和value和method属性,
但不满足headers属性,此时页面报404错误,即资源未找到

14 SpringMVC支持ant风格的路径

? 表示任意的单个字符
* 表示任意的0个或者多个字符
** 表示任意的一层或多层目录
注意:在使用**时,只能使用/**/xxx方式
	 注解中不允许在**前后添加内容

15 SpringMVC支持路径中的占位符

原始方式:/deleteById?id=1
rest方式:/deleteById/1

SpringMVC路径中的占位符常用语restful风格中,
	当请求路径中将某些数据通过路径的方式传输到服务器中,
	就可以在相应的@RequestMapping注解的value属性中,
	通过占位符{xxx}表示传输的数据,
	再通过@PathVariable注解,
	将占位符所表示的数据赋值给控制器方法的形参
	
注意:路径中的占位符名称必须和@PathVariable中的value属性值一致

16 SpringMVC获取请求参数的方式

16.1 通过servletAPI获取请求参数

1 通过servletAPI获取请求参数
	将HttpServletRequest作为控制器方法的形参,
	此时HttpServletRequest类型的参数表示封装了当前请求报文的对象

16.2 通过控制器方法的形参获取请求参数

2 通过控制器方法的形参获取请求参数
	在控制器方法的形参位置,设置和请求参数同名的形参,
	当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中。
	就会将请求参数赋值给相应的形参
	注:
	若请求所传输的请求参数中有多个同名的请求参数,
	此时可以在控制器方法的形参中:
	设置字符串数组或者字符串类型的形参接收此请求参数

16.3 @RequestParam创建映射关系

	
3 @RequestParam创建映射关系
	@RequestParam是将请求参数与控制器方法的形参创建映射关系
	@RequestParam注解一共有三个属性:
		value:指定为形参赋值的请求参数的参数名
		required 设置是否必须传输此请求参数,默认值为true
			若设置为true时,则当前请求必须传递value所指定的
				请求参数,若没有传递该请求参数,且没有设置defaultValue属性,
				则页面报400错误,
			若设置为false,则当前请求不是必须传输value所指定的请求参数
				若没有传输,则注解所表示的形参为null
		defaultValue属性:
			不管required属性值为true或false,
			当value所指定的请求参数没有传输时,
			则使用默认值为形参赋值

16.4 @RequestHeader

4 @RequestHeader创建映射关系
	@RequestHeader是将请求头信息和控制器方法的形参创建映射关系
	@RequestHeader注解:
		一共有三个属性:value required defaultValue
		用法同@RequestParam类似


16.5 @CookieValue

5 @CookieValue
	@CookieValue是将请求头信息和控制器方法的形参创建映射关系
	@CookieValue注解:
		一共有三个属性:value required defaultValue
		用法同@RequestParam类似

16.6 通过POJO获取请求参数

6 通过POJO获取请求参数
	可以在控制器方法的形参位置设置一个实体类型的参数,
	此时若浏览器传输的请求参数的参数名和实体类中的属性名一致
	那么请求参数就会为此属性赋值

17 解决获取请求参数的乱码问题

解决请求参数的乱码问题,可以使用SpringMVC提供的编码过滤器
CharacterEncodingFilter,但是必须在web.xml中配置

注:
	SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前,
	否则无效

在web.xml中配置过滤器,在servlet初始化之前设置好编码格式。
web容器三大组件:listener 、filter、servlet
	其中listener负责监听容器
		filter负责过滤资源
		servlet负责处理请求响应数据
	其先后顺序是:listener 、filter、srvlet

注:tomcat的server.xml中配置的URIEncoding=UTF-8,
	只针对GET请求的字符编码生效,
	对于POST请求不起作用,顾需要上述在web.xml中的操作

<!--配置过滤器-->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

18 向request域对象共享数据

18.1 使用servletAPI向request域对象共享数据

1 使用servletAPI向request域对象共享数据
	request域对象中存放键值对,页面通过${键的名称}获取对应的值
	<p th:text="${username}"></p>

18.2 使用ModelAndView向request域对象共享数据


2 使用ModelAndView向request域对象共享数据
	ModelAndView中的model和view的功能
	Model主要向请求域中共享数据
	View主要用于设置视图,实现页面跳转

18.3 使用Model向request域对象共享数据

3 使用Model向request域对象共享数据
	 model.addAttribute("model","hello model");

18.4 使用Map向request域对象共享数据

4 使用Map向request域对象共享数据
	 map.put("map", "hello map");


18.5 使用ModelMap向request域对象共享数据


5 使用ModelMap向request域对象共享数据
	 modelMap.addAttribute("modelMap", "hello modelMap");

19 Model、Map、ModelMap的区别

Model、Map、ModelMap类型的参数:
	其实本质上都是BindingAwareModelMap

public interface Model 
public class ModelMap extends LinkedHashMap<String, Object> 
public class LinkedHashMap<K,V> extends HashMap<K,Vi> mplements Map<K,V>
public class ExtendedModelMap extends ModelMap implements Model 
public class BindingAwareModelMap extends ExtendedModelMap

20 向session域共享数据

HttpSession session = request.getSession();
session.setAttribute("name", "hello session");

21 向application域共享数据(也是就是ServletContext上下文)

ServletContext context = 
		request.getSession().getServletContext();
context.setAttribute("name", "hello app");

22 SpringMVC的视图

SpringMVC中的视图是view接口,
	视图的作用是:
		渲染数据,将模型Model中的数据展示给用户。
	SpringMVC视图的种类很多,默认有转发视图和重定向视图
	当工程引入jstl依赖,转发视图hi自动转换为jstlView

	若使用的视图技术是Thymeleaf,
	在SpringMVC的配置文件中配置了视图解析器,
	由此视图解析器解析之后所得到的是ThymeleafView

23 模板视图:ThymeleafView

当控制器方法中所设置的视图的视图名没有任何前缀时,
此时的视图名会被SpringMVC配置文件中配置的视图解析器所解析,
视图名称拼接视图前缀和视图后缀所得到的的最终路径,
会通过转发的形式实现跳转、

24 转发视图:internalResourceView

SpringMVC中默认的转发视图是internalResourceView
SpringMVC中创建转发视图的情况:
当控制器方法所设置的视图名称以forward为前缀时,创建internalResourceView视图,
此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,
而是会将前缀forward去掉,剩余部分作为最终版路径通过转发的方式实现跳转

25 重定向视图:RedirectView

SpringMVC中默认的重定向视图是RedirectView
当控制器方法中所设置的视图名称是以“ redirect ”为前缀时,创建RedirectView视图,
此时的视图名称不会被SpringMVC配置文件中的视图解析器解析,
而是会将前缀redirect去掉,剩余的部分作为最终路径通过重定向的方式实现跳转

26 转发和重定向的区别

转发:服务器端的行为
request.getRequestDispatcher().forward(request,respond)
转发在服务器端发挥作用,
通过forward()方法提交信息在多个页面之间进行传递。

转发的特点是:
1.地址栏不会改变
2.转发只能转发到当前Web应用内的资源
3.在转发过程中,可以将数据保存到request域对象当中去
4.转发只有一次请求
5.转发是服务器端行为

转发的过程:
1.客户端浏览器发送http
2.web浏览器接收请求
3.调用内部的一个方法在容器内部完成请求处理和转发工作

需注意的是:转发的路径必须是同一个web容器下的url。
在客户端浏览器路径栏显示的仍然是第一次访问的路径。
转发行为是浏览器只做了一次访问请求。


重定向:客户端的行为
resp.sendRedirect(" ")

重定向的特点:
1.重定向地址栏会改变
2.重定向可以跳转到当前web应用,甚至是外部域名的网站。
3.不能在重定向的过程中,将数据保存到request域对象中。

重定向的过程:
1.客户端浏览器发送http请求
2.web服务器接收后,发送302状态码响应,
	并且发送location给客户端服务器。
3.客户端服务器发现是302响应后,则自动发送一个http请求,
	请求url为重定向的地址,响应的服务器根据此请求
	寻找资源并发送给客户。


转发和重定向的区别;
(1)请求转发是一次请求一次响应,而重定向是两次请求两次响应;
(2)请求转发地址栏不会改变,而重定向地址栏会显示第二次请求的地址;
(3)请求转发只能转发给本项目的其他资源,
	而重定向不仅可以重定向到本项目的其他资源, 
	还可以重定向到其他项目;
(4)请求转发是服务器端的行为,转发时只需要给出转发的资源路径即可,
	如Servlet的访问路径;而重定向需要给出全路径,
	即路径要包含项目名;
(5)请求转发比重定向的效率高,因为请求转发是一个请求。
	在以后的开发中,如果需要地址栏的地址发生改变,
	就选择重定向;如果需要在Servlet之间通过request域进行数据通信,
	就选择请求转发、

27 视图控制器 view-controller

当控制器方法中,仅仅用来实现页面跳转,即需要设置视图名称时,
可以将处理器方法使用view-controller标签进行展示

通过SpringMVC的核心配置文件中配置视图控制器,代替访问首页的功能
前提是必须在开启注解驱动的条件下使用,否则控制器中的方法映射均失效
	path:设置处理的请求地址
	view-name:设置请求地址所对应的视图名称

当SpringMVC中设置任何一个view-controller时,
其他控制器中的请求映射将全部失效,
此时需要在SpringMVC核心配置文件中设置开启mvc注解驱动标签:
<mvc:annotation-driven/>


<!--配置视图控制器 需要配合注解驱动的配置使用-->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
<!--k开启注解驱动-->
<mvc:annotation-driven/>

28 RESTFul间介

REST:即Representational State Transfer ,
	 表现层资源状态转移

资源:
	资源是一种看待服务器的方式
资源的表述:
	资源的表述是对资源在某个特定时刻的状态描述,
	可以在客户端-服务端之间转移交换,资源的表述可以有多种格式,
	例如HTML/XML/JSON/纯文本/图片/音频/视频。
	资源的表述格式可以通过协商机制来确定,
	请求-响应方向的表述通常使用不同的形式。

状态转移:
	是指在客户端和服务端之间转移代表资源状态的表述,
	通过转移和操作资源的表述,来间接实现操作资源的目的

30 RESTFul的实现

具体来说就是HTTP协议里面,四个表示操作方式的动词,GET PUT DELETE POST
它们分别对应四种操作:
	GET 用来获取资源
	PUT 用来更新资源
	DELETE 用来删除资源
	POST 用来新建资源
	
REST风格提倡URL地址使用统一的风格设计,
	从前到后各个单词使用斜杠分开
	不使用问号键值对方式携带请求参数,
	而是将要发送给服务器的数据作为URL地址的一部分,
	以保证整体风格的一致性。

31 HiddenHttpMethodFilter

由于浏览器只支持发送POST和GET请求,针对PUT和DELETE请求,
SpringMVC提供了HiddenHttpMethodFilter帮助我们将POST请求转化成DELETE或者PUT请求
HiddenHttpMethodFilter处理DELETE和PUT请求的条件:
	当前请求的方式必须是POST请求
	当前请求必须传输请求参数_method
	<input type="hidden" name="_method" value="put">
    <form th:action="@{/user}" method="post">
        <input type="hidden" name="_method" value="put">
        用户名:<input type="text" name="username"><br>
        密码:<input type="password" name="password"><br>
        提交:<input type="submit" name="修改用户信息学"><br>
    </form>

32 HttpMessageConverter

HttpMessageConverter:报文信息转换器
作用:将请求报文转换为Java对象,将Java对象转换为响应报文
HttpMessageConverter 提供了两个注解和两个类型:
	@RequestBody	@ResponseBody
	RequestEntity	ResponseEntity	

32.1 @RequestBody

@RequestBody 可以获取请求体,需要在控制器方法设置一个形参,
	使用@RequestBody注解进行标识,
	当前请求的请求体将会为当前注解所标识的形参赋值

32.2 RequestEntity

RequestEntity 封装请求报文的的一种类型,
需要在控制器方法的形参中设置该类型的对象,
当前请求的请求报文就会赋值给该形参
可以通过getHeaders()获取请求头信息
通过getBody()获取请求体信息

32.3 @ResponseBody

@ResponseBody 用于标识一个控制器方法
可以将该方法的返回值直接作为响应报文的响应体响应到浏览器

32,4 ResponseEntity

ResponseEntity用于控制器方法的返回值类型,
该控制器方法的返回值就是响应到浏览器的响应报文

33 @RestController注解

@RestController是SpringMVC提供的一复合注解
标识在控制器的类上,就相当于为类添加了@Controller注解,
并且为其中的每个方法添加了@ResponseBody注解

34 拦截器的配置

SpringMVC中的拦截器用于拦截控制器方法的执行
SpringMVC中的拦截器需要实现HandlerInterceptor
SpringMVC中的拦截器必须在SpringMVC的配置文件中进行配置

核心配置文件中配置:

以下配置方式可以通过ref或bean标签设置拦截器
通过mvc:mapping设置需要拦截的请求,
通过mvc:exclude-mapping设置需要排除的请求,即需要放行的请求
其中:
	对所有路径进行拦截 
	/*表示拦截上下文路径下的一层目录的请求,
	/**表示拦截多层目录的请求

35 拦截器的三个抽象方法

SpringMVC中的拦截器有三个抽象方法:
1 preHandle:
	控制器方法执行之前执行preHandle(),
	其boolean类型的返回值表示是否拦截或者放行,
	返回true为放行,即调用控制器方法
	返回false表示拦截,即不调用控制器方法
2 postHandle:
	在控制器方法执行之后执行postHandle()
3 afterComplation:
	处理完视图和模型数据即渲染完视图,
	之后执行afterComplation()

36 多个拦截器的执行顺序

1 若每个拦截器preHandle()都返回true
	此时多个拦截器的执行顺序,
	与拦截器在SpringMVC的配置文件中的配置顺序有关
	preHandle()会按照配置的顺序执行,
	而postHandle()afterComplation()会按照配置的反序执行
	
2 若某个拦截器的preHandle()返回了false
	preHandle()返回false和之前的拦截器的preHandle()都执行
	postHandle()不会执行,
	返回false的拦截器之前的afterComplation()会执行

37 SpringMVC常用组件

1 DispatcherServlet 前端控制器
	不需要工程师开发,由框架提供
	作用:统一处理请求和响应,
		 整个流程控制的中心,
		 由它调用其它组件来处理用户的请求

2 HandlerMapping 处理器映射器
	不需要工程师开发,由框架提供
	作用:根据请求的url,method等信息查找handler,
		 即控制器方法

3 Handler 处理器
	需要工程师开发
	作用:在DispatcherServlet的控制下,
		 Handler对具体的用户请求进行处理

4 HandlerAdapter 处理器适配器
	不需要工程师开发,由框架提供
	作用:通过HandlerAdapter对处理器,即控制器方法进行执行

5 ViewResolver 视图解析器
	不需要工程师开发,由框架提供
	作用:进行视图解析,得到相应的视图,
		 如:ThymeleafViewResolver  模板视图解析器
		 	InternalResourceView 转发视图解析器
		 	RedirectView 重定向视图解析器

6 View 视图
	不需要工程师开发,由框架或者视图技术提供
	作用:将模型数据通过页面展示给用户

38 DispatcherServlet的执行流程

1 用户向服务器发送请求,
	请求被SpringMVC的前端控制器DispatcherServlet获取
2 DispatcherServlet对请求URL进行解析,
	得到请求资源标识符URI,判断请求URI对应的映射
	不存在:再判断是否配置了mvc:default-servlet-handler
		如果没有配置,则控制台报错映射差找不到,客户端404错误
		如果有配置,则访问目标资源一般是静态资源:js/css/html
					找不到客户端也会展示404错误
	存在则执行以下流程
3 根据该URL,
	调用HandlerMapping获取该Handler配置的所有相关对象,
	包括Handler对象以及Handler对应的拦截器。
	最后以HandlerExecutionChain执行链对象的形式返回
4 DispatcherServlet根据获得的Handler,
	选择一个合适的HandlerAdapter
5 如果成功获得HandlerAdapter,
	此时将开始执行拦截器的preHandler(...)方法(正向)
6 提取Request中的模型数据,填充Handler入参,
	开始执行Handler方法(controller方法),处理请求
	在填充Handler的入参过程中,根据配置Spring将会做如下工作:
	a、HtppMessageConveter,
		将请求消息(json或xml)转换成一个对象,
		将对象转换成指定的响应信息
	b、数据转换:对请求头信息进行数据转换,如String转换成Integer
	c、数据格式化,对请求消息进行数据格式化。
		如将字符串换换成格式化数字或者格式化日期等
	d、数据验证:验证数据的有效性,如长度,格式,
		验证结果存储到BindingResult或error中
7 Handlerz还行完成后,向DispatcherServlet返回一个ModelAndView
8 此时将开始执行拦截器的postHandler(...)方法(逆向)
9 根据返回的ModelAndView,
	(但是此时会判断是否发生异常,如果存在异常,
	则执行HandlerExceptionResolver进行异常处理)
	选择一个合适的ViewResolver进行视图解析
	根据Model和View,来渲染视图。
10 渲染视图完毕执行拦截器的afterComplation(...)方法.(逆向)
11 将渲染结果返回给客户端				

MyBatis

1 MyBatis历史

MyBatis最初是Apache的一个开源项目iBatis。
	之后迁移到Goole Code,
iBatis3.x正式更名为MyBatis,代码之后迁移到Github。
iBatis是一个基于Java的持久层框架,
	iBatis提供的持久层框架包括:
	SQL、Maps和Data Access Objects(DAO)

2 MyBatis特性

1 MyBatis是支持定制化SQL。
	存储过程以及高级映射的优秀的持久层框架
2 MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
3 MyBatis可以使用简单的XML或注解用于配置和原始映射,
	将接口和java的POJO(
	普通的java对象 Plain Old Java Objects)
	映射成数据库中的记录。
4 MyBatis是一个半自动的ORM框架(Object Relation Mapping)

3 Mybatis和其他持久层框架对比

1 JDBC:
	SQL夹杂在java代码中,耦合度高,导致硬编码内伤
	维护不易且实际开发中SQL有变化,频繁修改的情况多见
	代码冗长,开发效率低

2 Hibernate 和 JPA
	操作简便,开发效率高
	程序中的长难复杂SQL需要绕过框架
	内部自动产生的SQL,不容易做特殊优化
	基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难
	反射操作太多,导致数据库性能下降

3 MyBatis
	轻量级,性能出色
	SQL和java代码分开,功能边界清晰,j
	ava代码专注业务,SQL语句专注数据
	开发效率稍逊于Hibernate,但是完全能够接受

4 映射文件的命名规则

映射文件的命名规则
	表对应的实体类的类名+Mapper.xml
	一个映射文件对应一个实体类,对应一张表的操作
	MyBatis的映射文件用于编写SQL,访问以及操作表中的数据
	MyBatis的映射文件存放的目录是:src/resources/mappers目录下

5 Mapper接口和Mapperxml的两个一致

MyBatis中可以向面向接口操作数据,要保证两个一致
	mapper接口的全类名和映射文件的命名空间(namespace)保持一致
	mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性一致

备注:一般情况下习惯操作
	1)mapper接口的名称与mapper.xml映射文件的名称保持一致
	2)mapper接口的包类路径(src/main/java下)与
	  mapper.xml映射文件的路径(src/main/resources下)保持一致
	3) mapper接口的层级包是以点分隔的
	4) mapper.xml文件的层级包是以正斜杠分隔的

6 数据库会话SqlSession的创建步骤:

1 SqlSession的创建步骤:
	1 读取MyBatis的核心配置文件
	2 创建SqlSessionFactoryBuilder对象
	3 通过核心配置文件对应的输入流,创建工厂类SqlSessionFactory 
	4 创建sqlSession对象
	5 通过代理模式创建自定义mapper接口的代理实现类对象
	6 调用自定义mapper接口的中的方法,
		就会根据自定义mapper的全类名匹配映射文件,
		通过调用的方法名匹配映射文件中SQL标签中的id属性值,
		并执行标签中的SQL语句
	7 sqlSession提交事务
	8 关闭sqlSession会话


解释:
	SqlSession:代表Java程序和数据库之间的会话
	HttpSession是Java程序和浏览器之间的会话
	SqlSessionFactory:是生产SqlSession对象的工厂
	工厂模式:
		如果创建一个对象,使用的过程都是基本固定的,
		那么我们就可以把创建这个对象的相关代码封装到一个工厂类中,
		以后使用这个工厂类来生产我们想要的对象

7 关于sqlSession提交事务的问题:

关于sqlSession提交事务的问题:
	 MyBatis对于增删改的业务需要操作事务,
	 如果未设置事务,则测试运行不报错,但数据库数据未发生变化
	 
	 sqlSession提交事务的两种方式:
		 方式一:手动提交
		 	通过sqlSession.commit()方法提交事务
		 方式二:自动提交
		 	在创建SqlSession对象时,参数设置为true,进行事务提交
		 	SqlSession sqlSession = 
		 				sessionFactory.openSession(true);

8 Mybatis的执行流程

在这里插入图片描述

    @Test
    public void testMyBatis1() throws IOException {
        //1.加载核心配置文件
        InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
        //2.获取会化工厂的构建对象SqlSessionFactoryBuilder
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder =
                new SqlSessionFactoryBuilder();
        //3.获取会话工厂对象SqlSessionFactory
        SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(stream);
        //4.获取SqlSession--代表java程序和数据库之间的会话
        SqlSession sqlSession = sessionFactory.openSession(true);
        //5.获取mapper接口对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //6.调用方法
        int result = userMapper.insertUser();
        //7.提交事务
        sqlSession.commit();
        System.out.println(result);
    }

9 #{}和${}的区别

MyBatis获取参数值的两种方式:${}和#{}
        #{}为sql预编译,编译时参数部分形成占位符?,
        	并且#{}变量会自动加上单引号’’
		${}为sql拼接,编译时参数部分不会加上单引号’’,
			需要时必须自己加上单引号’’。

#和$的区别:
#{}和${}这两个语法是为了动态传递参数而存在的,
	是Mybatis实现动态SQL的基础,
	总体上他们的作用是一致的(为了动态传参),
	但是在编译过程、是否自动加单引号、安全性、
	使用场景等方面有很多不同

a 编译过程
	#{} 是 占位符 :动态解析 -> 预编译 -> 执行
	${} 是 拼接符 :动态解析 -> 编译 -> 执行
	sql的预编译是在执行前就编译好,等执行时直接取编译结果去执行
		省去编译时间。sql预编译后会在参数位置用 ? 占位符表示。
	预编译:数据库驱动在发送sql和参数到DBMS之前,
		   先对sql语句进行编译处理,
		   之后DBMS则可以直接对sql进行处理,
		   不需要再次编译,提高了性能。
		   mybatis 默认情况下,对所有的 sql 进行预编译处理。 
	预编译语句可以重复利用。
		把一个 sql 预编译后产生的 PreparedStatement 对象缓存下来,
		下次对于同一个sql,可以直接使用这个缓存的PreparedState对象
b 是否自动加单引号
        #{}为sql预编译,编译时参数部分形成占位符?,
        	并且#{}变量会自动加上单引号’’
		${}为sql拼接,编译时参数部分不会加上单引号’’,
			需要时必须自己加上单引号’’。
c 安全性
        #{}  能防止sql 注入
        ${}  不能防止sql 注入
d Mybatis默认值不同
        #{} 可能是用arg0、arg1、arg2,或者 param1 、param2
        ${} 默认值param1、param2、param3

10 SQL注入攻击

什么是SQL注入:
	SQL注入攻击是指,后台数据库操作时,如果拼接外部参数到SQL语句中,
	就可能导致欺骗服务器执行恶意的SQL语句,造成数据泄露、删库、
	页面篡改等严重后果。
	
案例:
以name="张三 or name=李四 "
	${}方式出现SQL注入:
		select * from user where name = ${name}
		因为${}是拼接符,会直接替换,所以实际是
		select * from user where name = '张三' or name = '李四'
	#{}方式防止SQL注入:	
		select * from user where name = #{name}
		因为#{}是占位符,所以实际是:
		select * from user where name = '张三 or name = 李四'


${}:
	可以看到SQL语句的查询规则已经改变,
	原本是查一个叫名字叫“张三 or name=李四”的角色 ,
	现在变成了查一个名字叫“张三”或者叫“李四”的角色,
	这种通过传参就能改变SQL语句原本规则的操作就是SQL注入,
		这个在实际生产中是危险的,
		攻击者可以把SQL命令插入到Web表单的输入域,
		或者页面请求的查询字符串中,欺骗服务器执行恶意的SQL命令。
	所以,${} 直接拼接的方式可能引起SQL注入,不安全

#{}:
	可以看到, #{} 不会改变原本的SQL规则,
	占位符 “?” 处会被完整替换,因此可以防止SQL注入


如何选择 #{} 和 ${}
	能用 #{} 的地方就用 #{},尽量少用 ${}
    表名作参数,或者order by 排序时用 ${}
    传参时参数使用@Param("")注解,
    	@Param注解的作用是给参数命名,
    	参数命名后就能根据名字得到参数值,
    	正确的将参数传入sql语句中
    	(一般通过#{}的方式,${}会有sql注入的问题)。如下:

User selectById(@Param("id") String id);
List<User> selectByNameAndOrgId(@Param("name") String name, @Param("orgId") String orgId);

11 MyBatis获取参数值的方式(重点)

11.1 单个字面量类型的参数

情况1、
   mapper接口方法的参数为单个字面量类型
   可以通过${}和#{}以任意的名称获取参数值,
   但是要注意${}的单引号问题
   
方法:根据用户名查询用户信息
	 User findUserByUserName(String username);
方式一:使用#{}
<select id="findUserByUserName" resultType="com.zzy.pojo.User">
        select * from t_user where username = #{username};-
</select>   

方式一:使用${},注意引号问题
<select id="findUserByUserName" resultType="com.zzy.pojo.User">
        select * from t_user where username = "${username}";
</select>

在这里插入图片描述
在这里插入图片描述

11.2 多个字面量类型的参数

情况2:
  mapper接口方法的参数为多个个字面量类型
 	此时mybatis会将这些参数存储在一个map集合中,以两种方式进行存储
    1、以arg0.arg1...为键,以参数为值
    2、以param1,param2...为键,以参数为值
       因此只需要通过#{}和${}以键的方式访问即可,
       但是需要注意${}的单引号问题

方法:使用mybatis默认的map集合
	User checkLogin(String username,String password);

程序传递过来的参数1和参数2会维护在map集合中,
        map的key为:arg0,arg1 或者param1,param2
        需要使用map的key(arg0,arg1 或者 param1,param2,
        或者二者混合使用 arg0,param1)获取参数的值。
        故条件使用:
        	username=#{arg0} and password=#{arg1}

如果使用username=#{username} and password=#{password}
	则会出现binding异常,会报‘username’ no found
	报错的原因是:传递进来的参数维护在map中,
			map的key有四个值,分别是arg0,arg1,param1,param1,
			value是:username和password的真实值           
错误写法SQL:

<select id="checkLogin" resultType = "com.zzy.pojo.User">
    select * from t_user where username=#{username} and password=#{password}
</select>
正确写法SQL:

<select id="checkLogin" resultType = "com.zzy.pojo.User">
    select * from t_user where username=#{arg0} and password=#{arg1}
</select>

在这里插入图片描述

11.3 map集合类型的参数

情况3:
  mapper接口方法的参数有多个时,可以手动将参数放在一个map中存储
  此时map中的键值自行手动设置的,不是默认的
  因此只需要通过#{}和${}以键的方式访问,获得key对应的value值,
  但是需要注意${}的单引号问题

方法:
	User checkLoginMap(Map<String,Object> map);
<select id="checkLoginMap" resultType="com.zzy.pojo.User">
        select * from t_user where username=#{username} and password=#{password}
</select>
测试代码:
	@Test
    public void testCheckLoginMap() throws IOException {
        SqlSession sqlSession = 
        			SqlSessionUtils.getSqlSession();
        ParemeterMapper mapper = 
        			sqlSession.getMapper(ParemeterMapper.class);
        Map<String,Object> map = new HashMap<>();
        //将数据以k-v的形式放在map中
        map.put("username", "tom");
        map.put("password", "123456");
        User user = mapper.checkLoginMap(map);
        System.out.println(user);
    }

在这里插入图片描述

11.4 实体类类型的参数

情况4:
    mapper接口方法的参数是一个实体类类型的参数
    因此只需要通过#{}和${}以属性的方式获取属性值即可,
    		但是需要注意${}的单引号问题
方法:
	int addUser(User user);

11.5 使用@Param标识参数

情况5:
    使用@Param注解命名参数
    此时mybatis会将这些参数存储在一个map集合中,以两种方式进行存储
     	1、以@param注解的值为键,以参数为值
        2、以param1,param2...为键,以参数为值
  	MyBatis默认会将传递的参数存储在map集合中,
  		以当前@Param注解中的值为map的键,来获取属性值,
  		即:map中的键的值是注解中的值
	因此只需要通过#{}和${}以属性的方式获取属性值即可,
		但是需要注意${}的单引号问题

方法:
	User checkLoginByParam(@Param("username") String usernme, @Param("password") String password);
<select id="checkLoginByParam" resultType="com.zzy.pojo.User">
     select * from t_user where username=#{username} and password=#{password}
</select>
测试代码:
	 @Test
    public void testLoginByParam() throws IOException {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        ParemeterMapper mapper = sqlSession.getMapper(ParemeterMapper.class);
        User user = mapper.checkLoginByParam("lucy", "123456");
        System.out.println(user);
    }

在这里插入图片描述

12 MyBatis的各种查询功能

12.1 查询一个实体类对象

1 查询一个实体类对象

情况1MyBatis的各种查询功能--查询返回一个实体类对象
    若查询出的数据只有一条,可以通过实体类对象接收,或者集合接收
方法:
	User getUserById(@Param("id") Integer id);

<select id="getAllUser" resultType="com.zzy.pojo.User">
	  select * from t_User;
</select>

12.2 查询一个list集合

2 查询一个list集合
MyBatis的各种查询功能--查询返回一个list集合
   若查询出的数据由多条,可以通过list集合接收
   一定不能通过实体类来接收,会报异常TooManyResulrsException
方法:
	List<User> getAllUser();
<select id="getAllUser" resultType="com.zzy.pojo.User">
        select * from t_User;
</select>

12.3 查询单个数据

3 查询单个数据
情况3:
    MyBatis的各种查询功能--查询单个数据
    MyBatis设置了默认的类型别名
	    java.lang.Integer  -  int/Intefer
	    int ---- _int/_Integer
	    Map --- map
	    
方法:查询总记录数
	Integer getCount();
<select id="getCount" resultType="java.lang.Integer">
        select count(*) from t_user
</select>

12.4 查询一条数据为map集合

4 查询一条数据为map集合
情况4:
    MyBatis的各种查询功能 --查询一条数据为map集合
    查询结果为map集合:以字段为键,以字段所对应的值为值

方法:根据id查询结果返回为map集合
	Map<String,Object> getUserByIdToMap(@Param("id") Integer id);
<select id="getUserById" resultType="com.zzy.pojo.User">
        select * from t_user where id = #{id}
</select>

在这里插入图片描述

12.5 查询多条数据为Map集合

5 查询多条数据为Map集合
情况5:
   MyBatis的各种查询功能--查询多条数据为map集合
   方式一:封装成List<Map<String,Object>>
   方式二:封装成Map嵌套Map
     @MapKey注解,将每条数据转换成map集合作为值,
     以某个字段的值作为键,
     放在同一个map集合中
     
方法1:
	查询多条数据 封装到List-map集合中
    List<Map<String,Object>> getAllUserToListMap();

方法2:
	查询多条数据 封装到map集合中
		将表中的id字段作为map的key,将查询的每条数据封装到map中作为value
    	最后存储到map中,相当于map嵌套map
    	
	注解中的id表示map的key,value是每天数据封装到map 
		即Map<"id","map">
    @MapKey("id")
    Map<String,Object> getAllUserToMap();
List集合map:
 <select id="getAllUserToListMap" resultType="java.util.Map">
        select * from t_user
 </select>
 
Map集合map:
 <select id="getAllUserToMap" resultType="java.util.Map">
        select * from t_user
 </select>

在这里插入图片描述
在这里插入图片描述

13 特殊SQL的执行—使用${}

13.1 ${}----模糊查询

情况1、根据用户名模糊查询用户信息
		  方式一:使用${}进行字符串拼接
		  方式二:使用concat关键字拼接以及#{)占位符
		  方式三:使用#{}占位符操作

方法:根据用户名模糊查询用户信息
List<User> getUserByLike(@Param("username")String username);

     模糊查询:三种方式
     方案一:使用${}进行字符串拼接
     原始SQL:select * from t_user where username like "%${username}%"
     解析SQL:Preparing: select * from t_user where username like "%a%"

     方案二:常用方式-使用concat关键字拼接以及#{)占位符
     原始SQL:select * from t_user where username like concat('%',#{username},'%')
     解析SQL:Preparing: select * from t_user where username like concat('%',?,'%')

     方案三:常用方式-使用#{}占位符操作
     原始SQL:select * from t_user where username like "%"#{username}"%"
     解析SQL后Preparing: select * from t_user where username like "%"?"%"

13.2 ${}----批量删除

方法:
	int deleteMore(@Param("ids") String ids);
	
  	批量删除只能使用${},以字符串形式拼接进行删除,
		原始sql:delete from t_user where id in (${ids})
		解析sql:Preparing: delete from t_user where id in (13,14,15)
		
    注意:不能使用#{},会自动添加单引号。
    	 只会删除第一条,其余不删除
		原始sql:delete from t_user where id in (#{ids})
		解析sql:Preparing: delete from t_user where id in (?)

13.3 ${}----动态设置表名

动态设置表名
	只能使用${},不能使用#{},因为表名不能加单引号

方法:
List<User> getUserByTableName(@Param("tableName") String tableName);

	使用${}解析后的SQL:
		Preparing: select * from t_user;
	使用#{}解析后的SQL:
		Preparing: select * from ?;
		此时会报错。会给表名添加单引号,报错如下
		Caused by: 
		java.sql.SQLSyntaxErrorException:
		You have an error in your SQL syntax;
		check the manual that corresponds to 
		your MariaDB server versionfor the 
		right syntax to use near ''t_user'' at line 1

13.4 ${}----添加功能获取自增的主键

添加功能获取自增的主键
方法:
	int insertUser(User user);	
	
新增用户信息同时获取新增数据的主键
	useGeneratedKeys="true":
	设置当前标签中的SQL使用了自增的id
	keyProperty="id":
	将自增的主键的值,赋值给传输到映射文件中参数的某个属性
 <insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
        insert into t_user values (null,#{username},#{password},#{age},#{sex},#{email})
</insert>

14 自定义映射resultMap

14.1 resultMap 处理字段和属性的映射关系(三种方式:字段名和属性名不一致情况)

解决字段名和属性名不一致
    方式一:为字段起别名,保持和属性名一致
    方式二:核心配置文件中配置全局驼峰命名,将_自动映射成驼峰命名
      <setting name="mapUnderscoreToCamelCase" value="true"/>
    方式三:取消核心配置文件的驼峰命名,使用resultMap,
    		设置自定义之间的关系,使用字段名映射属性名

注意:如果属性名和字段名不一致,不对其进行以上三种方式操作
	 别名设置,或开启核心配置文件全局驼峰,或者自定义关系映射
	 那么查询结果中,此属性的值为null
方式一:为字段起别名
	数据库字段名:emp_name
	实体类属性名:empName
	要求:为字段起别名,其别名必须和实体类的属性名一致
	
	SQL案例如下:
		select eid,emp_name empName,age,sex,email from t_emp
方式二:MyBatis核心配置文件中配置全局驼峰命名,将_自动映射成驼峰命名
	<setting name="mapUnderscoreToCamelCase" value="true"/>
	
	SQL案例如下:直接查询,不需要别名
		select eid,emp_name,age,sex,email from t_emp
方式三:取消核心配置文件的驼峰命名,使用resultMap,
		设置自定义之间的关系,使用字段名映射属性名
		
方法:
	List<Emp> findAllEmp2();
属性:
    id:设置主键的映射关系
    result:设置其他普通属性的映射关系
    property:设置映射关系的属性名,必须是type属性所设置的实体类类型中的属性名
    column:设置数据映射关系的字段名,必须是SQL查询出来的表的字段名
    resultMap:设置自动以映射关系
    resultMap中的id:设置唯一标识,不能重复
    type:设置映射关系中的实体类类型


resultType自定义关系,解决字段名属性名不一致的SQL

	<resultMap id="empMap" type="Emp">
        <id property="eid" column="eid"/>
        <result property="empName" column="emp_name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <result property="email" column="email"/>
    </resultMap>
    <select id="findAllEmp2" resultMap="empMap">
        select * from t_emp;
    </select>

14.2 resultType 多对一映射处理(三种方式)

实体类Emp1 :员工表和部门表示多对一的关系在这里插入图片描述

14.2.1 级联方式处理多对一映射关系
需求:根据员工eid查询对应员工信息,以及员工的部门信息,
	 员工表和部门表的关系维护在多的一方,也就是员工表中的did字段

方式一:级联属性赋值多对一查询:
           查询员工以及员工做对应的部门信息,
           即:多个员工对应一个部分,关联关系在多的一方
            
    Emp1 getEmpAndDept(@Param("eid") Integer eid);
2 多对一-级联赋值查询SQL语句

    <resultMap id="empDeptMapOne" type="Emp1">
        <id property="eid" column="eid"/>
        <result property="empName" column="emp_name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <result property="email" column="email"/>
        <!--级联赋值-->
        <result property="dept.did" column="did"/>
        <result property="dept.deptName" column="dept_name"/>
    </resultMap>
    <select id="getEmpAndDept" resultMap="empDeptMapOne">
        select * from t_emp1 left join t_dept on t_emp1.did = t_dept.did where t_emp1.eid = #{eid}
    </select>

在这里插入图片描述

14.2.2 使用association处理多对一映射关系
需求:根据员工eid查询对应员工信息,以及员工的部门信息,
	 员工表和部门表的关系维护在多的一方,也就是员工表中的did字段

方式二:对一查新,映射使用association标签

	方法:
	Emp1 getEmpAndDept2(@Param("eid") Integer eid);

	association:联合,即处理多对一的映射关系
	property:需要处理的映射关系的属性名
	javaTyoe:该property属性的java类型

	property="dept" javaType="Dept"
	  dept是emp1中的属性名称
	  将查询到的字段did与java类型Dept中的did属性进行映射
	  将查询到的字段dept_name与java类型Dept中的deptName属性进行映射
	  映射完毕之后得到java的Dept类型的dept对象
	  再将此dept对象赋值给java类型Emp1中的dept属性
2 多对一查询使用association标签SQL语句

    <resultMap id="empDeptMapTwo" type="Emp1">
        <id property="eid" column="eid"/>
        <result property="empName" column="emp_name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <result property="email" column="email"/>
        <association property="dept" javaType="Dept">
            <id property="did" column="did"/>
            <result property="deptName" column="dept_name"/>
        </association>
    </resultMap>
    <select id="getEmpAndDept2" resultMap="empDeptMapTwo">
        select * from t_emp1 left join t_dept on t_emp1.did = t_dept.did where t_emp1.eid = #{eid}
    </select>

在这里插入图片描述

14.2.4 分步查询-多对一映射关系
需求:根据员工eid查询对应员工信息,以及员工的部门信息,
	 员工表和部门表的关系维护在多的一方,也就是员工表中的did字段

分步查询: 

	方法:
	Emp1Mapper方法:第一步
		Emp1 getEmpAndDept3(@Param("eid") Integer eid);
	DeptMapper方法:第二步
		Dept getDeptByEmp1Did(@Param("did") Integer did);

	数据库表emp1表维护对多一的关系,即部门的did主键作为emp1的外键存在
	步骤1:现根据eid查询得到员工信息,包括员工对应的did的值
	步骤2:通过did的值,映射到DeptMapper中,
		  执行根据did查询部门此案系的操作
    最后通过association标签进行联合
	
属性:
property="dept" :
	表示Emp1中的属性
select="com.zzy.mapper.DeptMapper.getDeptByEmp1Did":
	设置分步查询的SQL的唯一标识
	即:(namespace.sqlId,或者mapper接口的全类名.方法名)
	表示用DeptMapper中的方法,即通过Emp1中的did获取对应的Dept部门信息
column="did" :
	设置分步查询的条件,即did指的是员工表和部门的关系字段
	即通过查询得到的员工信息Emp1中的did的值,去dept表中查询对应部门信息
	再将部门的信息赋值给属性dept
fetchType=“lazy/eager”: 
	当开启全局延迟加载之后,可通过此属性设置延迟加载的效果
	<setting name="lazyLoadingEnabled" value="true"/>
	lazy 表示延迟加载   eager表示立即加载
3 多对一关系分步查询SQL

第一步:Emp1Mapper.xml中书写

    <resultMap id="empDeptMapThree" type="Emp1">
        <id property="eid" column="eid"/>
        <result property="empName" column="emp_name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <result property="email" column="email"/>
        <association property="dept"
                     select="com.zzy.mapper.DeptMapper.getDeptByEmp1Did"
                     column="did"
                     fetchType="lazy">
        </association>
    </resultMap>
    <select id="getEmpAndDept3" resultMap="empDeptMapThree">
        select * from t_emp1 where eid = #{eid}
    </select>

第二步:DeptMapper.xml中书写
    <!--多对一  EmpMapper分布查询的第二步-->
    <select id="getDeptByEmp1Did" resultType="Dept">
        select * from t_dept where did = #{did}
    </select>

在这里插入图片描述

14.3 resultType 一对多映射处理

实体类Dept :部门表和员工表示一对多的关系在这里插入图片描述

14.3.1 使用collection处理一对多映射
需求:根据部门did查询对应部门信息,以及部门下的员工信息,
	 员工表和部门表的关系维护在多的一方,也就是员工表中的did字段

一对多查询collection标签
    查询部门信息以及部门信息中的员工信息,使用集合属性设置员工信息
方法:
	Dept getDeptAndEmp(@Param("did") Integer did);

	属性:
     collection:用来处理一对多的关系
     property="emp1s":表示Dept实体类中的emp1s属性
     ofType:表示该属性所对应的集合中存储的数据的类型
2  一对多collection标签的SQL

   <resultMap id="deptAndEmpResultMap" type="Dept">
        <id property="did" column="did"/>
        <result property="deptName" column="dept_name"/>
        <collection property="emp1s" ofType="Emp1">
            <id property="eid" column="eid"/>
            <result property="empName" column="emp_name"/>
            <result property="age" column="age"/>
            <result property="sex" column="sex"/>
            <result property="email" column="email"/>
        </collection>
    </resultMap>
    <select id="getDeptAndEmp" resultMap="deptAndEmpResultMap">
        select * from t_dept left join t_emp1 on t_dept.did = t_emp1.did where t_dept.did = #{did}
    </select>

在这里插入图片描述

14.3.2 分步查询–一对多关系映射
需求:根据部门did查询对应部门信息,以及部门下的员工信息,
	 员工表和部门表的关系维护在多的一方,也就是员工表中的did字段

一对多分布查询
方法:
	DeptMapper方法:第一步
		Dept getDeptAndEmp2(@Param("did") Integer did);
	Emp1Mapper方法:第二步
		Emp1 getEmpByDeptDid(@Param("did") Integer did);

属性:
	property="emp1s" :
		Dept实体类中的emp1s属性
	select="com.zzy.mapper.EmpMapper.getEmpByDeptDid"
		表示应用EmpMapper中的方法,
		即通过Dept中的did对应Emp1中的did获取对应的emp的信息
	column="did" :
		did指的是员工表和部门的关系字段
		即通过查询Dept中的did的值,去emp1表中查询对应员工信息
		再将员工的信息赋值给属性emp1s
	fetchType=“lazy/eager”: 
		当开启全局延迟加载之后,可通过此属性设置延迟加载的效果
     	<setting name="lazyLoadingEnabled" value="true"/>
        lazy 表示延迟加载   eager表示立即加载
一对多分步查询SQL

第一步:DeptMapper.xml中书写
	<resultMap id="deptAndEmpResultMap2" type="Dept">
        <id property="did" column="did"/>
        <result property="deptName" column="dept_name"/>
        <collection property="emp1s"
                    select="com.zzy.mapper.EmpMapper.getEmpByDeptDid"
                    column="did"
                    fetchType="lazy"></collection>
    </resultMap>
    <select id="getDeptAndEmp2" resultMap="deptAndEmpResultMap2">
        select * from t_dept where did = #{did}
    </select>
    
第二步:Emp1Mapper.xml中书写
	<select id="getEmpByDeptDid" resultType="Emp1">
        select * from t_emp1 where did = #{did}
    </select>

在这里插入图片描述

15 动态SQL标签

15.1 if 标签

if标签-----动态SQL多条件查询
    标签含义:
    if:判断标签
      根据test属性对应的表达式决定标签中的内容是否需要拼接到SQL中,
      
      1=1 恒成立的条件,如果后续的都不成立,那么1=1的作用就体现出来了,
      select * from t_emp1 where 1=1
      如果if判断的条件均不满足,则SQL执行后where关键字后面无内容,
      即:select * from t_emp1 where
      所以需要拼接1=1 绝对成立的条件,达到符合SQL语法的目的。如:
      select * from t_emp1 
      				where 1=1 
		      				and emp_name = ? 
		      				and age = ? 
		      				and sex = ? 
		      				and email = ?
2 if标签SQL语句

<select id="getEmpByMoreCondition" resultType="Emp1">
        select * from t_emp1 where
        <!--select * from t_emp1 where 1=1-->
        <if test="empName != null and empName != ''">
            emp_name = #{empName}
        </if>
        <if test="age != null and age != ''">
            and age = #{age}
        </if>
        <if test="sex != null and sex != ''">
            and sex = #{sex}
        </if>
        <if test="email != null and email != ''">
            and email = #{email}
        </if>
    </select>

15.2 where标签

动态SQL标签-where动态生成
	标签含义:
    where:当where标签中有内容时,且内容满足条件时,
    	   		会自动生成关键字where,并且将内部多余的and或or去掉
          当where标签中有内容时,且内容不满足条件时,
                不会生成where关键字,此时where标签没有恩和效果
    注意:where标签不能将内容后的and去掉,只能将内容前面的and去掉
where标签SQL语句

<select id="getEmpByMoreCondition2" resultType="Emp1">
        select * from t_emp1
        <where>
            <if test="empName != null and empName != ''">
                emp_name = #{empName}
            </if>
            <if test="age != null and age != ''">
                and age = #{age}
            </if>
            <if test="sex != null and sex != ''">
                and sex = #{sex}
            </if>
            <if test="email != null and email != ''">
                and email = #{email}
            </if>
        </where>
    </select>

15.3 trim 标签

动态SQL标签-trim
     属性:
         prefix:在trim标签中的内容前面添加指定的内容
         suffix:在trim标签中的内容后面添加指定的内容
         prefixOverrides:在trim标签中的内容前面去掉指定的内容
         suffixOverrides:在trim标签中的内容前面去掉指定的内容
     如果标签中的内容无效,那么 trim标签也没有任何效果,
     解析后的SQL语句是:select * from t_emp1
trim标签SQL语句

 <select id="getEmpByMoreCondition3" resultType="Emp1">
        select * from t_emp1
        <trim prefix="where" suffixOverrides="and|or">
                <if test="empName != null and empName != ''">
                    emp_name = #{empName} and
                </if>
                <if test="age != null and age != ''">
                    age = #{age} or
                </if>
                <if test="sex != null and sex != ''">
                    sex = #{sex} and
                </if>
                <if test="email != null and email != ''">
                    email = #{email}
                </if>
        </trim>
    </select>

15.4 choose when otherwise 标签

动态SQL标签--- choose when otherwise	
	属性:
	when:当满足条件时,执行内容,相当于java中的if或else if语句
	otherwise:当不满足条件时,执行内容,相当于java中的else语句

	注意:
	如果第一个非主键字段值满足条件,后面的when中的内容将不再判断
	如果when中的条件都不满足,则会执行otherwise的内容
动态标签choose when otherwise SQL语句

<select id="getEmpByChoose" resultType="Emp1">
        select * from t_emp1
        <where>
            <choose>
                <when test="empName!=null and empName!=''">
                    emp_name = #{empName}
                </when>
                <when test="age!=null and age!=''">
                    age = #{age}
                </when>
                <when test="sex!=null and sex!=''">
                    sex = #{sex}
                </when>
                <when test="email!=null and email!=''">
                    email = #{email}
                </when>
                <otherwise>
                    did = 6
                </otherwise>
            </choose>
         </where>
</select>
满足条件的情况
      List<Emp1> list = mapper.getEmpByChoose(new Emp1(null,"tom" ,22 ,"男" ,"111@qq.com" , null));
      解析执行后的SQL:如果第一个非主键字段值满足条件,后面的when中的内容将不再判断
      Preparing: select * from t_emp1 WHERE emp_name = ?

不满足条件的情况
      List<Emp1> list = mapper.getEmpByChoose(new Emp1(null,"" ,null ,"" ,"" , null));
      解析执行后的SQL:如果when中的条件都不满足,则会执行otherwise的内容
      Preparing: select * from t_emp1 WHERE did = 6

15.5 foreach 标签

15.5.1 foreach 标签----通过数组实现批量删除(in,or)
1 动态SQL-foreach。通过数组实现批量删除 in

	方法:
	in关键字:
	int deleteMoreByArray(@Param("eids") Integer[] eids);
	or关键字:
	int deleteMoreByArray2(@Param("eids") Integer[] eids);
	
	属性:
		collection="eids" :集合数组的名称
		item="eid" :数组中的元素
		separator="," :元素之间以逗号隔开
		open="(" :以左括号开始
		close=")":以右括号结束
 	    
	SQL:in关键字
	delete from t_emp1 where eid in ( ? , ? , ? , ?)     
	SQL:or关键字
	delete from t_emp1 where eid = ? or eid = ?            
2 动态标签foreach--批量删除的SQL语句

 <!--in的删除方式一、括号在外面-->
<delete id="deleteMoreByArray">
		delete from t_emp1 where eid in
       (
         <foreach collection="eids" item="eid" separator=",">
                #{eid}
         </foreach>
        )
 </delete>

<!--in的删除方式二、括号在里面-->
<delete id="deleteMoreByArray">
	<foreach collection="eids" item="eid" separator="," open="(" close=")">
            #{eid}
    </foreach>
</delete>

<!--or的删除方式一-->
<delete id="deleteMoreByArray2">
        delete from t_emp1 where
        <foreach collection="eids" item="eid" separator="or">
            eid = #{eid}
        </foreach>
</delete>

在这里插入图片描述
在这里插入图片描述

15.5.2 foreach 标签----通过集合实现批量添加
1 动态SQL-foreach 通过集合实现批量添加

	方法:
	int InsertMoreByList(@Param("emp1s") List<Emp1> emp1s);

	属性:
		collection:设置需要循环的数组或者集合
    	item:表示数组或者集合中的每一个数据
    	separator:循环体之间的分隔符
    	open:foreach标签所循环的所有内容的开始符
    	close:foreach标签所循环的所有内容的结束符

解析运行后的SQL:
Preparing: insert into t_emp1 values (null,?,?,?,?,null) , 
				(null,?,?,?,?,null) , (null,?,?,?,?,null) ,
				(null,?,?,?,?,null)
动态标签foreach--批量添加的SQL语句

<insert id="InsertMoreByList">
    insert into t_emp1 values 
    <foreach collection="emp1s" item="emp1" separator=",">
     	(null,#{emp1.empName},#{emp1.age},#{emp1.sex},#{emp1.email},null)
    </foreach>
</insert>
测试代码

	@Test
    public void testAddMoreByList() throws IOException {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        DynamicSqlMapper mapper = sqlSession.getMapper(DynamicSqlMapper.class);
        Emp1 emp1 = new Emp1(null, "蛮王", 22, "男", "555@qq.com" , null);
        Emp1 emp2 = new Emp1(null, "三刀", 22, "男", "555@qq.com" , null);
        Emp1 emp3 = new Emp1(null, "五秒", 22, "男", "555@qq.com" , null);
        Emp1 emp4 = new Emp1(null, "真男人", 22, "男", "555@qq.com" , null);
        List<Emp1> list = Arrays.asList(emp1, emp2, emp3, emp4);
        int i = mapper.InsertMoreByList(list);
        System.out.println(i);
    }

在这里插入图片描述

15.6 sql片段标签+include标签

动态SQL- sql标签
    用于提取公共部分,引入使用
    设置SQL片段,提取公共部分代码:<sql id=“common”>公共代码</sql>
    引用SQL片段,在SQL中引入公共代码:
    			<include refid="common"></include>

16 Mybatis缓存

16.1 MyBatis的一级缓存

一级缓存介绍:
	一级缓存是SqlSession级别的,一级缓存是默认开启的,
	通过一个SqlSession查询的额数据会被缓存,下次查询相同的数据,
	就会从缓存中直接获取,不会重新访问数据库

16.2 一级缓存失效的四种情况:

一级缓存失效的四种情况:
	1 不同的SqlSession对应不同的缓存
	2 同一个SqlSession查询条件不同
	3 同一个SqlSession两次查询期间,
		执行了任意一次增删改查的操作
	4 同一个SqlSession两次查询期间手动清空了缓存


   一级缓存生效
  	   同个sqlSession下的。同一个mapper两次查询
			SQL知执行一次
       	    第一次查询数据库
       	    第二次查询缓存


	一级缓存生效
        同个sqlSession下的。不同mapper分别查询
            SQL知执行一次
            第一次查询数据库
            第二次查询缓存


	一级缓存失效
          不同sqlSession下的mapper分别查询
              SQL会执行两次
              第一次查询数据库
              第二次查询数据库
              	 一级缓存是sqlSession级别的,
                 相同的sqlSession使用的是同一个缓存
                 不同的sqlSession使用的是不同的缓存

	一级缓存失效
         同一个sqlSession下的mapper分别查询
              第一次查询数据库
              执行新增之后,自动清理缓存
              此时缓存中无数据。第二次查询依然查询数据库

	一级缓存失效
         同个sqlSession下的。同一个mapper两次查询
               SQL知执行一次
               第一次查询数据库
               手动清理缓存
               第二次查询数据库
        
         手动清理缓存,只能清理一级缓存
         sqlSession.clearCache();

16.3 MyBatis的二级缓存

二级缓存介绍:
	二级缓存是SqlSessionFactory级别的,
	通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存,
	此后若再次执行相同的查询操作,结果会从缓存中获取


二级缓存开启的条件
	1 在核心配置文件中,设置全局配置属性,cacheEnable="true",
	  默认为true,不需要设置
	2 在映射文件中设置标签<cache>
	3 二级缓存必须在SqlSession关闭或者提交之后有效
	4 查询的数据所转换的实体类类型必须实现序列化接口

二级缓存失效的情况
	两次查询期间执行了任意的增删改,会使得一级缓存和二级缓同时失效

二级缓存生效
    不同sqlSession下的mapper分别查询
          SQL只执行一次
          第一次查询数据库
          第一次查询二级缓存

二级缓存生效
    不同sqlSession下的mapper分别查询
         第一次查询数据库
         随机执行新增操作,此时缓存会被清理
         第二次查询数据库

16.4 二级缓存的相关配置< cache/ >标签中的属性

在mapper配置文件中添加的cache标签可以设置一些属性:
	eviction属性:缓存回收策略
	LRU:最近最少使用的,移除最长时间不被使用的对象
	FIFO:先进先出,按对象进入缓存的顺序来移除他们
	SOFT:软引用,移除基于垃圾回收器和软引用规则的对象
	WEAK:弱引用,更积极的移除基于垃圾回收器和弱引用规则的对象
	flushinterval:刷新间隔,单位毫秒,默认情况下不设置,
				  也就是没有刷新问题,缓存仅仅在调用增删改语句时刷新
	size:引用数目,代表缓存最多可以存储多少个对象,太大容易导致内存溢出
	readOnly:只读 true/false
	true:表示只读缓存,会给所有调用者返回缓存对象的相同实例,
	      因为这些对象不能被修改
	false:表示读写缓存,会返回缓存对象的拷贝(通过序列化),效率低且安全

16.5 MyBatis缓存查询的顺序

1 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,
  可以拿来直接使用
2 如果二级缓存中没有命中,则查询一级缓存
3 如果一级缓存中也没有命中,则查询数据库
4 SqlSession关闭之前,从数据库查询的数据默认保存在一级缓存
5 SqlSession关闭之后,一级缓存的数据会写入二级缓存
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值