SSM 笔记

1 篇文章 0 订阅
1 篇文章 0 订阅

文章目录

Spring

Spring就是一个轻量级的控制反转 (IOC) 和面向切面编程 (AOP) 的框架

1、环境

  • 官网下载:https://repo.spring.io/release/org/springframework/spring/

  • 导入Spring Web MVC:

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.6</version>
</dependency>
  • 导入JDBC
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.6</version>
</dependency>

2、组成

gPNEw9.png

Spring Boot

  • 快速开发脚手架
  • 基于它可以快速开发单个微服务
  • 约定大于配置

Spring Cloud

  • 基于Spring Boot 实现

3、IOC

3.1、概念

  • 控制反转,是一种设计思想,DL(依赖注入)是实现IOC的一种方法
  • 面向对象编程时,对象的创建由程序自己控制,而控制反转后将对象的创建转移给第三方
  • 一句话,对象由spring创建装配

3.2、对象创建

  • 使用无参构造创建对象,默认实现

    • 给类参数赋值
    <bean id="mysqlImpl" class="dao.Impl.UserDaoMysqlImp"></bean>
    <bean id="oracleImpl" class="dao.Impl.UserDaoOracleImp"></bean>
    <!--创建实现类-->
    <bean id="UserServiceImpl" class="service.Impl.UserServiceImp">
        <!-- 给实现类设置参数,ref:引用Spring容器创建好的对象-->
        <!-- 给实现类设置参数,value:引用基本数据类型-->
        <property name="UserDao" ref="oracleImpl"></property>
    </bean>
    
  • 如果使用有参构造创建对象

    • 下标赋值
    <bean id="user" class="domain.User">
        <constructor-arg index="0" value="小明"></constructor-arg>
    </bean>
    
    • 参数类型赋值(多个参数同类型不可用)
    <bean id="user" class="domain.User">
        <constructor-arg type="java.lang.String" value="小王"></constructor-arg>
    </bean>
    
    • 参数名赋值
    <bean id="user" class="domain.User">
        <constructor-arg name="name" value="小王"></constructor-arg>
    </bean>
    

4、Spring标签

4.1、alias

  • 别名
<alias name="" alias=""></alias>

4.2、bean的配置

  • id:bean的唯一标识符
  • class:bean对象所对应的全限定名,全类名
  • name:别名,也可以取多个别名,比alias更高级

4.3、import

  • 一般用于团队开发使用
  • 可以将多个配置文件合并为一个
<import resource="xxx"></import>

5、依赖注入

5.1、构造器注入

constructor-arg

5.2、set 注入

  • 依赖注入
    • 依赖:bean 对象的创建依赖于容器
    • 注入:bean 对象的所有属性,由容器来注入
<bean id="address" class="domain.Address">
    <property name="address" value="运城"></property>
</bean>
<bean id="student" class="domain.Student">
    <!--第一种,基本数据类型注入-->
    <property name="name" value="小明"></property>
    <!--第二种,引用数据类型注入-->
    <property name="address" ref="address"></property>
    <!--第三种,数组注入-->
    <property name="books">
        <array>
            <value>红楼梦</value>
            <value>西游记</value>
            <value>三国演义</value>
            <value>水浒传</value>
        </array>
    </property>
    <!--第四种,List注入-->
    <property name="hobbys">
        <list>
            <value>吃饭</value>
            <value>睡觉</value>
        </list>
    </property>
    <!--第五种,Map注入-->
    <property name="card">
        <map>
            <entry key="身份证" value="xxx"></entry>
            <entry key="银行卡" value="xxx"></entry>
        </map>
    </property>
    <!--第六种,Set注入-->
    <property name="games">
        <set>
            <value>xx1</value>
            <value>xx2</value>
            <value>xx3</value>
        </set>
    </property>
    <!--第七种,null值注入-->
    <property name="wife">
        <null></null>
    </property>
    <!--第八种,空值注入-->
    <property name="wife" value=""></property>
    <!--第九种,properties注入-->
    <property name="info">
        <props>
            <prop key="学号">001</prop>
            <prop key="性别"></prop>
            <prop key="年龄">18</prop>
        </props>
    </property>
</bean>

5.3、拓展方式

  • p 命名空间注入,可以直接注入属性的值
	<!--导入约束-->
	xmlns:p="http://www.springframework.org/schema/p"
    <!--扩展注入:p 命名空间注入-->
    <bean id="stu2" class="domain.Student" p:name="小王"></bean>
  • c 命名空间注入,可以直接注入属性的值,必须有有参构造函数
	<!--导入约束-->
	xmlns:c="http://www.springframework.org/schema/c"
    <!--扩展注入:c 命名空间注入-->
    <bean id="stu3" class="domain.Student" c:name="小明"></bean>

6、Bean的作用域

  • 代理模式(Spring 默认机制):scope=“singleton” 一个对象
  • 原型模式:scope=“prototype” 多个对象,每次从容器中 get 时都会产生一个新对象
  • 其余的request,session,application只能在web开发使用

7、Bean的自动装配

  • 自动装配是Spring满足bean依赖的一种方式
  • Spring会在上下文中自动寻找,并自动给bean装配属性
<bean id="cat" class="domain.Cat"></bean>
<bean id="dog" class="domain.Dog"></bean>

7.1、byname

<!--byname 自动装配:会自动在容器上下文查找,和自己set方法后面的值对应的 bean id-->
<bean id="person" class="domain.Person" autowire="byName">
    <property name="name" value="小王"></property>
</bean>

7.2、bytype

<!--bytype 自动装配:会自动在容器上下文查找,和自己对象属性类型相同的 bean ,必须保证全局唯一-->
<bean id="person" class="domain.Person" autowire="byType">
    <property name="name" value="小王"></property>
</bean>

7.3、注解装配

jdk 1.5 支持注解 Spring 2.5 支持注解

  1. 导入约束:

    <?xml version="1.0" encoding="UTF-8"?>
    <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:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!--指定要扫描的包,该包下的注解就会生效-->
        <context:component-scan base-package="domain"/>
        <!--注解支持-->
        <context:annotation-config/>
    </beans>
    
  2. 配置注解支持

    	<context:annotation-config/>
    
  3. @Autowired:直接在类成员属性上使用即可,也可以在set方式上使用

    使用Autowired 我们可以不使用 set 方法了,前提是自动装配的属性在 IOC(Spring) 容器中可以找到对应的属性和名字,首先bytype,其次byname必须要求对象存在

@Nullable 字段标记了这个注解,说明了这个注解可以为空

public @interface Autowired {
    boolean required() default true;
}

@Autowired(required = false):说明这个对象可以为null,否则出现异常,与@Nullable一致

//配合Qualifier手动匹配对象
@Autowired
@Qualifier("dog222")
private Dog dog;

@Resource也可以进行匹配,是Java内置的,默认通过byname实现,找不到bytype实现
@Resource(name="dog222")//手动匹配 

8、注解开发

  • Spring 4 后,使用注解开发必须保证 aop 包的导入,导入 context 约束,
  1. bean

    @Component:组件,放在类上,等价于

  2. 属性注入

    @Value(“xx”):加在属性定义或 set 方法前,相当于 bean 中的 property 赋值

  3. 衍生的注解

    @Component 有几个衍生注解,功能一致,在 web 开发中,会按照 mvc 三层架构分层

    • dao:@Repository
    • service:@Service
    • controller:@Controller
  4. 自动装配注解

  5. 作用域:@Scope(“xxx”)

9、Java Config

  • 完全使用 Java 方式配置 Spring ,不使用 Xml,全权 Java
  • 是 Spring 的一个子项目
//这个也会被Spring接管,注册到容器中,本身就是一个Component
//配置类,相当于 xml 文件
@Configuration

//扫描包
@ComponentScan("domain")

public class MyConfig {

    //注册一个 Bean ,相当于一个Bean标签,方法名字相当于 id,返回值相当于class属性
    @Bean
    public User getUser(){
        return new User();
    }
}

//说明这个类被spring接管,注册到了容器中
@Component
public class User {

    private String name;

    public String getName() {
        return name;
    }

    //赋值
    @Value("小王")
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

10、代理模式

  1. 静态代理

    • 抽象角色:一般会使用接口或抽象类来解决
    • 真实角色:被代理的角色
    • 代理角色:代理真实角色,代理真实角色后,会做一些附属操作
    • 客户:访问代理对象的人

    代理模式好处:

    • 可以使真实角色的操作更加纯粹,不用去关注一些公共事务
    • 公共交给代理角色,实现了业务的分工
    • 公共业务发生拓展时,方便集中管理

    代理模式缺点:

    • 一个真实角色产生代理角色,代码量翻倍
  2. 动态代理

    • 动态代理和静态代理角色一样
    • 动态代理的代理类是动态生成的,不是直接写好的
    • 动态代理分为两大类;
      • 基于接口:基于JDK
      • 基于类:基于cglib
    • 基于接口有两个类:
      • Proxy:生成动态代理实例
      • InvocationHandler:调用处理程序并返回结果
    //动态生成代理类
    public class ProxyUtils implements InvocationHandler {
    
        //被代理的接口
        private Object target;
    
        public void setRent(Object target){
            this.target = target;
        }
    
        //生成得到代理类
        public Object getProxy(){
            return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this );
        }
        
        /**
         * 处理代理实例,并返回结果
         * @param proxy
         * @param method
         * @param args
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            //动态代理本质就是使用反射机制
            Object result = method.invoke(target, args);
            return result;
        }
    }
    

11、AOP

  • 面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术

  • 使用方式:

    • 使用原生Spring API 接口

      • 定义两个实现类实现 MethodBeforeAdvice 和 AfterReturningAdvice 接口
      方法一:使用原生Spring API 接口
      配置aop:需要导入aop约束
      <aop:config>
          <!--切入点:expression = execution(修饰符  返回值  包名.类名/接口名.方法名(参数列表))-->
          <aop:pointcut id="pointcut" expression="execution(* service.Impl.UserServiceImp.*(..))"/>
      
          <!--执行环绕增加-->
          <aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
          <aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"></aop:advisor>
      
      </aop:config>
      
    • 使用自定义实现AOP(主要切面定义)

      • 自定义一个类,定义方法,before,after
          方法二:自定义类
          <bean id="diy" class="diy.DiyPointCut"></bean>
          <aop:config>
              <!--定义切面 ref 引用的类-->
              <aop:aspect ref="diy">
                  <!--切入点-->
                  <aop:pointcut id="pointcut" expression="execution(* service.Impl.UserServiceImp.*(..))"/>
                  <!--通知-->
                  <aop:before method="before" pointcut-ref="pointcut"></aop:before>
                  <aop:after method="after" pointcut-ref="pointcut"></aop:after>
              </aop:aspect>
          </aop:config>
      
    • 使用注解

      • 定义一个类,@Aspect、@Before("")、@After("")、@Around("")注解
      //使用注解实现AOP
      @Aspect //标注这个类是一个切面
      public class AnnotationPointCut {
      
          @Before("execution(* service.Impl.UserServiceImp.*(..))")
          public void before(){
              System.out.println("方法执行前");
          }
      
          @After("execution(* service.Impl.UserServiceImp.*(..))")
          public void after(){
              System.out.println("方法执行后");
          }
      
          //环绕增强:可以给定一个参数,代表我们要获取处理切入的点
          @Around("execution(* service.Impl.UserServiceImp.*(..))")
          public void around(ProceedingJoinPoint jp) throws Throwable {
              System.out.println("环绕前");
      
              //执行方法
              Object proceed = jp.proceed();
      
              System.out.println("环绕后");
          }
      
      }
      
      
      • 配置注解
          <!--方法三:注解-->
          <bean id="annotationPointCut" class="diy.AnnotationPointCut"></bean>
          <!--开启注解支持-->
          <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
      

12、声明式事务

12.1、概念

  • 把一组业务当作一个业务来做,要么都成功,要么都失败,保证完整性和一致性

12.2、ACID原则

  • 原子性:是不可分割的最小操作单位,要么同时成功,要么同时失败
  • 持久性:当事务提交或回滚后,数据库会持久化的保存数据
  • 隔离性:多个事物之间相互独立
  • 一致性:事务操作前后数据总量不变

13、常用注解

  1. @Autowired:自动装配,直接在类成员属性上使用即可,也可以在set方法上使用,首先bytype,其次byname必须要求对象存在
  2. @Nullable:字段标记了这个注解,说明了这个注解可以为空
  3. @Resource:自动装配,是Java内置的,默认通过byname实现,找不到bytype实现
  4. @Qualifier:配合Autowired手动匹配对象,写在之后
  5. @Component:组件,放在类上,说明这个类被 Spring 管理了,想当于 xml 中的 bean
  6. @Value(“xx”):加在属性定义或 set 方法前,相当于 bean 中的 property 赋值
  7. @Scope(“xxx”):作用域
  8. @Configuration:配置类,相当于 xml 文件
  9. @ComponentScan(“xxx”):扫描包,加在配置类下
  10. @Import(xxx.class):导入别的配置类
  11. @Aspect:表明这个类是一个切面
  12. @Before(“execution(* service.Impl.UserServiceImp.*(…))”):方法执行前
  13. @After(“execution(* service.Impl.UserServiceImp.*(…))”):方法执行后
  14. @Around(“execution(* service.Impl.UserServiceImp.*(…))”):环绕增强,可以给定一个参数,代表我们要获取处理切入的点,执行方法:Object proceed = jp.proceed();

Spring MVC

1、概念

  • 模型(dao,service)、视图(jsp,html)、控制器(servlet)
  • 基于Java实现的 MVC 的轻量级 Web 框架( Servlet )

2、原理

SpringMVC的原理如下图所示:

​ 当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。

图片

图片

简要分析执行流程

  1. DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。

    我们假设请求的url为 : http://localhost:8080/SpringMVC/hello

    如上url拆分成三部分:

    http://localhost:8080服务器域名

    SpringMVC部署在服务器上的web站点

    hello表示控制器

    通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。

  2. HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。

  3. HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。

  4. HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。

  5. HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。

  6. Handler让具体的Controller执行。

  7. Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。

  8. HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。

  9. DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。

  10. 视图解析器将解析的逻辑视图名传给DispatcherServlet。

  11. DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。

  12. 最终视图呈现给用户。

3、HelloMVC

3.1、导入依赖

<dependencies>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.6</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

    </dependencies>

3.2、注册Servlet

<!--配置 DispatchServlet :SpringMVC的核心,请求分发器,前端控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatchServlet要绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--启动级别:1 跟随服务器一块启动-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--在SpringMVC中,/ 只匹配所有的请求,不包括jsp页面,而 /* 匹配所有的请求,包括jsp页面-->
	<!--处理器映射-->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

3.3、配置文件

<?xml version="1.0" encoding="UTF-8"?>
<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 class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>

    <!--处理器适配器-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>

    <!--视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <!--后缀-->
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--BeanNameUrlHandlerMapping:bean-->
    <bean id="/hello" class="controller.HelloController"></bean>

</beans>

3.4、控制器

public class HelloController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mv = new ModelAndView();

        //业务代码
        String result = "helloSpringMVC";
        mv.addObject("msg",result);

        //视图跳转
        mv.setViewName("test");

        return mv;
    }
}

4、AnHelloMVC

4.1、导入依赖

4.2、注册Servlet

4.3、配置文件

<?xml version="1.0" encoding="UTF-8"?>
<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">

    <!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
    <context:component-scan base-package="controller"/>
    <!-- 让Spring MVC不处理静态资源 -->
    <mvc:default-servlet-handler />
    <!--开启mvc注解驱动-->
    <mvc:annotation-driven />

    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>

</beans>

4.4、控制器

@Controller//接管类
public class HelloController {

    @RequestMapping("/hello")//路径
    //如果返回值为String,并且有具体页面可以跳转,那么就会被视图解析器解析
    public String hello(Model model){
        //封装数据
        model.addAttribute("msg","hello,Spring!");

        return "hello";//会被视图解析器处理
    }
}

5、RestFul风格

5.1、概念

Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

5.2、使用

@Controller
public class RestFullController {

    //@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.DELETE)
    @GetMapping("/add/{a}/{b}")
    public String test1(@PathVariable int a,@PathVariable int b, Model model){
        int res = a + b;
        model.addAttribute("msg","结果为:" + res);
        return "test";
    }
}

6、数据处理和跳转

6.1、结果跳转方式

6.1.1、ModelAndView
  • 设置ModelAndView对象 , 根据view的名称 , 和视图解析器跳到指定的页面
  • 页面 : {视图解析器前缀} + viewName +{视图解析器后缀}
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
     id="internalResourceViewResolver">
   <!-- 前缀 -->
   <property name="prefix" value="/WEB-INF/jsp/" />
   <!-- 后缀 -->
   <property name="suffix" value=".jsp" />
</bean>
  • Controller
public class ControllerTest1 implements Controller {

   public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
       //返回一个模型视图对象
       ModelAndView mv = new ModelAndView();
       mv.addObject("msg","ControllerTest1");
       mv.setViewName("test");
       return mv;
  }
}
6.1.2、ServletAPI
  • 通过设置ServletAPI , 不需要视图解析器 .
    • 通过HttpServletResponse进行输出
    • 通过HttpServletResponse实现重定向
    • 通过HttpServletResponse实现转发
@Controller
public class ResultGo {

   @RequestMapping("/result/t1")
   public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
       rsp.getWriter().println("Hello,Spring BY servlet API");
  }

   @RequestMapping("/result/t2")
   public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
       rsp.sendRedirect("/index.jsp");
  }

   @RequestMapping("/result/t3")
   public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
       //转发
       req.setAttribute("msg","/result/t3");
       req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
  }

}
6.1.3、SpringMVC
  • 通过SpringMVC来实现转发和重定向 - 无需视图解析器
@Controller
public class ResultSpringMVC {
   @RequestMapping("/rsm/t1")
   public String test1(){
       //转发
       return "/index.jsp";
  }

   @RequestMapping("/rsm/t2")
   public String test2(){
       //转发二
       return "forward:/index.jsp";
  }

   @RequestMapping("/rsm/t3")
   public String test3(){
       //重定向
       return "redirect:/index.jsp";
  }
}

6.2、数据处理

6.2.1、处理提交数据
  • 域名称和处理方法参数一致
http://localhost:8080/hello?name=xxx

@RequestMapping("/hello")
public String hello(String name){
   System.out.println(name);
   return "hello";
}
  • 域名称和处理方法的参数名不一致
http://localhost:8080/hello?username=xxx

//@RequestParam("username") : username提交的域的名称 .
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name){
   System.out.println(name);
   return "hello";
}
  • 提交对象

    • 首先要有对应的实体类,User
    • 提交数据:/user?name=xxx&id=xx&age=xx
    • 处理方法
    @RequestMapping("/user")
    public String user(User user){
       System.out.println(user);
       return "hello";
    }
    
    • 如果参数名不一致,则对应成员变量的值为null
6.2.2、前端显示数据
  • ModelAndView
public class ControllerTest1 implements Controller {

   public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
       //返回一个模型视图对象
       ModelAndView mv = new ModelAndView();
       mv.addObject("msg","ControllerTest1");
       mv.setViewName("test");
       return mv;
  }
}
  • ModelMap
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap model){
   //封装要显示到视图中的数据
   //相当于req.setAttribute("name",name);
   model.addAttribute("name",name);
   System.out.println(name);
   return "hello";
}
  • Model
@RequestMapping("/ct2/hello")
public String hello(@RequestParam("username") String name, Model model){
   //封装要显示到视图中的数据
   //相当于req.setAttribute("name",name);
   model.addAttribute("msg",name);
   System.out.println(name);
   return "test";
}
  • 对比
    • Model 只有几个方法只适合用于储存数据
    • ModelMap 继承了 LinkedMap ,继承 LinkedMap 的方法和特性
    • ModelAndView 可以在储存数据,可以设置返回的逻辑视图,进行控制展示层的跳转
6.3.3、乱码问题
  • Spring 内置
<filter>
   <filter-name>encoding</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>encoding</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>
  • 通用解决方案
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;

/**
* 解决get和post请求 全部乱码的过滤器
*/
@WebFilter("/*")
public class GenericEncodingFilter implements Filter {

   @Override
   public void destroy() {
  }

   @Override
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
       //处理response的字符编码
       HttpServletResponse myResponse=(HttpServletResponse) response;
       myResponse.setContentType("text/html;charset=UTF-8");

       // 转型为与协议相关对象
       HttpServletRequest httpServletRequest = (HttpServletRequest) request;
       // 对request包装增强
       HttpServletRequest myrequest = new MyRequest(httpServletRequest);
       chain.doFilter(myrequest, response);
  }

   @Override
   public void init(FilterConfig filterConfig) throws ServletException {
  }

}

//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {

   private HttpServletRequest request;
   //是否编码的标记
   private boolean hasEncode;
   //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
   public MyRequest(HttpServletRequest request) {
       super(request);// super必须写
       this.request = request;
  }

   // 对需要增强方法 进行覆盖
   @Override
   public Map getParameterMap() {
       // 先获得请求方式
       String method = request.getMethod();
       if (method.equalsIgnoreCase("post")) {
           // post请求
           try {
               // 处理post乱码
               request.setCharacterEncoding("utf-8");
               return request.getParameterMap();
          } catch (UnsupportedEncodingException e) {
               e.printStackTrace();
          }
      } else if (method.equalsIgnoreCase("get")) {
           // get请求
           Map<String, String[]> parameterMap = request.getParameterMap();
           if (!hasEncode) { // 确保get手动编码逻辑只运行一次
               for (String parameterName : parameterMap.keySet()) {
                   String[] values = parameterMap.get(parameterName);
                   if (values != null) {
                       for (int i = 0; i < values.length; i++) {
                           try {
                               // 处理get乱码
                               values[i] = new String(values[i]
                                      .getBytes("ISO-8859-1"), "utf-8");
                          } catch (UnsupportedEncodingException e) {
                               e.printStackTrace();
                          }
                      }
                  }
              }
               hasEncode = true;
          }
           return parameterMap;
      }
       return super.getParameterMap();
  }

   //取一个值
   @Override
   public String getParameter(String name) {
       Map<String, String[]> parameterMap = getParameterMap();
       String[] values = parameterMap.get(name);
       if (values == null) {
           return null;
      }
       return values[0]; // 取回参数的第一个值
  }

   //取所有值
   @Override
   public String[] getParameterValues(String name) {
       Map<String, String[]> parameterMap = getParameterMap();
       String[] values = parameterMap.get(name);
       return values;
  }
}

6.3、JSON

6.3.1、Jackson
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.9.8</version>
</dependency>
  • 关键对象:ObjectMapper
  • @RequestMaping的produces属性设置MIME类型
  • @RequestMapping(value = “/json1”,produces = “application/json;charset=utf-8”)
  • @ResponseBody:不经过视图解析器解析,给前端传输数据
  • @RestController:该类下所有方法都不走视图解析器,只返回字符串
6.3.2、Fastjson
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>fastjson</artifactId>
   <version>1.2.60</version>
</dependency>
  • JSONObject 代表 json 对象
  • JSONArray 代表 json 对象数组
  • JSON代表 JSONObject和JSONArray的转化
System.out.println("*******Java对象 转 JSON字符串*******");
       String str1 = JSON.toJSONString(list);
       System.out.println("JSON.toJSONString(list)==>"+str1);
       String str2 = JSON.toJSONString(user1);
       System.out.println("JSON.toJSONString(user1)==>"+str2);

       System.out.println("\n****** JSON字符串 转 Java对象*******");
       User jp_user1=JSON.parseObject(str2,User.class);
       System.out.println("JSON.parseObject(str2,User.class)==>"+jp_user1);

       System.out.println("\n****** Java对象 转 JSON对象 ******");
       JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2);
       System.out.println("(JSONObject) JSON.toJSON(user2)==>"+jsonObject1.getString("name"));

       System.out.println("\n****** JSON对象 转 Java对象 ******");
       User to_java_user = JSON.toJavaObject(jsonObject1, User.class);
       System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>"+to_java_user);
6.3.3、JsonUtils
public class JsonUtils {
   
   public static String getJson(Object object) {
       return getJson(object,"yyyy-MM-dd HH:mm:ss");
  }

   public static String getJson(Object object,String dateFormat) {
       ObjectMapper mapper = new ObjectMapper();
       //不使用时间差的方式
       mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
       //自定义日期格式对象
       SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
       //指定日期格式
       mapper.setDateFormat(sdf);
       try {
           return mapper.writeValueAsString(object);
      } catch (JsonProcessingException e) {
           e.printStackTrace();
      }
       return null;
  }
}
6.3.4、JSON乱码
<mvc:annotation-driven>
   <mvc:message-converters register-defaults="true">
       <bean class="org.springframework.http.converter.StringHttpMessageConverter">
           <constructor-arg value="UTF-8"/>
       </bean>
       <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
           <property name="objectMapper">
               <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                   <property name="failOnEmptyBeans" value="false"/>
               </bean>
           </property>
       </bean>
   </mvc:message-converters>
</mvc:annotation-driven>

7、拦截器

7.1、概念

  • 拦截器是 AOP 方法的具体应用
  • 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
  • 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的

7.2、使用

7.2.1、MyInterceptor

创建一个类,并且实现 HandlerInterceptor 接口

public class MyInterceptor implements HandlerInterceptor {

    // return true; 执行下一个拦截器,放行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("------------处理前------------");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("------------处理后------------");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("------------清理------------");
    }

}

7.2.2、ApplicationContext.xml

在springmvc的配置文件中配置拦截器

<!--拦截器配置-->
<mvc:interceptors>
    <mvc:interceptor>
        <!--过滤这个请求下的所有请求-->
        <mvc:mapping path="/**"/>
        <!--哪个类来过滤请求-->
        <bean class="com.yy.config.MyInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>

7.3、登录拦截

7.3.1、loginInterceptor
public class loginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //如果在登陆页面,放行
        if(request.getRequestURI().contains("login")){
            return true;
        }

        //获取session
        HttpSession session = request.getSession();

        if(session.getAttribute("username") != null){
            //已经登陆,放行
            return true;
        }
        
        //其他情况下转发到登陆页面
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
        return false;
    }
}
7.3.2、ApplicationContext.xml
    <!--拦截器配置-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--过滤这个请求下的所有请求-->
            <mvc:mapping path="/user/**"/>
            <!--哪个类来过滤请求-->
            <bean class="com.yy.config.loginInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
7.3.3、loginController
@Controller
@RequestMapping("/user")
public class loginController {

   @RequestMapping("/tomain")
   public String tomain(){
       return "main";
   }

    @RequestMapping("/tologin")
    public String tologin(){
        return "login";
    }

    @RequestMapping("/login")
    public String login(Model model,HttpSession session , String username, String password){
        if ("admin".equals(username) && "admin".equals(password)){
            //登陆成功后将用户保存在session中
            session.setAttribute("username",username);
            model.addAttribute("username",username);
            return "main";
        }else {
            return "login";
        }
    }

    @RequestMapping("/Exit")
    public String Exit(HttpSession session){
       //清除session
        session.invalidate();
        return "login";
    }
}

7.3.4、index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <a href="${pageContext.request.contextPath}/user/tologin">登陆页面</a>
  <a href="${pageContext.request.contextPath}/user/tomain">首页</a>
  </body>
</html>
7.3.5、login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登陆</title>
</head>
<body>
<h1>登陆页面</h1>
<form action="${pageContext.request.contextPath}/user/login" method="post">
    用户名:<input type="text" name="username">
    密码:<input type="text" name="password">
    <input type="submit">
</form>
</body>
</html>
7.3.6、main.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>首页</h1>
<h2>${username}</h2>
<h2><a href="${pageContext.request.contextPath}/user/Exit">注销</a></h2>
</body>
</html>

8、文件上传下载

8.1、概念

springMVC 可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver。

前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器;

对表单中的 enctype 属性:

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
  • text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。

8.2、使用

8.2.1、依赖
<!--文件上传-->
<dependency>
   <groupId>commons-fileupload</groupId>
   <artifactId>commons-fileupload</artifactId>
   <version>1.3.3</version>
</dependency>
<!--servlet-api导入高版本的-->
<dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <version>4.0.1</version>
</dependency>
8.2.2、ApplicationContext.xml
<!--这个bena的id必须为:multipartResolver , 否则上传文件会报400的错误!-->
<!--文件上传配置-->
<bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
   <!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
   <property name="defaultEncoding" value="utf-8"/>
   <!-- 上传文件大小上限,单位为字节(10485760=10M) -->
   <property name="maxUploadSize" value="10485760"/>
   <property name="maxInMemorySize" value="40960"/>
</bean>

CommonsMultipartFile 的 常用方法:

  • String getOriginalFilename():获取上传文件的原名
  • InputStream getInputStream():获取文件流
  • void transferTo(File dest):将上传文件保存到一个目录文件中
8.2.3、index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post">
    <input type="file" name="file"/>
    <input type="submit" value="upload">
  </form>
  <a href="/download">点击下载</a>
  </body>
</html>
8.2.4、FileController
@Controller
public class FileController {

    //@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象
    //批量上传CommonsMultipartFile则为数组即可
    @RequestMapping("/upload")
    public String fileUpload(@RequestParam("file") CommonsMultipartFile file , HttpServletRequest request) throws IOException {

        //获取文件名 : file.getOriginalFilename();
        String uploadFileName = file.getOriginalFilename();

        //如果文件名为空,直接回到首页!
        if ("".equals(uploadFileName)){
            return "redirect:/index.jsp";
        }
        System.out.println("上传文件名 : "+uploadFileName);

        //上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        //如果路径不存在,创建一个
        File realPath = new File(path);
        if (!realPath.exists()){
            realPath.mkdir();
        }
        System.out.println("上传文件保存地址:"+realPath);

        InputStream is = file.getInputStream(); //文件输入流
        OutputStream os = new FileOutputStream(new File(realPath,uploadFileName)); //文件输出流

        //读取写出
        int len=0;
        byte[] buffer = new byte[1024];
        while ((len=is.read(buffer))!=-1){
            os.write(buffer,0,len);
            os.flush();
        }
        os.close();
        is.close();

        return "redirect:/index.jsp";
    }
    /*
     * 采用file.Transto 来保存上传的文件
     */
    @RequestMapping("/upload2")
    public String  fileUpload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {

        //上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        File realPath = new File(path);
        if (!realPath.exists()){
            realPath.mkdir();
        }
        //上传文件地址
        System.out.println("上传文件保存地址:"+realPath);

        //通过CommonsMultipartFile的方法直接写文件(注意这个时候)
        file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));

        return "redirect:/index.jsp";
    }


    @RequestMapping(value="/download")
    public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{
        //要下载的图片地址
        String  path = request.getServletContext().getRealPath("/upload");
        // web 目录下创建 upload 文件夹
        String  fileName = "1.jpg";

        //1、设置response 响应头
        response.reset(); //设置页面不缓存,清空buffer
        response.setCharacterEncoding("UTF-8"); //字符编码
        response.setContentType("multipart/form-data"); //二进制传输数据
        //设置响应头
        response.setHeader("Content-Disposition",
                "attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));
        File file = new File(path,fileName);
        //2、 读取文件--输入流
        InputStream input=new FileInputStream(file);
        //3、 写出文件--输出流
        OutputStream out = response.getOutputStream();

        byte[] buff =new byte[1024];
        int index=0;
        //4、执行 写出操作
        while((index= input.read(buff))!= -1){
            out.write(buff, 0, index);
            out.flush();
        }
        out.close();
        input.close();
        return null;
    }
}

MyBatis

1、简介

  • MyBatis 是一款优秀的持久层框架
  • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程
  • MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类 【Plain Old Java Objects,普通的 Java对象】映射成数据库中的记录。
  • MyBatis 本是apache的一个开源项目ibatis, 2010年这个项目由apache 迁移到了google code,并且改名为MyBatis 。
  • 2013年11月迁移到Github .
  • Mybatis官方文档 : http://www.mybatis.org/mybatis-3/zh/index.html
  • GitHub : https://github.com/mybatis/mybatis-3

2、第一个MyBatis

  • 数据库
CREATE DATABASE mybatis;

USE mybatis;

CREATE TABLE USER(
	id INT(20) NOT NULL PRIMARY KEY,
	NAME VARCHAR(30) DEFAULT NULL,
	pwd VARCHAR(30) DEFAULT NULL
)ENGINE = INNODB DEFAULT CHARSET = utf8;

INSERT INTO USER(id,NAME,pwd) VALUES
(1,'小明','123456'),
(2,'小王','123456'),
(3,'小张','123456');
  • 导入依赖
<!--导入驱动-->
    <dependencies>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.26</version>
        </dependency>
        <!--mybatis驱动-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!--junit驱动-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <!--解决写的配置文件,无法被导出或者生效问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
  • 编写MyBatis配置文件 mybatis-config.xml
<!--configuration核心配置文件-->
<configuration>
    <environments default="development">
        <environment id="development">
            <!--事务管理-->
            <transactionManager type="JDBC"/>
            <!--数据库配置-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <!--每一个 Mapper.xml都需要在mybatis核心配置文件中注册!-->
    <mappers>
        <mapper resource="UserMapper.xml"/>
    </mappers>

</configuration>
  • 编写工具类MyBatisUtil
//sqlSessionFactory --> sqlSession
public class MyBatisUtil {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            //使用 MyBatis 获取 sqlSessionFactory 对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //有了sqlSessionFactory,就可以从中获取 sqlSession 的实例了
    //sqlSession 完全包含了面向数据库执行 SQL 命令所需要的所有方法
    public static SqlSession getsqlSession(){
        return sqlSessionFactory.openSession();
    }

}
  • 实体类

  • Dao接口

public interface UserDao {
    List<User> getUserList();
}
  • 测试类
public class UserDaoTest {
    @Test
    public void test(){
        SqlSession sqlSession = null;

        try {
            //获取执行 sql 的 sqlSession
            sqlSession = MyBatisUtil.getsqlSession();

            //方式一
            //获取mapper
            UserDao mapper = sqlSession.getMapper(UserDao.class);
            //执行方法
            List<User> userList = mapper.getUserList();
            for (User user : userList) {
                System.out.println(user);
            }

            //方法二
            // List<User> userList = sqlSession.selectList("com.y.dao.UserDao.getUserList");
            // for (User user : userList) {
            //     System.out.println(user);
            // }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭 sqlSession
            sqlSession.close();
        }
    }
}

3、CRUD

  • id:就是对应的namespace中的方法名

  • resultType:Sql 语句的返回值类型

  • parameterType:参数类型

  • 注意:RUD要注意事务提交 sqlSession.commit()

3.1、Select

    <!--查询所哟用户-->
    <select id="getUserList" resultType="com.y.pojo.User">
        select * from mybatis.user
    </select>
    <!--根据id查询用户-->
    <select id="getUserById" parameterType="int" resultType="com.y.pojo.User">
        select * from mybatis.user where id = #{id}
    </select>

3.2、Insert

<!--添加用户-->
<insert id="addUser" parameterType="com.y.pojo.User">
    insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>

3.3、Update

<!--修改用户-->
<update id="updateUser" parameterType="com.y.pojo.User">
    update mybatis.user set name = #{name},pwd = #{pwd} where id = #{id};
</update>

3.4、Delete

<!--删除用户-->
<delete id="deleteUser" parameterType="int">
    delete from mybatis.user where id = #{id}
</delete>

4、配置解析

4.1、核心配置文件

  • mybatis-config.xml 系统核心配置文件
  • MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
  • 能配置的内容如下:
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
<!-- 注意元素节点的顺序!顺序不对会报错 -->

4.2、configuration

<environments default="development">
 <environment id="development">
   <transactionManager type="JDBC">
     <property name="..." value="..."/>
   </transactionManager>
   <dataSource type="POOLED">
     <property name="driver" value="${driver}"/>
     <property name="url" value="${url}"/>
     <property name="username" value="${username}"/>
     <property name="password" value="${password}"/>
   </dataSource>
 </environment>
</environments>
  • 可以配套多套运行环境,将sql映射到不同的数据库上,但是必须使用default指定一个默认运行环境
  • transactionManager :事务管理器
<transactionManager type="[ JDBC | MANAGED ]"/>
  • dataSource:数据源

    <dataSource type="[UNPOOLED|POOLED|JNDI]">
    
    • unpooled:这个数据源的实现只是每次被请求时打开和关闭连接。
    • pooled:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来 , 这是一种使得并发 Web 应用快速响应请求的流行处理方式。
    • jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
    • 数据源也有很多第三方的实现,比如dbcp,c3p0,druid等等

4.3、Properties

  • 新建db.properties配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8
username=root
password=123456
  • 将文件导入properties 配置文件
<properties resource="db.properties"/>

4.4、typeAliases

<!--起别名,实体类较少时可以使用-->
<typeAliases>
    <typeAlias type="com.yy.pojo.User" alias="User"/>
</typeAliases
<!--扫包,默认别名就为类名首字母小写,较多时可以-->
<typeAliases>
    <package name="com.yy.pojo"/>
</typeAliases>
//使用注解
@Alias("user")
public class User {
  ...
}

4.5、mappers

  • 映射器 : 定义映射SQL语句文件

  • 引入资源方式

    <!-- 使用相对于类路径的资源引用 -->
    <mappers>
     <mapper resource="org/mybatis/builder/PostMapper.xml"/>
    </mappers>
    
    <!-- 使用完全限定资源定位符(URL) -->
    <mappers>
     <mapper url="file:///var/mappers/AuthorMapper.xml"/>
    </mappers>
    
    <!--
    使用映射器接口实现类的完全限定类名
    需要配置文件名称和接口名称一致,并且位于同一目录下
    -->
    <mappers>
     <mapper class="org.mybatis.builder.AuthorMapper"/>
    </mappers>
    
    <!--
    将包内的映射器接口实现全部注册为映射器
    但是需要配置文件名称和接口名称一致,并且位于同一目录下
    -->
    <mappers>
     <package name="org.mybatis.builder"/>
    </mappers>
    
  • Mapper文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
       PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
       "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yy.mapper.UserMapper">
   
</mapper>
  • namespace
    • namespace的命名必须跟某个接口同名
    • 接口中的方法与映射文件中sql语句id应该一一对应
    • namespace和子元素的id联合保证唯一 , 区别不同的mapper
    • 绑定DAO接口
    • namespace命名规则 : 包名+类名

4.6、其他配置

4.6.1、settings
  • 懒加载
  • 日志实现
  • 缓存开启关闭
4.6.2、typeHandlers
  • 类型处理器
  • 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
  • 你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型
4.6.3、ObjectFactory
  • 对象工厂
  • MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。
  • 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过有参构造方法来实例化。
  • 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。

5、作用域和生命周期

图片

  • SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
  • SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。
    • 由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。
    • 因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。
  • SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try…catch…finally… 语句来保证其正确关闭,所以 SqlSession 的最佳的作用域是请求或方法作用域

6、ResultMap

  • 解决属性名和字段名不一致

6.1、指定别名

  • 为列名指定别名 , 别名和java实体类的属性名一致 .
<select id="selectUserById" resultType="User">
  select id , name , pwd as password from user where id = #{id}
</select>

6.2、ResultMap

结果集映射

<resultMap id="UserMap" type="User">
   <!-- column是数据库表的列名 , property是对应实体类的属性名 -->
   <!-- 什么不一样映射什么 -->
   <!-- id为主键 -->
   <id column="id" property="id"/>
   <result column="name" property="name"/>
   <result column="pwd" property="password"/>
</resultMap>

<select id="selectUserById" resultMap="UserMap">
  select id , name , pwd from user where id = #{id}
</select>

7、分页

7.1、Limit
  • 接口
//分页查询用户
List<User> getUserByPage(@Param("start") int start,@Param("PageSize") int PageSize);
  • UserMapper.xml
<!--分页查询用户-->
<select id="getUserByPage" parameterType="int" resultType="user">
    select * from mybatis.user limit #{start},#{PageSize}
</select>
  • 测试
public void test_getUserByPage(){
    SqlSession sqlSession = MyBatisUtil.getsqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> users = mapper.getUserByPage(0, 2);
    for (User user : users) {
        System.out.println(user);
    }
    sqlSession.close();
}
7.2、RowBounds
  • 接口
List<User> getUserByRowBounds();
  • UserMapper.xml
<!--分页查询用户-->
<select id="getUserByRowBounds" resultType="user">
    select * from mybatis.user
</select>
  • 测试
public void test_getUserByRowBounds(){
    SqlSession sqlSession = MyBatisUtil.getsqlSession();

    //RowBounds 实现
    RowBounds rowBounds = new RowBounds(1, 2);
    List<User> userList = sqlSession.selectList("com.yy.dao.UserMapper.getUserByRowBounds",null,rowBounds);

    for (User user : userList) {
        System.out.println(user);
    }
    sqlSession.close();
}
7.3、MyBatis分页插件

MyBatis 分页插件 PageHelper

8、日志

日志工厂

Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j
  • JDK logging

具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。

8.1、STDOUT_LOGGING

  • 标准日志输出
<!--设置日志-->
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

8.2、Log4j

  • 可以控制日志信息输送的目的地:控制台,文本,GUI组件…
  • 控制每一条日志的输出格式
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程

1、导入依赖

<dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>1.2.17</version>
</dependency>

2、创建配置文件

  • log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/yy.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

3、配置Log4j为日志实现

<!--设置日志-->
<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

4、实现

//导包:org.apache.log4j.Logger
//获取日志对象
static Logger logger = Logger.getLogger(UserDaoTest.class);
public void test_log4j(){
    logger.info("info:进入了TestLog4J");
    logger.debug("debug:进入了TestLog4J");
    logger.error("error:进入了TestLog4J");
}

9、注解开发

  • 可以在工具类编写中选择提交事务:

    return sqlSessionFactory.openSession(true);
    
  • mybatis-config 绑定接口

<!--绑定接口-->
<mappers>
    <mapper class="com.yy.dao.UserMapper"></mapper>
</mappers>
  • 接口增删改查
public interface UserMapper {

    //查询所有数据
    @Select("select * from user")
    List<User> getUserList();

    //根据id查询
    @Select("select * from user where id = #{id}")
    User getUserById(@Param("id") int id);

    //增加一个用户
    @Insert("insert into user (id,name,pwd) values (#{id},#{name},#{pwd})")
    int addUser(User user);

    //删除一个用户
    @Delete("delete from user where id = #{id}")
    int deleteUser(int id);

    //修改用户数据
    @Update("update user set name = #{name},pwd = #{pwd} where id = #{id}")
    void updateUser(User user);

    //分页查询用户数据
    @Select("select * from user limit #{start},#{PageSize}")
    List<User> findUserByPage(@Param("start") int start,@Param("PageSize") int PageSize);
}

10、MyBatis执行流程

图片

11、Lombok

  1. idea 安装Lombok 插件

  2. 导入 Lombok 依赖

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.16</version>
        <scope>provided</scope>
    </dependency>
    
  3. 使用

    @Data//GET,SET,ToString,Hashcode,equals
    public class Student {
       private int id;
       private String name;
    }
    
    @AllArgsConstructor//有参构造
    @NoArgsConstructor//无参构造
    

12、复杂查询

12.1、多对一

查询所有的学生信息(包括对应老师信息)

环境搭建:

  • 创建表
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师');

CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8


INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');
  • 实体类
@Data
public class Student {
    private int id;
    private String name;

    //学生关联老师
    private Teacher teacher;
}

  • 接口
  • 核心配置文件 mybatis-config
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!--configuration核心配置文件-->
<configuration>

    <!--设置日志-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--<setting name="logImpl" value="LOG4J"/>-->
    </settings>
    <!--引入外部配置文件-->
    <properties resource="db.properties"/>
    <!--扫包,默认别名就为类名首字母小写,较多时可以-->
    <typeAliases>
        <package name="com.yy.pojo"/>
    </typeAliases>


    <environments default="development">
        <environment id="development">
            <!--事务管理-->
            <transactionManager type="JDBC"/>
            <!--数据库配置-->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--绑定接口-->
    <mappers>
        <!--<mapper resource="com/yy/dao/*Mapper.xml"></mapper>-->
        <mapper class="com.yy.dao.StudentMapper"></mapper>
        <mapper class="com.yy.dao.TeacherMapper"></mapper>
    </mappers>
</configuration>
12.1.1、子查询
<!--根据查询出来的学生tid查询老师  子查询-->
<select id="getStudent" resultMap="Student_Teacher">
    select * from student
</select>
<resultMap id="Student_Teacher" type="Student">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <!--复杂的属性,我们要单独处理-->
    <!--对象用 association 集合使用 collection-->
    <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>

<select id="getTeacher" resultType="Teacher">
    select * from teacher where id = #{id}
</select>
12.1.2、嵌套查询
<!--结果集嵌套查询-->
<select id="getStudent2" resultMap="Student_Teacher_2">
    select s.id sid,s.name sname,t.name tname
    from student s,teacher t
    where s.tid = t.id
</select>
<resultMap id="Student_Teacher_2" type="Student">
    <result property="id" column="sid"/>
    <result property="name" column="sname"/>
    <association property="teacher" javaType="Teacher">
        <result property="name" column="tname"/>
    </association>
</resultMap>

12.2、一对多

环境搭建

实体类

@Data
public class Teacher {
    private int id;
    private String name;

    //一个老师拥有多个学生
    private List<Student> studentList;
}

12.2.1、子查询
<select id="getTeacherById" resultMap="Teacher_Student">
    select s.id sid,s.name sname,t.name tname,t.id tid
    from student s,teacher t
    where s.tid = t.id and t.id = #{tid}
</select>
<resultMap id="Teacher_Student" type="Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <collection property="studentList" ofType="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tid" column="tid"/>
    </collection>
</resultMap>
12.2.2、嵌套查询
<select id="getTeacherById2" resultMap="Teacher_Student_2">
    select * from teacher where id = #{tid}
</select>
<resultMap id="Teacher_Student_2" type="Teacher">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <collection property="studentList" column="id" ofType="Student" select="getStudent"/>
</resultMap>
<select id="getStudent" resultType="Student">
    select * from student where tid = #{tid}
</select>

13、动态Sql

搭建环境

  • 创建表
CREATE TABLE `blog` (
`id` varchar(50) NOT NULL COMMENT '博客id',
`title` varchar(100) NOT NULL COMMENT '博客标题',
`author` varchar(30) NOT NULL COMMENT '博客作者',
`create_time` datetime NOT NULL COMMENT '创建时间',
`views` int(30) NOT NULL COMMENT '浏览量'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
  • 实体类
@Data
public class Blog {
    private int id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}

  • 接口
  • 核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!--configuration核心配置文件-->
<configuration>

    <!--引入外部配置文件-->
    <properties resource="db.properties"/>
    
    <!--设置日志-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--开启自动驼峰命名规则映射,将数据库字段名 _ 后字母改为大写 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--<setting name="logImpl" value="LOG4J"/>-->
    </settings>
    <!--起别名,实体类较少时可以使用-->
    <!--<typeAliases>-->
    <!--    <typeAlias type="com.User" alias="User"/>-->
    <!--</typeAliases>-->
    <!--扫包,默认别名就为类名首字母小写,较多时可以-->
    <typeAliases>
        <package name="com.yy.pojo"/>
    </typeAliases>


    <environments default="development">
        <environment id="development">
            <!--事务管理-->
            <transactionManager type="JDBC"/>
            <!--数据库配置-->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--绑定接口-->
    <mappers>
        <!--<mapper resource="com/yy/dao/*Mapper.xml"></mapper>-->
        <mapper class="com.yy.dao.BlogMapper"></mapper>
    </mappers>

</configuration>

13.1、if

BlogMapper.xml

<select id="queryBlogIf" resultType="Blog">
    select * from blog where 1 = 1
    <if test="title != null">
        and title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</select>

测试类

public void test_queryBlogIf(){
    SqlSession sqlSession = MyBatisUtil.getsqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap<String, String> map = new HashMap<>();
    // map.put("title","Mybatis如此简单");
    map.put("author","xxx");
    List<Blog> blogList = mapper.queryBlogIf(map);
    for (Blog blog : blogList) {
        System.out.println(blog);
    }
    sqlSession.close();
}

13.2、Where

<select id="queryBlogIf" parameterType="map" resultType="blog">
  select * from blog
   <where>
       <if test="title != null">
          title = #{title}
       </if>
       <if test="author != null">
          and author = #{author}
       </if>
   </where>
</select>

where标签会知道如果它包含的标签中有返回值的话,它就插入一个 where 。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。

13.3、Set

<!--注意set是用的逗号隔开-->
<update id="updateBlog" parameterType="map">
  update blog
     <set>
         <if test="title != null">
            title = #{title},
         </if>
         <if test="author != null">
            author = #{author}
         </if>
     </set>
  where id = #{id};
</update>

13.4、choose

<select id="queryBlogChoose" parameterType="map" resultType="blog">
  select * from blog
   <where>
       <choose>
           <when test="title != null">
                title = #{title}
           </when>
           <when test="author != null">
              and author = #{author}
           </when>
           <otherwise>
              and views = #{views}
           </otherwise>
       </choose>
   </where>
</select>

不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句

13.5、Sql 片段

  • 提取 sql 片段
<sql id="if-title-author">
   <if test="title != null">
      title = #{title}
   </if>
   <if test="author != null">
      and author = #{author}
   </if>
</sql>
  • 引用 sql 片段
<select id="queryBlogIf" parameterType="map" resultType="blog">
  select * from blog
   <where>
       <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
       <include refid="if-title-author"></include>
       <!-- 在这里还可以引用其他的 sql 片段 -->
   </where>
</select>

注意:

  • 单表定义 sql 片段
  • sql 片段中不要包含 where

13.6、Foreach

<select id="queryBlogForeach" parameterType="map" resultType="blog">
  select * from blog
   <where>
       <!--
       collection:指定输入对象中的集合属性
       item:每次遍历生成的对象
       open:开始遍历时的拼接字符串
       close:结束时拼接的字符串
       separator:遍历对象之间需要拼接的字符串
       select * from blog where 1=1 and (id=1 or id=2 or id=3)
     -->
       <foreach collection="ids"  item="id" open="and (" close=")" separator="or">
          id=#{id}
       </foreach>
   </where>
</select>
public void testQueryBlogForeach(){
   SqlSession session = MybatisUtils.getSession();
   BlogMapper mapper = session.getMapper(BlogMapper.class);

   HashMap map = new HashMap();
   List<Integer> ids = new ArrayList<Integer>();
   ids.add(1);
   ids.add(2);
   ids.add(3);
   map.put("ids",ids);

   List<Blog> blogs = mapper.queryBlogForeach(map);

   System.out.println(blogs);

   session.close();
}

14、缓存

14.1、一级缓存

  • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
  • 一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它,可以手动清除session.clearCache()
  • 每个sqlSession中的缓存相互独立
  • 执行 URD 语句后会重新查数据库

14.2、二级缓存

  • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
  • 每次会话执行一次查询语句,会将缓存放入当前会话的一级缓存中,会话结束后,一级缓存中的数据存放到二级缓存中
  • 新的会话查询信息,从二级缓存中获取内容,如果没有查询一级缓存,再没有则查询数据库

使用步骤:

  1. 开启全局缓存【mybatis-config.xml】

    <setting name="cacheEnabled" value="true"/>
    
  2. 每个mapper.xml 配置使用二级缓存

    <cache/>
    
    <cache
     eviction="FIFO"
     flushInterval="60000"
     size="512"
     readOnly="true"/>
    

    创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

14.3、Cache

  • 我们可以通过实现Cache接口来自定义二级缓存
  • Ehcache是一种广泛使用的java分布式缓存,用于通用缓存

使用步骤

  1. 导入依赖

    <dependency>
       <groupId>org.mybatis.caches</groupId>
       <artifactId>mybatis-ehcache</artifactId>
       <version>1.1.0</version>
    </dependency>
    
  2. mapper.xml

    <mapper namespace = “org.acme.FooMapper” >
       <cache type = “org.mybatis.caches.ehcache.EhcacheCache” />
    </mapper>
    
  3. ehcache.xml文件,如果在加载时未找到/ehcache.xml资源或出现问题,则将使用默认配置。

    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
            updateCheck="false">
       <!--
          diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
          user.home – 用户主目录
          user.dir – 用户当前工作目录
          java.io.tmpdir – 默认临时文件路径
        -->
       <diskStore path="./tmpdir/Tmp_EhCache"/>
       
       <defaultCache
               eternal="false"
               maxElementsInMemory="10000"
               overflowToDisk="false"
               diskPersistent="false"
               timeToIdleSeconds="1800"
               timeToLiveSeconds="259200"
               memoryStoreEvictionPolicy="LRU"/>
    
       <cache
               name="cloud_user"
               eternal="false"
               maxElementsInMemory="5000"
               overflowToDisk="false"
               diskPersistent="false"
               timeToIdleSeconds="1800"
               timeToLiveSeconds="1800"
               memoryStoreEvictionPolicy="LRU"/>
       <!--
          defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
        -->
       <!--
         name:缓存名称。
         maxElementsInMemory:缓存最大数目
         maxElementsOnDisk:硬盘最大缓存个数。
         eternal:对象是否永久有效,一但设置了,timeout将不起作用。
         overflowToDisk:是否保存到磁盘,当系统当机时
         timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
         timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
         diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
         diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
         diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
         memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
         clearOnFlush:内存数量最大时是否清除。
         memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
         FIFO,first in first out,这个是大家最熟的,先进先出。
         LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
         LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
      -->
    </ehcache>
    

框架整合

1、pom.xml

<!--依赖:junit,数据库驱动,连接池,servlet,jsp,mybatis,mybatis-spring,spring-->
    <dependencies>
        <!--Junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!-- 数据库连接池 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>

        <!--Servlet - JSP -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!--Mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>

        <!--Spring-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
    </dependencies>

    <!--静态资源导出-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

2、database.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=false&useUnicode=true&characterEncoding=utf8
jdbc.name=root
jdbc.password=root

3、mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--配置数据源,交给 Spring 去做-->
    <!--日志-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <typeAliases>
        <package name="com.yy.pojo"/>
    </typeAliases>

    <!--配置要管理的mapper-->
    <mappers>
        <mapper class="com.yy.dao.BookMapper"/>
    </mappers>
</configuration>

4、spring-dao.xml

<?xml version="1.0" encoding="UTF-8"?>
<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"
       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">

    <!--1.关联数据库文件-->
    <context:property-placeholder location="classpath:database.properties"/>
    <!--2.连接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.name}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- c3p0连接池的私有属性 -->
        <property name="maxPoolSize" value="30"/>
        <property name="minPoolSize" value="10"/>
        <!-- 关闭连接后不自动commit -->
        <property name="autoCommitOnClose" value="false"/>
        <!-- 获取连接超时时间 -->
        <property name="checkoutTimeout" value="10000"/>
        <!-- 当获取连接失败重试次数 -->
        <property name="acquireRetryAttempts" value="2"/>
    </bean>

    <!--3.sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入数据库连接池 -->
        <property name="dataSource" ref="dataSource"/>
        <!--绑定Mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!--4.配置dao接口扫描包,动态实现Dao接口注入到Spring容器中-->
    <!--解释 :https://www.cnblogs.com/jpfss/p/7799806.html-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--注入 sqlSessionFactory-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!--要扫描的dao包-->
        <property name="basePackage" value="com.yy.dao"/>
    </bean>
</beans>

5、spring-service.xml

<?xml version="1.0" encoding="UTF-8"?>
<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"
       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">

    <!--1.扫描service下的包-->
    <context:component-scan base-package="com.yy.service"/>

    <!--2.BookServiceImpl注入到IOC容器中-->
    <bean id="BookServiceImpl" class="com.yy.service.BookServiceImpl">
        <property name="bookMapper" ref="bookMapper"/>
    </bean>

    <!--3.声明事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--4.aop 事务支持-->

</beans>

6、spring-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<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">

    <!--1.注解驱动-->
    <mvc:annotation-driven/>
    <!--2.静态资源过滤-->
    <mvc:default-servlet-handler/>
    <!--3.扫描包::controller-->
    <context:component-scan base-package="com.yy.controller"/>
    <!--4.视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

7、applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<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">

    <import resource="spring-dao.xml"/>
    <import resource="spring-service.xml"/>
    <import resource="spring-mvc.xml"/>
</beans>

8、web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0">

    <!--DispatchServlet-->
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <!--跟随 Tomcat 一起启动-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--乱码过滤-->
    <filter>
        <filter-name>encodingFilter</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>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--Session-->
    <session-config>
        <session-timeout>15</session-timeout>
    </session-config>
    
</web-app>

work.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”>

<!--1.注解驱动-->
<mvc:annotation-driven/>
<!--2.静态资源过滤-->
<mvc:default-servlet-handler/>
<!--3.扫描包::controller-->
<context:component-scan base-package="com.yy.controller"/>
<!--4.视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>
```

7、applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<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">

    <import resource="spring-dao.xml"/>
    <import resource="spring-service.xml"/>
    <import resource="spring-mvc.xml"/>
</beans>

8、web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0">

    <!--DispatchServlet-->
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <!--跟随 Tomcat 一起启动-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--乱码过滤-->
    <filter>
        <filter-name>encodingFilter</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>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--Session-->
    <session-config>
        <session-timeout>15</session-timeout>
    </session-config>
    
</web-app>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值