Spring+SpringMVC+SpringBoot+Mybatis Plus框架整合笔记

框架整合

基础框架Spring Framework

  • Spring Framework 是Spring生态圈最基础的项目,是其他Spring框架的根基

image-20231212143111300

  • 学习路线

核心容器

核心概念(IoC/DI) 目标:充分解耦

  • IoC(Inversion of Control)控制反转

    • 对象的创建控制权由程序转移到外部(使用对象时,由主动new产生的对象转换未由外部提供对象,此过程中对象创建控制器由程序转移到了外部,此思想称为控制反转)

    • Spring 技术对IoC思想进行了实现,提供一个容器(IoC),用来充当外部

    • IoC容器负责对象的创建,初始化等一些列工作,被创建或者被管理的对象在IoC容器中统称为Bean

  • DI(Dependency Injection) 依赖注入

    • 在容器中建立Bean和Bean之间的依赖关系的整个过程,称为依赖注入

IoC入门

1.导入xml创建的Maven依赖(导入Spring的坐标spring-context)

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

2.配置bean

<?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">

    <!--2.配置bean
    bean标签标示配置bean
    id属性标示给bean的名字
    class属性表示给bean定义类型-->
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl"/>
</beans>

3.获取IoC容器

4.获取bean

        //3.获取IoC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //4.获取bean
        //BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        //bookDao.save();

        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();

DI入门

5.删除new创建dao方法

6.提供对应的set方法

public class BookServiceImpl implements BookService {
    //private BookDao bookDao = new BookDaoImpl();
    //5.删除new创建dao方法
    private BookDao bookDao;

    //6.提供对应的set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    @Override
    public void save() {
        System.out.println("book service save...");
        bookDao.save();
    }
}

7.配置server与 dao的关系

    <!--2.配置bean
    bean标签标示配置bean
    id属性标示给bean的名字
    class属性表示给bean定义类型-->
    <bean id="bookDao1" class="com.itheima.dao.impl.BookDaoImpl"/>
    <bean id="bookService" name="service service2" class="com.itheima.service.impl.BookServiceImpl">
        <!--7.配置server与 dao的关系-->
        <!--property标签表示配置当前bean的属性
        name属性标识配置哪一个具体的属性(setBookDao的名字部分)
        ref属性标识参照哪一个bean-->
        <property name="bookDao" ref="bookDao1"/>
    </bean>

        //bookService service效果是一样的,可以取id值也能取name值
        BookService bookService = (BookService) ctx.getBean("service4");
        bookService.save();
//bean名称错误:Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'service4' available

Bean作用范围配置

本质是对象

  • scope
    • 默认为singleton单例模式(从IoC获取的多个对象地址相同)
    • 可以改成prototype非单例模式
    <bean id="bookService" name="service service2" class="com.itheima.service.impl.BookServiceImpl" scope="singleton">

适合交给容器进行管理的bean

  • 表现层对象
  • 业务层对象
  • 数据层对象
  • 工具对象

不适合交给容器进行管理的bean

  • 封装实体的域对象(因为可能要存不同的值)

bean实例化(三种方式)

常用-构造方法
<!--构造方法实现-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
public class BookDaoImpl implements BookDao {
    public BookDaoImpl() {
        System.out.println("book constructor is running...");
    }

    @Override
    public void save() {
        System.out.println("book dao save...");
    }
}

无参构造方法如果不存在,将抛出异常BeanCreationException

分析Spring的错误信息

接下来,我们主要研究下Spring的报错信息来学一学如阅读。

  • 错误信息从下往上依次查看,因为上面的错误大都是对下面错误的一个包装,最核心错误是在最下面
  • Caused by: java.lang.NoSuchMethodException: com.itheima.dao.impl.BookDaoImpl.<init>()
    • Caused by 翻译为引起,即出现错误的原因
    • java.lang.NoSuchMethodException:抛出的异常为没有这样的方法异常
    • com.itheima.dao.impl.BookDaoImpl.<init>():哪个类的哪个方法没有被找到导致的异常,<init>()指定是类的构造方法,即该类的无参构造方法

如果最后一行错误获取不到错误信息,接下来查看第二层:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.itheima.dao.impl.BookDaoImpl]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.itheima.dao.impl.BookDaoImpl.<init>()

  • nested:嵌套的意思,后面的异常内容和最底层的异常是一致的
  • Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.itheima.dao.impl.BookDaoImpl]: No default constructor found;
    • Caused by: 引发
    • BeanInstantiationException:翻译为bean实例化异常
    • No default constructor found:没有一个默认的构造函数被发现

看到这其实错误已经比较明显,给大家个练习,把倒数第三层的错误分析下吧:

Exception in thread “main” org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘bookDao’ defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.itheima.dao.impl.BookDaoImpl]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.itheima.dao.impl.BookDaoImpl.<init>()。

至此,关于Spring的构造方法实例化就已经学习完了,因为每一个类默认都会提供一个无参构造函数,所以其实真正在使用这种方式的时候,我们什么也不需要做。这也是我们以后比较常用的一种方式。


方式二:静态工厂实例化bean(了解)
    <!--方式二:使用静态工厂实例化bean-->
    <bean id="orderDao" 
          class="com.itheima.factory.OrderDaoFactory" 
          factory-method="getOrderDao"/>
public class OrderDaoImpl implements OrderDao {

    public void save() {
        System.out.println("order dao save ...");
    }
}
-----------------------------------------
public class OrderDaoFactory {
    public static OrderDao getOrderDao(){
        System.out.println("factory setup....");
        return new OrderDaoImpl();
    }
}
方式三:实例工厂(了解)
    <!--方式三:使用实例工厂实例化bean-->
    <!--先创建工厂bean-->
     <bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
    <bean id="userDao" factory-bean="userFactory" factory-method="getUserDao"/>
public class UserDaoFactory {
    public UserDao getUserDao(){
        return new UserDaoImpl();
    }
}

public class AppForInstanceUser {
    public static void main(String[] args) {
//        //创建实例工厂对象
//        UserDaoFactory userDaoFactory = new UserDaoFactory();
//        //通过实例工厂对象创建对象
//        UserDao userDao = userDaoFactory.getUserDao();
//        userDao.save();


        //类路径获取xml文件
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //文件绝对路径访问
        //ApplicationContext ctx2 = new FileSystemXmlApplicationContext("D:\\MineFile\\zuoye\\code\\spring_quickstart\\src\\main\\resources\\applicationContext.xml");

        UserDao userDao1 = (UserDao) ctx.getBean("userDao");
        UserDao userDao2 = (UserDao) ctx.getBean("userDao");
        System.out.println(userDao1);
        System.out.println(userDao2);
        userDao1.save();

    }
}
方式四:使用FactoryBean实例化bean(实用)

方式三的变种

    <!--方式四:方式三的变种,使用FactoryBean实例化bean-->
    <bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
												造出来的是getObject对象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
    //代替原始实例工厂中创建对象的方法
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }

    public Class<?> getObjectType() {
        return UserDao.class;
    }
    public boolean isSingleton() {
        return true;//为true则为非单例模式
    }

}

-----------------
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        UserDao userDao1 = (UserDao) ctx.getBean("userDao");
        UserDao userDao2 = (UserDao) ctx.getBean("userDao");
        System.out.println(userDao1);
        System.out.println(userDao2);//单例
        userDao1.save();

bean生命周期

配置:

​ init-method

​ destroy-method

  • 初始化容器
    • 创建对象(内存分配)
    • 执行构造方法
    • 执行属性注入(set操作)
    • 执行bean初始化方法
  • 使用bean
    • 执行业务操作
  • 关闭/销毁容器
    • 执行bean销毁方法
    • 手动关闭容器
      • ConfigurableApplicationContext接口close()操作
      • ClassPathXmlApplicationContext继承自ConfigurableApplicationContext
    • 注册关闭钩子,在虚拟机退出前先关闭容器再退出虚拟机
      • ConfigurableApplicationContext接口registerShutdownHook()操作

依赖注入

方式

  • setter注入

    • 简单类型、

    • 引用类型

    • public class BookDaoImpl implements BookDao {
          private int connectionNum;
          private String databaseName;
          private BookDao bookDao;
      
          public void setConnectionNum(int connectionNum) {
              this.connectionNum = connectionNum;
          }
      
          public void setDatabaseName(String databaseName) {
              this.databaseName = databaseName;
          }
          public void setBookDao(BookDao bookDao) {
              this.bookDao = bookDao;
          }
      }
      

          <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
              <!--使用value注入简单类型数据和引用类型-->
              <property name="databaseName" value="mysql"/>
              <property name="bookDao" ref="bookDao"/>
              <property name="connectionNum" value="10"/>
          </bean>
      
  • 构造器注入

    • 简单类型

    • 引用类型

    • public class BookDaoImpl implements BookDao {
          private int connectionNum;
          private String databaseName;
      
          public BookDaoImpl(int connectionNum, String databaseName) {
              this.connectionNum = connectionNum;
              this.databaseName = databaseName;
              System.out.println("book constructor is running..." + connectionNum + ", " + databaseName);
          }
      }
      

    • 标准格式

      <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
              <constructor-arg name="connectionNum" value="100"/>
              <constructor-arg name="databaseName" value="mysql2"/>
          </bean>
      
          <bean id="bookService" name="service service2" class="com.itheima.service.impl.BookServiceImpl" scope="singleton">
              <constructor-arg name="bookDao" ref="bookDao"/>
              <constructor-arg name="userDao" ref="userDao"/>
          </bean>
      
    • 优化形参名称万一不一样问题(name改为type)

    • <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
              <constructor-arg type="int" value="100"/>
              <constructor-arg type="java.lang.String" value="mysql2"/>
          </bean>
      
          <bean id="bookService" name="service service2" class="com.itheima.service.impl.BookServiceImpl" scope="singleton">
              <constructor-arg name="bookDao" ref="bookDao"/>
              <constructor-arg name="userDao" ref="userDao"/>
          </bean>
      
    • 优化形参类型可能重复问题(加索引)

    • <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
              <constructor-arg index="0" value="100"/>
              <constructor-arg index="1" value="mysql2"/>
          </bean>
      
          <bean id="bookService" name="service service2" class="com.itheima.service.impl.BookServiceImpl" scope="singleton">
              <constructor-arg name="bookDao" ref="bookDao"/>
              <constructor-arg name="userDao" ref="userDao"/>
          </bean>
      

如果有必要可以两者同时使用,用构造器完成强制依赖的注入,setter完成可选依赖的注入

自己开发的模块推荐使用setter注入

依赖自动装配

只能用于引用类型的依赖注入,不能对简单类型进行操作

自动装配优先级低于setter和构造器,同时出现会自动装配失效

  • 按类型(常用)

    • 必须保证相同类型的bean唯一(推荐)

    • 注:相应的类(impl实现类)要有setter方法

      • xml配置文件里要有对应的实现bean()
      • 不能有两个相同的dao层实现类(<bean id="bookDao" class=“com.itheima.dao.impl.BookDaoImpl”/>和<bean id="bookDao2" class=“com.itheima.dao.impl.BookDaoImpl”/>)
    •     <bean class="com.itheima.dao.impl.BookDaoImpl"/>
          <!--autowire属性:开启自动装配,通常使用按类型装配-->
          <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
      
  • 按名称

    • 要保证实现dao的bean有对应id的名称在impl类里
    • 变量名和配置耦合(不推荐)
  • 按构造方法

  • 不启用自动装配

集合注入

数组,List,Set,Map,Properties

注:name属性是对应dao类的成员变量的值

​ 里面那个才是类型

    <bean id="orderDao" class="com.itheima.dao.impl.OrderDaoImpl">
        <property name="array">
            <array>
                <value>100</value>
                <value>200</value>
                <value>300</value>
            </array>
        </property>
        <property name="list">
            <list>
                <value>itcast</value>
                <value>itheima</value>
                <value>boxuegu</value>
            </list>
        </property>
        <property name="set">
            <set>
                <value>100</value>
                <value>200</value>
                <value>300</value>
            </set>
        </property>
        <property name="map">
            <map>
                <entry key="country" value="china"/>
                <entry key="province" value="henan"/>
                <entry key="city" value="kaifeng"/>
            </map>
        </property>
        <property name="properties">
            <props>
                <prop key="county">china</prop>
                <prop key="province">henan</prop>
                <prop key="city">kaifeng</prop>
            </props>
        </property>
    </bean>

加载properties文件

需要修改xml文件的位置,新增一个内容空间

image-20231213144227475

<!--1.开启context命名空间-->
    <context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
                                                                <!--不使用系统的变量模块,防止设置的变量名一样导致读取不正确-->
    <!--2.使用context空间加载properties文件-->
    <bean class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassLoader" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

容器创建

方式一:类路径加载配置文件

//类路径获取xml文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

方式二:文件路径加载配置文件

ApplicationContext ctx2 = new FileSystemXmlApplicationContext("D:\\...\\applicationContext.xml");

加载多个配置文件

ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml", "bean2.xml");

获取Bean

方式1:使用bean名称

//方式1:使用bean名称
BookDao bookDao = (BookDao) ctx.getBean("bookDao");

方式二:使用bean名称获取指定类型

//方式二:使用bean名称获取指定类型
BookDao bookDao = ctx.getBean("bookDao", BookDao.class);

方式三:使用bean类型获取,适用于只有一个同类型的bean

//方式三:使用bean类型获取,适用于只有一个同类型dao
BookDao bookDao = ctx.getBean(BookDao.class);

容器类层次结构图

image-20231213151814776

核心容器总结

容器相关

  • BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载

  • ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载

  • ApplicationContext提供基础的bean操作相关方法,通过其他接口扩展功能

  • ApplicationContext常用初始化类

    • ClassPathXmlApplicationContext
    • FileSystemXmlApplicationContext

bean相关

依赖注入相关

image-20231213152716932

注解开发定义bean

有对应名称的注解

xml配置,注解开发用的是context容器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<!--    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>-->
    <!--注解开发用扫描-->
    <context:component-scan base-package="com.itheima.dao.impl"/>
    <!--可改成扫描范围大一点,service包就也能扫描到-->
    <context:component-scan base-package="com.itheima"/>
</beans>

@Component("bookDao")
public class BookDaoImpl implements BookDao {
}
---
@Component//组件
public class BookServiceImpl implements BookService {
}
---------------------
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
衍生注解

@Controller:用于表现层bean定义

@Serveice:用于业务层bean定义

@Repository:用于数据层bean 定义

@Repository("bookDao")
public class BookDaoImpl implements BookDao {
}
@Serveice
public class BookDaoImpl implements BookDao {
}

纯注解开发(Spring3.0开始)

  • 使用了java类替代配置文件,开启了Spring快速开发赛道
  • java类代替Spring核心配置文件

右图的注解等价于左边灰的一片配置代码

<context:component-scan base-package=“com.itheima”/>该段等价于第二个注解

代替的java文件
//声明当前类为Spring配置类
@Configuration
//设置bean扫描路径,多个路径书写为字符串数组格式
@ComponentScan({"com.itheima.service","com.itheima.dao"})
//读取properties文件,可以直接使用里面的键值对,但不能使用通配符		*.properties
@PropertySource("jdbc.properties")
public class SpringConfig {
}
  • 创建方法

  •         //3.获取IoC容器
            ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
            //4.获取bean
            BookDao bookDao = (BookDao) ctx.getBean("bookDao");
            bookDao.save();
    

注解的bean生命周期和多例

@Repository     //等价于@Component("bookDao")
@Scope("prototype")//非单例模式开启
public class BookDaoImpl implements BookDao {

    @Override
    public void save() {
        System.out.println("book dao save...");
    }

    @PostConstruct
    public void init() {
        System.out.println("构造之后执行...");
    }
    @PreDestroy
    public void destroy() {
        System.out.println("销毁之前执行...");
    }
}
------------------------
    //3.获取IoC容器
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
    ctx.close();//该方法调用销毁前

注解开发的依赖注入

    @Autowired//该注解可以删除setter方法,该注解会自己创建
    @Qualifier("bookDao") //如果有多个相同类型的dao类,则用该注释指定
    private BookDao bookDao;
    private UserDao userDao;

    //6.提供对应的set方法
//    public void setBookDao(BookDao bookDao) {
//        this.bookDao = bookDao;
//    }

    @Override
    public void save() {
        System.out.println("book service save...");
        bookDao.save();
    }

注:自动装配基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据,因此无需提供setter方法

​ 自动装配建议使用无参构造方法创造对象(默认),如果不提供对应构造方法,请提供唯一的构造方法


赋值

@Repository     //等价于@Component("bookDao")
@Scope("prototype")//非单例模式开启
public class BookDaoImpl implements BookDao {

    @Value("itheima")//赋值
    private String name;

    @Override
    public void save() {
        System.out.println("book dao save..." + name);
    }

管理第三方bean(mysql)

  • 不推荐把所有config的java文件放在SpringConfig文件里
  • 推荐使用导入法

Jdbc配置文件

//声明当前类为Spring配置类
//@Configuration
public class JdbcConfig {
    //定义一个方法获得要管理的对象
    //添加bean,表示当前方法返回的是一个bean
    @Bean
    public DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/db1");
        ds.setName("root");
        ds.setPassword("root");
        return ds;
    }
}

主Config文件

//声明当前类为Spring配置类
@Configuration
//设置bean扫描路径,多个路径书写为字符串数组格式
@ComponentScan({"com.itheima.service","com.itheima.dao"		//,"com.itheima.config"})
@Import({JdbcConfig.class})//导入配置
public class SpringConfig {

}

简单依赖注入(用成员变量)

image-20231214093554858

引用类型依赖注入(用方法形参)

image-20231214093623874

XML配置比对注解配置

image-20231214094741163

整合MyBatis

  • spring整合数据层技术MyBatis

导入坐标

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.16</version>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-annotations-api</artifactId>
      <version>7.0.47</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.34</version>
    </dependency>
    <dependency>
      <!--spring操作jdbc需要的坐标-->
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
    <!--mybatis和spring整合需要的坐标-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.0</version>
    </dependency>

jdbc.properties文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jdbc.username=root
jdbc.password=root

JdbcConfig文件

public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}

在com.itheima.domain包里的
public class Account implements Serializable {
//在com.itheima.domain包里的
    private Integer id;
    private String name;
    private Double money;

MybatisConfig文件

public class MybatisConfig {
    //定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        //扫描数据需要存放的类型的包
        ssfb.setTypeAliasesPackage("com.itheima.domain");
        //在spring容器中自动装配的
        ssfb.setDataSource(dataSource);
        return ssfb;
    }
    //定义bean,返回MapperScannerConfigurer对象
    //等价于xml配置文件里的
    //    <mappers>
    //    <!--扫描mapper-->
    //    <package name="com.itheima.mapper"/>
    //    </mappers>
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        //扫描映射的包,数据交互层的和数据库交互的包,具体看下面的注释举例
        msc.setBasePackage("com.itheima.dao");
        return msc;
        //public interface AccountDao {
    	//@Insert("insert into tbl_account(name,money)values(#{name},#{money})")
    	//void save(Account account);
    }
}

image-20231214103630782

image-20231214103708489

SpringConfig主文件

//声明为配置bean文件
@Configuration
//扫描包
@ComponentScan("com.itheima")
//@PropertySource:加载类路径jdbc.properties文件
@PropertySource("classpath:jdbc.properties")
//导入第三方bean
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}

整合JUnit

test目录下的单元测试

//设置类运行器
@RunWith(SpringJUnit4ClassRunner.class)
//设置Spring环境对应的配置类
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
    //支持自动装配注入bean
    @Autowired
    private AccountService accountService;

AOP(面向切面编程)

Aspect Oriented Programming 面向切面编程,一种编程范式

OOP(Object Oriented programming)面向对象编程

核心概念

  • 在不惊动原始设计的基础上为其进行功能增强

  • spring理念:无入侵式/无侵入式

  • 连接点(JoinPoint):程序执行过程中的任意位置,粒度为执行方法,抛出异常,设置变量等

    • 在SpringAOP中,理解为方法的执行
  • 切入点(Pointcut)匹配连接点的式子

    • 在SpringAOP中,一个切入点可以只描述一个具体的方法,也可以匹配多个方法
      • 一个具体方法:BookDao接口中的无形参,无返回值的save方法
      • 匹配多个方法:所有的save方法,所有get开头的方法,所有以Dao结尾的方法,所有带一个参数的方法
  • 通知(Advice):在切入点处执行的操作,也就是共性功能

    • 功能最终以方法的形式表现
  • 通知类:定义通知的类

  • 切面(Aspect):描述通知与切入点的对应关系

    image-20231214142702374

AOP基础操作

在SpringConfig文件中配置使用AOP的注解

@Configuration
@ComponentScan("com.itheima")
//开启注解开发AOP功能
@EnableAspectJAutoProxy
public class SpringConfig {
}

写通知文件,用切面联系切入点

切入点定义依托一个不具有实际意义的方法进行,即无参数,无返回值,方法体无实际逻辑

//通知类必须配置成Spring管理的bean
@Component
//设置当前类为切面类类
@Aspect
public class MyAdvice {
    //设置切入点,要求配置在方法上方
    // execution 执行
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}

    
    //设置在切入点pt()的前面运行当前操作(前置通知)
    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

AOP工作流程

  • spring容器启动

  • 读取所有切面配置的切入点

  • 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点

    • 匹配失败,创建对象
    • 匹配成功,创建原始对象(目标对象)的代理对象
  • 获取bean执行方法

  • 获取bean,调用方法并执行

  • bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作

  • 核心概念

  • 目标对象(Target )︰原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的

  • 代理( Proxy ):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现

AOP切入点的表达式方式

标准格式

image-20231214154541417

方式一:执行com.itheima.dao包下的BookDao.update()

  • @Pointcut(“execution(void com.itheima.dao.BookDao.update())”)
    private void pt(){}

方式二:执行com.itheima.dao.impl包下的BookDaoImpl类中的无参数update方法

  • @Pointcut(“execution(void com.itheima.dao.impl.BookDaoImpl.update())”)
    private void pt(){}

通配符

  • *:单个独立的任意符号,可独立,可匹配前后缀

    • execution(public * com.itheima.*.UserService.Find*(*))
  • ..:简化包名和参数的

    • execution(public User com…UserService.FindByid(…))
  • +:专用于匹配子类类型

    • 匹配业务层的所有方法
    • execution(* * …*Service+.*(…))
书写技巧

image-20231214155942502

AOP通知类型(5种)

抽取的共性功能的位置不同,运行代码加入的位置也不同

5种类型:

    @Pointcut("execution(void com.itheima.dao.BookDao.update())")//无返回值
    private void pt(){}
    @Pointcut("execution(int com.itheima.dao.BookDao.select())")//返回值是int类型
    private void pt2(){}
  • 前置通知
    //@Before:前置通知,在原始方法运行之前执行
    @Before("pt()")
    public void before() {
        System.out.println("before advice ...");
    }
  • 后置通知
    //@After:后置通知,在原始方法运行之后执行
    @After("pt()")
    public void after() {
        System.out.println("after advice ...");
    }
  • 环绕通知(重点)
    //@Around:环绕通知,在原始方法运行的前后执行
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before advice ...");
        //表示对原始操作的调用
        Object ret = pjp.proceed();
        System.out.println("around after advice ...");
        return ret;
    }

    @Around("pt2()")
//方法声明加上Object
    public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before advice ...");
        //表示对原始操作的调用
        Integer ret = (Integer) pjp.proceed();
        System.out.println("around after advice ...");
        return ret;//返回值
    }
  • 返回后通知(了解)
    //@AfterReturning:返回后通知,在原始方法执行完毕后运行,且原始方法执行过程中未出现异常现象
//    @AfterReturning("pt2()")
    public void afterReturning() {
        System.out.println("afterReturning advice ...");
    }
  • 抛出异常通知(了解)
    //@AfterThrowing:抛出异常后通知,在原始方法执行过程中出现异常后运行
    @AfterThrowing("pt2()")
    public void afterThrowing() {
        System.out.println("afterThrowing advice ...");
    }
}
@Around注意事项

image-20231214161735170

AOP通知获取数据

获取切入点方法的参数

  • JoinPoint:适用于前置,后置,返回后,抛出异常后通知
  • proceedJoinPoint:适用于环绕通知

image-20231214165555681

image-20231214165607995

获取切入点方法运行异常信息

  • 抛出异常后通知
  • 环绕通知

image-20231214165634233

AOP总结

核心概念

  • 代理(Proxy ) : SpringAOP的核心本质是采用代理模式实现的
  • 连接点( JoinPoint ) :在SpringAOP中,理解为任意方法的执行
  • 切入点( Pointcut ):匹配连接点的式子,也是具有共性功能的方法描述
  • 通知( Advice ):若干个方法的共性功能,在切入点处执行,最终体现为一个方法
  • 切面( Aspect )︰描述通知切入点的对应关系
  • 目标对象(Target )︰被代理的原始对象成为目标对象

image-20231214174956033

Spring事务

  • 事务作用:在数据层保证一系列的数据库操作同时成功和失败
  • Spring事务作用:在数据层或业务层保证一系列的数据库操作同成功同失败

开启注解式事务

@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
//开启注解式事务驱动
@EnableTransactionManagement
public class SpringConfig {
}

配置事务位置,一般写在接口,实现类也行

public interface AccountService {
    /**
     * 转账操作
     * @param out 传出方
     * @param in 转入方
     * @param money 金额
     */
    //配置当前接口方法具有事务
    @Transactional
    public void transfer(String out,String in ,Double money) ;
}

事务相关配置

都可以在注解里添加属性

image-20231215093533097

业务追加日志

无论操作是否成功,均进行转账操作的日志留痕

要让该日志不回滚,要开启新事务

public interface LogService {
    //propagation设置事务属性:传播行为设置为当前操作需要新事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    void log(String out, String in, Double money);
}
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Autowired
    private LogService logService;

    public void transfer(String out,String in ,Double money) {
        try{
            accountDao.outMoney(out,money);
           // int i = 1/0;
            accountDao.inMoney(in,money);
        }finally {
            logService.log(out,in,money);
        }
    }

}

SpringMVC

基于spring的javaweb技术

  • springMVC技术和Servlet技术功能等同,均属于web层开发技术
  • 使用简单,开发便捷(相较于servlet)

image-20231215151739545

基本使用

  • 使用流程

image-20231215163418233

  • 导入SpringMVC坐标和servlet坐标

        <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
          <version>3.1.0</version>
          <scope>provided</scope>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>5.2.10.RELEASE</version>
        </dependency>
    
  • 创建SpringMVC控制器类(等同于Servlet功能)

//定义表现层控制器bean
@Controller
public class UserController {

    //设置映射路径为/save,即外部访问路径
    @RequestMapping("/save")
    //设置当前操作返回结果为指定json数据(本质上是一个字符串信息)
    @ResponseBody
    public String save(){
        System.out.println("user save ...");
        return "{'info':'springmvc'}";
    }
}
  • 初始化SpringMVC环境(同Spring环境),设定加载对应的bean
//springmvc配置类,本质上还是一个spring配置类
@Configuration
@ComponentScan("com.itheima.controller")
public class SpringMvcConfig {
}
  • 初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求
//web容器配置类
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
    //加载springmvc配置类,产生springmvc容器(本质还是spring容器)
    protected WebApplicationContext createServletApplicationContext() {
        //初始化WebApplicationContext对象
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        //加载指定配置类
        ctx.register(SpringMvcConfig.class);
        return ctx;
    }

    //设置由springmvc控制器处理的请求映射路径
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    //加载spring配置类
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }
}

Bean的加载控制

  • SpringMVC相关bean(表现层bean)

  • Spring控制的bean

    • 业务bean(Service)
    • 功能bean(DataSource等)
  • 排除SpringMVC扫描的包,避免重复

    • SpringMVC相关bean加载控制

      • springMVC加载的bean对应的包均在com.itheima.controller包内
    • Spring相关bean加载控制

      • 方式一:Spring加载的bean设定扫描范围为com.itheima,但要排除掉controller包内的bean
      @Configuration
      @ComponentScan({"com.itheima.service","com.itheima.dao"})
      public class SpringConfig {
      }
      ----------------------
      @ComponentScan("com.itheima.controller")
      public class SpringMvcConfig {
      }
      
      
      • 方式二:Spring加载的bean设定扫描范围为精准范围,例如service包,dao包等
      @Configuration
      //@ComponentScan({"com.itheima.service","com.itheima.dao"})
      //设置spring配置类加载bean时的过滤规则,当前要求排除掉表现层对应的bean
      //excludeFilters属性:设置扫描加载bean时,排除的过滤规则
      //type属性:设置排除规则,当前使用按照bean定义时的注解类型进行排除
      //classes属性:设置排除的具体注解类,当前设置排除@Controller定义的bean
      @ComponentScan(value="com.itheima",
          excludeFilters = @ComponentScan.Filter(
                  //筛选类型为ANNOTATION(注解)的
              type = FilterType.ANNOTATION,
              //选中Controller注解
              classes = Controller.class
          )
      )
      public class SpringConfig {
      }
      ------------------
      @Configuration
      //@ComponentScan("com.itheima.controller")
      public class SpringMvcConfig {
      }
      

PostMan

模拟HTTP协议快速发送请求

请求与响应

请求映射路径

  • @RequestMapping
  • 在方法上注解 再在类上注解
  • 设置当前控制器方法请求访问路径
@Controller
//类上方配置的请求映射与方法上面配置的请求映射连接在一起,形成完整的请求映射路径
@RequestMapping("/user")
public class UserController {
    //请求路径映射
    @RequestMapping("/save")
    @ResponseBody
    public String save(){
        System.out.println("user save ...");
        return "{'module':'user save'}";
    }
    //请求路径映射
    @RequestMapping("/delete")
    @ResponseBody
    public String delete(){
        System.out.println("user delete ...");
        return "{'module':'user delete'}";
    }

}

Get请求和Post请求发送普通参数

包含处理中文乱码问题
处理json数据格式
  • @RequestBody和@RequestParam
  • 区别
    • @RequestParam用于接受url地址传参,表单传参【application/x-www-form-urlencoded】
    • @RequestBod用于接收json数据【application/json】
  • 应用
    • 后期开发发送json格式数据为主
    • 发送非json格式数据用@RequestParam

导入坐标

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.0</version>
    </dependency>

扫描控制器类以及开启json自动转换

@Configuration
@ComponentScan("com.itheima.controller")
//开启json数据类型自动转换
@EnableWebMvc
public class SpringMvcConfig {
}

ServletContainersInitConfig配置以及开启对前台请求的乱码处理

public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    protected Class<?>[] getRootConfigClasses() {
        return new Class[0];
    }

    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMvcConfig.class};
    }

    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    //乱码处理,过滤器
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("UTF-8");
        return new Filter[]{filter};
    }
}	

控制器类

//请求参数
@Controller
public class UserController {

    //普通参数:请求参数与形参名称对应即可完成参数传递
    @RequestMapping("/commonParam")
    @ResponseBody
    public String commonParam(String name ,int age){
        System.out.println("普通参数传递 name ==> "+name);
        System.out.println("普通参数传递 age ==> "+age);
        return "{'module':'common param'}";
    }

    //普通参数:请求参数名与形参名不同时,使用@RequestParam注解关联请求参数名称与形参名称之间的关系
    //使用name注入形参userName
    @RequestMapping("/commonParamDifferentName")
    @ResponseBody
    public String commonParamDifferentName(@RequestParam("name") String userName , int age){
        System.out.println("普通参数传递 userName ==> "+userName);
        System.out.println("普通参数传递 age ==> "+age);
        return "{'module':'common param different name'}";
    }

    //POJO参数:请求参数与形参对象中的属性对应即可完成参数传递
    @RequestMapping("/pojoParam")
    @ResponseBody
    public String pojoParam(User user){
        System.out.println("pojo参数传递 user ==> "+user);
        return "{'module':'pojo param'}";
    }

    //嵌套POJO参数:嵌套属性按照层次结构设定名称即可完成参数传递
    @RequestMapping("/pojoContainPojoParam")
    @ResponseBody
    public String pojoContainPojoParam(User user){
        System.out.println("pojo嵌套pojo参数传递 user ==> "+user);
        return "{'module':'pojo contain pojo param'}";
    }

    //数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中
    @RequestMapping("/arrayParam")
    @ResponseBody
    public String arrayParam(String[] likes){
        System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes));
        return "{'module':'array param'}";
    }

    //集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
    @RequestMapping("/listParam")
    @ResponseBody
    public String listParam(@RequestParam List<String> likes){
        System.out.println("集合参数传递 likes ==> "+ likes);
        return "{'module':'list param'}";
    } 
-----------------------------------------------------------

image-20231218160217556image-20231218160235443

重点json

    //集合参数:json格式
    //1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
    //2.使用@RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据
    @RequestMapping("/listParamForJson")
    @ResponseBody
    //先在配置文件里开启json自动转换,再使用@RequestBody注解就可以存入json到list了
    public String listParamForJson(@RequestBody List<String> likes){
        System.out.println("list common(json)参数传递 list ==> "+likes);
        return "{'module':'list common for json param'}";
    }

    //POJO参数:json格式
    //1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
    //2.使用@RequestBody注解将外部传递的json数据映射到形参的实体类对象中,要求属性名称一一对应
    @RequestMapping("/pojoParamForJson")
    @ResponseBody
    public String pojoParamForJson(@RequestBody User user){
        System.out.println("pojo(json)参数传递 user ==> "+user);
        return "{'module':'pojo for json param'}";
    }

    //集合参数:json格式
    //1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
    //2.使用@RequestBody注解将外部传递的json数组数据映射到形参的保存实体类对象的集合对象中,要求属性名称一一对应
    @RequestMapping("/listPojoParamForJson")
    @ResponseBody
    public String listPojoParamForJson(@RequestBody List<User> list){
        System.out.println("list pojo(json)参数传递 list ==> "+list);
        return "{'module':'list pojo for json param'}";
    }

    //日期参数
    //使用@DateTimeFormat注解设置日期类型数据格式,默认格式yyyy/MM/dd
    @RequestMapping("/dataParam")
    @ResponseBody
    public String dataParam(Date date,
                            @DateTimeFormat(pattern="yyyy-MM-dd") Date date1,
                            @DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2){
        System.out.println("参数传递 date ==> "+date);
        System.out.println("参数传递 date1(yyyy-MM-dd) ==> "+date1);
        System.out.println("参数传递 date2(yyyy/MM/dd HH:mm:ss) ==> "+date2);
        return "{'module':'data param'}";
    }

}

image-20231218160259274

响应

控制器类(其他类和请求的一样)

image-20231218172216812

@Controller
public class UserController {

    //响应页面/跳转页面
    //返回值为String类型,设置返回值为页面名称,即可实现页面跳转
    @RequestMapping("/toJumpPage")
    public String toJumpPage(){
        System.out.println("跳转页面");
        return "page.jsp";
    }

    //响应文本数据
    //返回值为String类型,设置返回值 为任意字符串信息,即可实现返回指定字符串信息,需要依赖@ResponseBody注解
    @RequestMapping("/toText")
    @ResponseBody
    public String toText(){
        System.out.println("返回纯文本数据");
        return "response text";
    }

    //响应POJO对象
    //返回值为实体类对象,设置返回值为实体类类型,即可实现返回对应对象的json数据,需要依赖@ResponseBody注解和@EnableWebMvc注解
    @RequestMapping("/toJsonPOJO")
    @ResponseBody
    public User toJsonPOJO(){
        System.out.println("返回json对象数据");
        User user = new User();
        user.setName("itcast");
        user.setAge(15);
        return user;
    }

    //响应POJO集合对象
    //返回值为集合对象,设置返回值为集合类型,即可实现返回对应集合的json数组数据,需要依赖@ResponseBody注解和@EnableWebMvc注解
    @RequestMapping("/toJsonList")
    @ResponseBody
    public List<User> toJsonList(){
        System.out.println("返回json集合数据");
        User user1 = new User();
        user1.setName("传智播客");
        user1.setAge(15);

        User user2 = new User();
        user2.setName("黑马程序员");
        user2.setAge(12);

        List<User> userList = new ArrayList<User>();
        userList.add(user1);
        userList.add(user2);

        return userList;
    }
}

REST风格

  • REST(Representational State Transfer),表现形式状态转换
  • 传统风格资源描述形式
    • http://localhost/user/getById?id=1
    • http://localhost/user/saveUser
  • REST风格描述形式
    • http://localhost/user/1
    • http://localhost/user

优点

  • 隐藏资源的访问行为,无法通过地址得知对资源是何种操作
  • 书写简化

image-20231218173201559

@RequestBody @Requestparam @PathVariable

根据REST风格对资源进行访问称为RESTful

image-20231218182651335

一样的前置

  • 先在SpringMvcConfig配置文件扫描controller的文件夹
  • 再开启JSON自动转换
  • 然后到ServletContainersInitConfig配置中获取Servlet配置文件(SpringMvcConfig)路径
  • 记得开启乱码处理中文
  • 下面就是Controller主要测试代码
@Controller
public class UserController {

    //设置当前请求方法为POST,表示REST风格中的添加操作
    @RequestMapping(value = "/users",method = RequestMethod.POST)
    @ResponseBody
    public String save(){
        System.out.println("user save...");
        return "{'module':'user save'}";
    }

    //设置当前请求方法为DELETE,表示REST风格中的删除操作
    //@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同
    @RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE)
    @ResponseBody
    public String delete(@PathVariable Integer id){
        System.out.println("user delete..." + id);
        return "{'module':'user delete'}";
    }

    //设置当前请求方法为PUT,表示REST风格中的修改操作
    @RequestMapping(value = "/users",method = RequestMethod.PUT)
    @ResponseBody
    public String update(@RequestBody User user){
        System.out.println("user update..."+user);
        return "{'module':'user update'}";
    }

    //设置当前请求方法为GET,表示REST风格中的查询操作
    //@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同
    @RequestMapping(value = "/users/{id}" ,method = RequestMethod.GET)
    @ResponseBody
    public String getById(@PathVariable Integer id){
        System.out.println("user getById..."+id);
        return "{'module':'user getById'}";
    }

    //设置当前请求方法为GET,表示REST风格中的查询操作
    @RequestMapping(value = "/users",method = RequestMethod.GET)
    @ResponseBody
    public String getAll(){
        System.out.println("user getAll...");
        return "{'module':'user getAll'}";
    }

}

REST快速开发

优化共同拥有的注解,不同方法使用相对应的注解

image-20231218185327292


image-20231218185357893

//@Controller
//@ResponseBody配置在类上可以简化配置,表示设置当前每个方法的返回值都作为响应体
//@ResponseBody
@RestController     //使用@RestController注解替换@Controller与@ResponseBody注解,简化书写
@RequestMapping("/books")
public class BookController {

//    @RequestMapping( method = RequestMethod.POST)
    @PostMapping        //使用@PostMapping简化Post请求方法对应的映射配置
    public String save(@RequestBody Book book){
        System.out.println("book save..." + book);
        return "{'module':'book save'}";
    }

//    @RequestMapping(value = "/{id}" ,method = RequestMethod.DELETE)
    @DeleteMapping("/{id}")     //使用@DeleteMapping简化DELETE请求方法对应的映射配置
    public String delete(@PathVariable Integer id){
        System.out.println("book delete..." + id);
        return "{'module':'book delete'}";
    }

//    @RequestMapping(method = RequestMethod.PUT)
    @PutMapping         //使用@PutMapping简化Put请求方法对应的映射配置
    public String update(@RequestBody Book book){
        System.out.println("book update..."+book);
        return "{'module':'book update'}";
    }

//    @RequestMapping(value = "/{id}" ,method = RequestMethod.GET)
    @GetMapping("/{id}")    //使用@GetMapping简化GET请求方法对应的映射配置
    public String getById(@PathVariable Integer id){
        System.out.println("book getById..."+id);
        return "{'module':'book getById'}";
    }

//    @RequestMapping(method = RequestMethod.GET)
    @GetMapping             //使用@GetMapping简化GET请求方法对应的映射配置
    public String getAll(){
        System.out.println("book getAll...");
        return "{'module':'book getAll'}";
    }
}

基于RESTful页面数据交互

  • 制作SpringMVC控制器,并通过PostMan测试接口功能
@RestController
@RequestMapping("/books")
public class BookController {

    //新增
    @PostMapping
    public String save(@RequestBody Book book){
        System.out.println("book save ==> "+ book);
        return "{'module':'book save success'}";
    }
    //查询
    @GetMapping
    public List<Book> getAll(){
        System.out.println("book getAll is running ...");
        List<Book> bookList = new ArrayList<Book>();

        Book book1 = new Book();
        book1.setType("计算机");
        book1.setName("SpringMVC入门教程");
        book1.setDescription("小试牛刀");
        bookList.add(book1);
		//模拟数据...

        return bookList;
    }

}
  • 设置对静态资源的访问放行
  • 记得要把该配置放入扫描
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    //设置静态资源访问过滤,当前类需要设置为配置类,并被扫描加载
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        //当访问/pages/????时候,从/pages目录下查找内容
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/");
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
        registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
    }
}
  • 前端页面通过异步提交访问后台控制器
//添加
saveBook () {
    axios.post("/books",this.formData).then((res)=>{

    });
},

//主页列表查询
getAll() {
    axios.get("/books").then((res)=>{
        this.dataList = res.data;
    });
},

SSM整合(Spring+SpringMVC+MyBatis)

image-20231219103536061

image-20231219153016349

整合的配置(项目)

  • 首先创建目录结构
0.pom.xml
    <!--servlet和springMVC的整合-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    <!--包括aop,beans,context.core,expression,web-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

    <!--mybatis和spring的整合-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.0</version>
    </dependency>

    <!--test单元测试和spring的整合-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.16</version>
    </dependency>

    <!--用来做json转换-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.0</version>

      ------  
        <plugin>
      <groupId>org.apache.tomcat.maven</groupId>
      <artifactId>tomcat7-maven-plugin</artifactId>
      <version>2.1</version>
      <configuration>
        <port>80</port>
        <path>/</path>
      </configuration>
    </plugin>
1.SpringConfig 13.开启事务
//设置为配置文件,核心配置类
@Configuration
//设置扫描包
@ComponentScan({"com.itheima.service"})
//载入properties文件
@PropertySource("classpath:jdbc.properties")
//加载jdbcConfig和MyBatisConfig
@Import({JdbcConfig.class, MyBatisConfig.class})
//13.开启事务
@EnableTransactionManagement
public class SpringConfig {
}

2.jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db1
jdbc.username=root
jdbc.password=root
3.JdbcConfig 14.事务处理
public class JdbcConfig {

    //从jdbc.properties中得到
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    //声明为bean容器
    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    //14.事务处理bean,管理器
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        DataSourceTransactionManager ds = new DataSourceTransactionManager();
        ds.setDataSource(dataSource);
        return ds;
    }

}

4.MybatisConfig
public class MyBatisConfig {
    //定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象
    //该bean存入容器里
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        //在spring容器中自动装配的
        factoryBean.setDataSource(dataSource);
        //扫描数据需要存放的类型的包
        factoryBean.setTypeAliasesPackage("com.itheimna.domain");
        return factoryBean;
    }

    //定义bean,返回MapperScannerConfigurer对象
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        //扫描映射的包,数据交互层的和数据库交互的包,具体看下面的注释举例
        msc.setBasePackage("com.itheima.dao");
        return msc;


        //public interface AccountDao {
        //@Insert("insert into tbl_account(name,money)values(#{name},#{money})")
        //void save(Account account);
    }
}

5.SpringMvcConfig
//核心配置类
@Configuration
//设置扫描servlet路径
@ComponentScan("com.itheima.controller")
//功能很多,主要用来处理JSON自动转换
@EnableWebMvc
public class SpringMvcConfig {
}
6.ServletConfig
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    //根配置
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }

    //Mvc配置,Spring不能访问MVC,但MVC能访问spring的容器
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMvcConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    //根据是否需要处理乱码问题来重写方法
    //一般传json数据是不需要用到该方法
    //json自动转换会处理中文
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("utf-8");
        return new Filter[]{filter};
    }
}

7.Book
public class Book {
    private Integer id;
    private String type;
    private String name;
    private String description;
	//getter和setter方法
}
8.BookDao
public interface BookDao {
    //    @Insert("insert into tbl_book values(null,#{type},#{name},#{description})")
    @Insert("insert into tbl_book (type,name,description) values(#{type},#{name},#{description})")
    public void save(Book book);

    @Update("update tbl_book set type = #{type}, name = #{name}, description = #{description} where id = #{id}")
    public void update(Book book);

    @Delete("delete from tbl_book where id = #{id}")
    public void delete(Integer id);

    @Select("select * from tbl_book where id = #{id}")
    public Book getById(Integer id);

    @Select("select * from tbl_book")
    public List<Book> getAll();
}

9.BookService 15.加事务
//15.加事务
@Transactional
public interface BookService {
    /**
     * 保存
     * @param book
     * @return
     */
    public boolean save(Book book);

    /**
     * 修改
     * @param book
     * @return
     */
    public boolean update(Book book);

    /**
     * 删除
     * @param id
     * @return
     */
    public boolean delete(Integer id);

    /**
     * 查询单个
     * @param id
     * @return
     */
    public Book getById(Integer id);

    /**
     * 查询全部
     * @return
     */
    public List<Book> getAll();
}

10.BookServiceImpl
  • 实现自动装配时@Autowired报错则需要修改报错检查
  • 报错是因为dao包里可能没有对应的BookDao
@Service
public class BookServiceImpl implements BookService {

    //自动装配到该变量
    @Autowired
    private BookDao bookDao;
    @Override
    public boolean save(Book book) {
        bookDao.save(book);
        return true;
    }

    @Override
    public boolean update(Book book) {
        bookDao.update(book);
        return true;
    }

    @Override
    public boolean delete(Integer id) {
        bookDao.delete(id);
        return true;
    }

    @Override
    public Book getById(Integer id) {
        return bookDao.getById(id);
    }

    @Override
    public List<Book> getAll() {
        return bookDao.getAll();
    }
}
11.BookController
//使用@RestController注解替换@Controller与@ResponseBody注解,简化书写
@RestController
@RequestMapping("/books")
public class BookController {
    //自动装配
    @Autowired
    private BookService bookService;

    //根据业务操作不同,实现不同的Mapping
    @PostMapping
    //根据形参类型选中形参注入,从json中取出
    public boolean save(@RequestBody Book book) {
        return bookService.save(book);
    }
    @PutMapping
    public boolean update(@RequestBody Book book) {
        return bookService.update(book);

    }
    @DeleteMapping("/{id}")
    public boolean delete(@PathVariable Integer id) {
        return bookService.delete(id);

    }

    @GetMapping("/{id}")
    public Book getById(@PathVariable Integer id) {
        return bookService.getById(id);

    }
    @GetMapping
    public List<Book> getAll() {
        return bookService.getAll();

    }
}

12.开发后台完成之后要测试

两个环节

  • 业务层接口开发完进行junit单元测试

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringConfig.class)
    public class BookServiceTest {
    
        @Autowired
        private BookService bookService;
    
        @Test
        public void testGetById() {
            Book book = bookService.getById(1);
            System.out.println(book);
        }
    
        @Test
        public void testGetAll() {
            List<Book> all = bookService.getAll();
            System.out.println(all);
        }
    
    }
    
    
  • 表现层接口用postman测试
    image-20231220095750035

image-20231220111740848

表现层和前端数据

  • 表现层数据封装
  • 设置统一数据返回结果类
Result
public class Result {
    //数据
    private Object data;
    //操作的码
    private Integer code;
    //操作失败的提示信息
    private String msg;
}
public Result() {
    }

    public Result( Integer code,Object data) {
        this.data = data;
        this.code = code;
    }

    public Result(Integer code, Object data, String msg) {
        this.data = data;
        this.code = code;
        this.msg = msg;
    }
//get和set方法
Code
public class Code {
    public static final Integer SAVE_OK = 20011;
    public static final Integer DELETE_OK = 20021;
    public static final Integer UPDATE_OK = 20031;
    public static final Integer GET_OK = 20041;

    public static final Integer SAVE_ERR = 20010;
    public static final Integer DELETE_ERR = 20020;
    public static final Integer UPDATE_ERR = 20030;
    public static final Integer GET_ERR = 20040;
}

BookController更新
//使用@RestController注解替换@Controller与@ResponseBody注解,简化书写
@RestController
@RequestMapping("/books")
public class BookController {
    //自动装配
    @Autowired
    private BookService bookService;

    //根据业务操作不同,实现不同的Mapping
    @PostMapping
    //根据形参类型选中形参注入,从json中取出
    public Result save(@RequestBody Book book) {
        boolean flag = bookService.save(book);
        return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERR, flag);
    }
    @PutMapping
    public Result update(@RequestBody Book book) {
        boolean flag = bookService.update(book);
        return new Result(flag ? Code.UPDATE_OK : Code.UPDATE_ERR, flag);
    }
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable Integer id) {
        boolean flag = bookService.delete(id);
        return new Result(flag ? Code.DELETE_OK : Code.DELETE_ERR, flag);
    }

    @GetMapping("/{id}")
    public Result getById(@PathVariable Integer id) {
        Book date = bookService.getById(id);
        Integer code = date != null ? Code.GET_OK : Code.GET_ERR;
        String msg = date != null ? "" : "数据查询失败,请重试!";
        return new Result(code, date, msg);
    }
    @GetMapping
    public Result getAll() {
        List<Book> all = bookService.getAll();
        Integer code = all != null ? Code.GET_OK : Code.GET_ERR;
        String msg = all != null ? "" : "数据查询失败,请重试!";
        return new Result(code, all, msg);
    }
}

异常处理器

出现异常现象的常见位置与诱因如下:

  • 框架内部抛出的异常:因使用不合规导致
  • 数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)
  • 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
  • 表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)
  • 工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)

各个层级均出现异常,所以异常处理代码书写在表现层可以全部进行处理

ProjectExceptionAdvice

该异常接受类写在controller里,要被MVC扫描到才行

对所有异常进行捕获测试

//定义异常处理通知
@RestControllerAdvice
public class ProjectExceptionAdvice {
    //处理哪种异常
    @ExceptionHandler(Exception.class)
    public Result doException(Exception e) {
        System.out.println("嘿嘿,异常,我的异常!");
        return new Result(666,null, " 异常了");
    }
}

项目异常分类

image-20231220144803670

项目异常处理方案

image-20231220145054018

创建自定义异常包exception存储自定义异常类

在Controller类try…catch…异常并给ProjectExceptionAdvice处理

@RestControllerAdvice
public class ProjectExceptionAdvice {

    //系统问题异常
    @ExceptionHandler(SystemException.class)
    public Result doSystemException(SystemException e) {
        //记录日志
        //发送消息给运维
        //发送邮件给开发人员,e对象发送给开发人员

        return new Result(e.getCode(), null, e.getMessage());
    }
    //恶意调用异常
    @ExceptionHandler(BusinessException.class)
    public Result doBusinessException(BusinessException e) {
        return new Result(e.getCode(), null, e.getMessage());
    }


	//未知异常
    @ExceptionHandler(Exception.class)
    public Result doException(Exception e) {
        System.out.println("嘿嘿,异常,我的异常!");
        return new Result(Code.SYSTEM_UNKNOWN_ERR,null, "系统繁忙,请稍后再试!");
    }
}

前后台协议联调的vue

image-20231221102616638

<!DOCTYPE html>

<html>

<head>

    <!-- 页面meta -->

    <meta charset="utf-8">

    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <title>SpringMVC案例</title>

    <meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport">

    <!-- 引入样式 -->

    <link rel="stylesheet" href="../plugins/elementui/index.css">

    <link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css">

    <link rel="stylesheet" href="../css/style.css">

</head>

<body class="hold-transition">

<div id="app">

    <div class="content-header">

        <h1>图书管理</h1>

    </div>

    <div class="app-container">

        <div class="box">

            <div class="filter-container">

                <el-input placeholder="图书名称" v-model="pagination.queryString" style="width: 200px;"
                          class="filter-item"></el-input>

                <el-button @click="getAll()" class="dalfBut">查询</el-button>

                <el-button type="primary" class="butT" @click="handleCreate()">新建</el-button>

            </div>

            <el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row>

                <el-table-column type="index" align="center" label="序号"></el-table-column>

                <el-table-column prop="type" label="图书类别" align="center"></el-table-column>

                <el-table-column prop="name" label="图书名称" align="center"></el-table-column>

                <el-table-column prop="description" label="描述" align="center"></el-table-column>

                <el-table-column label="操作" align="center">

                    <template slot-scope="scope">

                        <el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>

                        <el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>

                    </template>

                </el-table-column>

            </el-table>

            <!-- 新增标签弹层 -->

            <div class="add-form">

                <el-dialog title="新增图书" :visible.sync="dialogFormVisible">

                    <el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right"
                             label-width="100px">

                        <el-row>

                            <el-col :span="12">

                                <el-form-item label="图书类别" prop="type">

                                    <el-input v-model="formData.type"/>

                                </el-form-item>

                            </el-col>

                            <el-col :span="12">

                                <el-form-item label="图书名称" prop="name">

                                    <el-input v-model="formData.name"/>

                                </el-form-item>

                            </el-col>

                        </el-row>


                        <el-row>

                            <el-col :span="24">

                                <el-form-item label="描述">

                                    <el-input v-model="formData.description" type="textarea"></el-input>

                                </el-form-item>

                            </el-col>

                        </el-row>

                    </el-form>

                    <div slot="footer" class="dialog-footer">

                        <el-button @click="dialogFormVisible = false">取消</el-button>

                        <el-button type="primary" @click="handleAdd()">确定</el-button>

                    </div>

                </el-dialog>

            </div>

            <!-- 编辑标签弹层 -->

            <div class="add-form">

                <el-dialog title="编辑检查项" :visible.sync="dialogFormVisible4Edit">

                    <el-form ref="dataEditForm" :model="formData" :rules="rules" label-position="right"
                             label-width="100px">

                        <el-row>

                            <el-col :span="12">

                                <el-form-item label="图书类别" prop="type">

                                    <el-input v-model="formData.type"/>

                                </el-form-item>

                            </el-col>

                            <el-col :span="12">

                                <el-form-item label="图书名称" prop="name">

                                    <el-input v-model="formData.name"/>

                                </el-form-item>

                            </el-col>

                        </el-row>

                        <el-row>

                            <el-col :span="24">

                                <el-form-item label="描述">

                                    <el-input v-model="formData.description" type="textarea"></el-input>

                                </el-form-item>

                            </el-col>

                        </el-row>

                    </el-form>

                    <div slot="footer" class="dialog-footer">

                        <el-button @click="dialogFormVisible4Edit = false">取消</el-button>

                        <el-button type="primary" @click="handleEdit()">确定</el-button>

                    </div>

                </el-dialog>

            </div>

        </div>

    </div>

</div>

</body>

<!-- 引入组件库 -->

<script src="../js/vue.js"></script>

<script src="../plugins/elementui/index.js"></script>

<script type="text/javascript" src="../js/jquery.min.js"></script>

<script src="../js/axios-0.18.0.js"></script>

<script>
    var vue = new Vue({

        el: '#app',
        data: {
            pagination: {},
            dataList: [],//当前页要展示的列表数据
            formData: {},//表单数据
            dialogFormVisible: false,//控制表单是否可见
            dialogFormVisible4Edit: false,//编辑表单是否可见
            rules: {//校验规则
                type: [{required: true, message: '图书类别为必填项', trigger: 'blur'}],
                name: [{required: true, message: '图书名称为必填项', trigger: 'blur'}]
            }
        },

        //钩子函数,VUE对象初始化完成后自动执行
        created() {
            this.getAll();
        },

        methods: {
            //列表
            getAll() {
                //发送ajax请求
                axios.get("/books").then((res => {
                    this.dataList = res.data.data;
                }))
            },

            //弹出添加窗口
            handleCreate() {
                this.dialogFormVisible = true;
                this.resetForm();
            },

            //重置表单
            resetForm() {
                this.formData = {}
            },

            //添加
            handleAdd() {
                //发送ajax请求
                axios.post("/books", this.formData).then((res => {
                    console.log(res)
                    //如果操作成功,关闭弹窗,再次查询数据
                    if (20011 == res.data.code) {
                        this.dialogFormVisible = false;
                        this.$message.success("添加成功");
                    } else if (20010 == res.data.code) {
                        this.$message.error("添加失败");
                    } else {
                        this.$message.error(res.data.msg);
                    }

                })).finally(() => {
                    //无论成功都要执行查询
                    this.getAll();
                })
            },

            //弹出编辑窗口
            handleUpdate(row) {
                console.log(row)
                //根据id查询数据
                axios.get("/books/" + row.id).then((res) => {
                    //查询到了才进入
                    if (20041 == res.data.code) {
                        this.formData = res.data.data;
                        this.dialogFormVisible4Edit = true;
                    } else {
                        this.$message.error(res.data.msg);
                    }
                })
                //展示弹窗,加载数据

            },

            //编辑
            handleEdit() {
                axios.put("/books", this.formData).then((res) => {
                    if (20031 == res.data.code) {
                        this.$message.success("修改成功");
                        this.dialogFormVisible4Edit = false;
                    } else if (20030 == res.data.code) {
                        this.$message.error("修改失败");
                    } else {
                        this.$message.error(res.data.msg);
                    }
                }).finally(() => {
                    this.getAll();
                })
            },

            // 删除
            handleDelete(row) {

                //1.弹出提示框
                this.$confirm("此操作会永久删除当前数据,是否继续?", "提示", {
                    type: 'info'
                }).then(() => {
                    axios.delete("/books/" + row.id).then((res) => {
                        //2.做删除业务
                        if (20021 == res.data.code) {
                            this.$message.success("删除成功");
                        } else if (20020 == res.data.code) {
                            this.$message.error("删除失败");
                        } else {
                            this.$message.error(res.data.msg);
                        }
                    })
                }).catch(() => {
                    //3.取消删除
                    this.$message.success("取消删除");
                }).finally(() => {
                    this.getAll();
                })
            }
        }
    })

</script>

</html>

拦截器

写在controller层/interceptor

image-20231221104203023

  • 拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行
  • 作用:
    • 在指定的方法调用前后执行预先设定的代码
    • 阻止原始方法的执行

拦截器和过滤器区别

  • 归属不同:Filter属于Servlet技术,Interceptor属于SpringMvc技术
  • 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强

执行流程

image-20231221111009208

SpringMvcSupport载入拦截器设置,记得把该配置文件放入springConfig里扫描

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    @Autowired
    private ProjectInterceptor projectInterceptor;

    //过滤器
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
    }

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        //配置拦截器
        //拦截器对象在容器里,需要自动装配容器注入
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
    }
}

SpringMvcSupport可以替换到MVC配置文件中

@Configuration
@ComponentScan({"com.itheima.controller"})
@EnableWebMvc
//实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {
    @Autowired
    private ProjectInterceptor projectInterceptor;
    @Autowired
    private ProjectInterceptor2 projectInterceptor2;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //配置多拦截器
        //执行顺序和拦截器载入顺序有关
        //先进1,后进2	先出2,后出1
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
        registry.addInterceptor(projectInterceptor2).addPathPatterns("/books","/books/*");
    }
}
多拦截器执行顺序

image-20231221144706963

需要载入的拦截器类

@Component
//定义拦截器类,实现HandlerInterceptor接口
//注意当前类必须受Spring容器控制
public class ProjectInterceptor implements HandlerInterceptor {
    @Override
    //原始方法调用前执行的内容
    //返回值类型可以拦截控制的执行,true放行,false终止'
    //如果为false,则只会执行preHandle,后面的不会执行了

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String contentType = request.getHeader("Content-Type");
        HandlerMethod hm = (HandlerMethod)handler;
        System.out.println("preHandle..."+contentType);
        return true;
    }

    @Override
    //原始方法调用后执行的内容
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...");
    }

    @Override
    //原始方法调用完成后(postHandle执行完)执行的内容
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...");
    }
}

SpringBoot

打包时最好清除clean之前的打包文件

注意:基于idea开发SpringBoot程序必须要联网才能加载到程序框架结构

  • SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程

  • 最简Spring Boot程序包含的基础文件

    • pom.xml
    • web依赖是创建时打勾载入的

    image-20231221160736340

    • Application类

    image-20231221160831087

SpringBoot可以使用官网创建一个项目,idea的创建也是基于官网创建的

  • Spring Boot的jar包快速启动
  • 注:先通过idea打包
    • 运行环境jdk版本要和jar包一样

执行启动指令

java -jar springboot.jar

image-20231221174255816

image-20231222085011023

Spring boot如何自动加载版本

为了不频繁切换坐标依赖和插件的版本,spring boot的父项的父项加载了全部版本的依赖和插件

  • 根据spring boot的版本来自动加载不同版本的依赖和插件
  • 整个spring boot项目就是基于该方法加载的
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <!--指定了一个父工程,父工程中的东西在该工程中可以继承过来使用-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.0</version>
    </parent>
    <groupId>com.itheima</groupId>
    <artifactId>springboot_01_quickstart</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <!--JDK 的版本-->
    <properties>
        <java.version>8</java.version>
    </properties>
    
    <dependencies>
        <!--该依赖就是我们在创建 SpringBoot 工程勾选的那个 Spring Web 产生的-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
		<!--这个是单元测试的依赖,我们现在没有进行单元测试,所以这个依赖现在可以没有-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!--这个插件是在打包时需要的,而这里暂时还没有用到-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Springboot起步依赖

image-20231222090531280

spring-boot-starter-parent

包含starter的全是起步依赖

配置文件格式(3种)比如修改端口号

  • resources目录下的application.properties文件里

  • image-20231222092544453

  • 自建的yml格式文件

  • image-20231222092747598

  • 自建的yaml文件

  • image-20231222093231418

三种配置文件如果都配置了不同的端口

  • 优先级properties文件权限大于yml大于yaml

image-20231222093514551

打包之后如何启动包

进入 jar 包所在位置,在 命令提示符 中输入如下命令

jar -jar springboot_01_quickstart-0.0.1-SNAPSHOT.jar

概述

起步依赖

我们使用 Spring Initializr 方式创建的 Maven 工程的的 pom.xml 配置文件中自动生成了很多包含 starter 的依赖

image-20210918220338109

探索父工程

父工程中包含所有version版本,根据需要会自动选择对应的版本

  • 所有 SpringBoot 项目要继承的项目,定义了若干个坐标版本号(依赖管理,而非依赖),以达到减少依赖冲突的目的

  • spring-boot-starter-parent(2.5.0)与 spring-boot-starter-parent(2.4.6)共计57处坐标版本不同

实际开发

  • 使用任意坐标时,仅书写GAV中的G和A,V由SpringBoot提供

    G:groupid

    A:artifactId

    V:version

  • 如发生坐标错误,再指定version(要小心版本冲突)

切换web服务器

现在我们启动工程使用的是 tomcat 服务器,那能不能不使用 tomcat 而使用 jetty 服务器,jetty 在我们 maven 高级时讲 maven 私服使用的服务器。而要切换 web 服务器就需要将默认的 tomcat 服务器给排除掉,怎么排除呢?使用 exclusion 标签

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <groupId>org.springframework.boot</groupId>
        </exclusion>
    </exclusions>
</dependency>

现在我们运行引导类会不行,因为没有服务器了,所以引入服务器

程序直接停止了,为什么呢?那是因为排除了 tomcat 服务器,程序中就没有服务器了。所以此时不光要排除 tomcat 服务器,还要引入 jetty 服务器。在 pom.xml 中因为 jetty 的起步依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

接下来再次运行引导类,在日志信息中就可以看到使用的是 jetty 服务器

配置文件修改

我们现在启动服务器默认的端口号是 8080,访问路径可以书写为

http://localhost:8080/books/1

在线上环境我们还是希望将端口号改为 80,这样在访问的时候就可以不写端口号了,如下

http://localhost/books/1

SpringBoot 程序如何修改呢?SpringBoot 提供了多种属性配置方式

  • application.properties

    server.port=80
    
  • application.yml

    server:
    	port: 81
    
  • application.yaml

    server:
    	port: 82
    

注意:SpringBoot 程序的配置文件名必须是 application ,只是后缀名不同而已。

三种配合文件的优先级

在三种配合文件中分别配置不同的端口号,启动服务查看绑定的端口号。用这种方式就可以看到哪个配置文件的优先级更高一些

application.properties 文件内容如下:

server.port=80

application.yml 文件内容如下:

server:
	port: 81

application.yaml 文件内容如下:

server:
	port: 82

启动服务,在控制台可以看到使用的端口号是 80。说明 application.properties 的优先级最高

注释掉 application.properties 配置文件内容。再次启动服务,在控制台可以看到使用的端口号是 81,说明 application.yml 配置文件为第二优先级。

从上述的验证结果可以确定三种配置文件的优先级是:

application.properties > application.yml > application.yaml

注意:

  • SpringBoot 核心配置文件名为 application

  • SpringBoot 内置属性过多,且所有属性集中在一起修改,在使用时,通过提示键+关键字修改属性

    例如要设置日志的级别时,可以在配置文件中书写 logging,就会提示出来。配置内容如下

    logging:
      level:
        root: info
    

yaml

YAML(YAML Ain’t Markup Language),一种数据序列化格式

优点

  • 容易阅读
  • 容易与脚本语言交互
  • 以数据为核心,重数据轻格式

YAML文件拓展名

  • .yml(主流)
  • .yaml

image-20231222095924141

语法规则

image-20231222100111768

每行多个值

yaml数组数据

  • 数组数据在数据书写位置下方使用减号做数据开始符号
  • 注意空格
  • image-20231222100333740

yaml数据读取方式(三种)

方式一和二

  • 一是用@Value一个一个读取
  • 二是用@Autowired自动装配到Environment类型的变量里
  • 三是先把yaml里的数据封装到对应的user对象里,再自动装配到对应的对象里使用

第一种和第二种

image-20231222102318652

使用@Value

image-20231222105302086

封装全部数据到Environment对象

image-20231222105545637

application.yaml

lesson: SpringBoot

server:
  port: 80

enterprise:
  name: itcast
  age: 16
  tel: 400000444
  subject:
    - java
    - 前端
    - web
    - 大数据

BookController.java

@RestController
@RequestMapping("/books")
public class BookController {

    @Value("${lesson}")
    private String lesson;
    @Value("${server.port}")
    private Integer port;
    @Value("${enterprise.subject[0]}")
    private String subject_00;

    //自动装配全部数据
    @Autowired
    private Environment environment;

    @GetMapping("/{id}")
    public String getById(@PathVariable Integer id) {
        System.out.println(lesson);
        System.out.println(port);
        System.out.println(subject_00);
        System.out.println("id ==>" + id);
        System.out.println("--------------------------");
        System.out.println(environment.getProperty("lesson"));
        System.out.println(environment.getProperty("server.port"));
        System.out.println(environment.getProperty("server"));
        System.out.println(environment.getProperty("enterprise.age"));
        System.out.println(environment.getProperty("enterprise.subject[1]"));
        return "hello ,spring boot!";
    }

}
自定义对象封装指定数据

image-20231222105712843

第三种

image-20231222104601682

多环境开发

有这样的场景,我们开发完毕后需要测试人员进行测试,由于测试环境和开发环境的很多配置都不相同,所以测试人员在运行我们的工程时需要临时修改很多配置,如下

java –jar springboot.jar –-spring.profiles.active=test --server.port=85 --server.servlet.context-path=/heima --server.tomcat.connection-timeout=-1 …… …… …… …… ……

针对这种情况,SpringBoot 定义了配置文件不同的放置的位置;而放在不同位置的优先级时不同的。

SpringBoot 中4级配置文件放置位置:

  • 1级:classpath:application.yml
  • 2级:classpath:config/application.yml
  • 3级:file :application.yml
  • 4级:file :config/application.yml

==说明:==级别越高优先级越高

  • 生产环境
  • 开发环境
  • 测试环境
  • image-20231222111913552
yaml环境

image-20231222111855816

#设置启用的环境
spring:
  profiles:
    active: dev


---
#开发
spring:
  config:
    activate:
      on-profile: dev

server:
  port: 80


---
#生产
spring:
  config:
    activate:
      on-profile: pro

server:
  port: 81


---
#测试
spring:
  config:
    activate:
      on-profile: test

server:
  port: 82


---

application.properties环境

image-20231222112008250

  • 需要创建多个properties文件,通过主的properties文件调用
  • 该图片调用的是application-pro.properties文件

image-20231222111655636

多环境命令行启动参数设置

image-20210917193910191

  • 执行打包前最好clean一下,以免前次的东西影响此次打包
  • 打包前还需要确认打包编码,要不然也会打包出错
  • image-20231222141839997

打包之后在包所在的文件目录打开cmd窗口

  • 先启动服务器java -jar demo1-0.0.1-SNAPSHOT.jar
  • 带参数启动Springboot
    • 再改变启用的环境jjava -jar demo1-0.0.1-SNAPSHOT.jar --spring.profiles.active=test

image-20231222160855757

命令行的优先级更高

maven中设置多环境属性

image-20231222161720905

SpringBoot中引用Maven属性

image-20231222161807733

执行Maven打包指令

失败

image-20231222162036562

对资源文件开启对默认占位符的解析

image-20231222162102538

配置文件分类

idea里的yml文件属于最低级(4级)

image-20231222162328216

和jar包放在同一个目录下的application.yml文件优先级比jar包里的大

image-20231222162604434

而config里的又更大

image-20231222162711001

整合第三方技术

整合JUnit

image-20231222163051173

整合MyBatis

2.4.2之前都有数据库连接url时区问题

设置时区解决

  • url: jdbc:mysql://localhost:3306/db1?serverTimezone=UTC

image-20231225094121376

案例

image-20231225094429452

总结

MyBatis-Plus

简称MP,是一个MyBatis的增强工具,在MyBatis基础上只做增强不做改变,为简化开发,提升效率而生

框架结构

image-20231228083945445

快速入门

导入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.1</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

连接数据库配置

配置application.yml
# DataSource Config
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
    username: root
    password: 1234

启动类

在Spring Boot启动类中添加@MapperScan注解,扫描mapper包

@MapperScan("com.itheima.mapper")
@SpringBootApplication
public class MybatisPlusStudyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusStudyApplication.class, args);
    }
}

添加实体类

@Data//lombok注解
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

添加mapper

BaseMapper是MyBatis-Plus提供的模板mapper,其中包含了基本的CRUD方法,泛型为操作的 实体类型

public interface UserMapper extends BaseMapper<User> {
}

测试

    @Autowired
    private UserMapper userMapper;

    @Test
    void test01(){
        List<User> users = userMapper.selectList(null);
        for (User user : users) {
            System.out.println(user);
        }
    }

思考

SQL语句:Mybatis-Plus写的

方法:也是MybatisPlus写的

添加日志,查看sql执行语句

我们所有的sql现在是不可见的,我们希望知道他是怎么执行的,所以我们必须要看日志!

  • 在application.yml中配置日志输出
# 配置日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations:

增删改查

/**
	 * step3:测试
	 *
	 * 测试删除功能,真正执行的是修改
	 *
	 * UPDATE t_user SET is_deleted=1 WHERE id=? AND is_deleted=0
	 *
	 * 测试查询功能,被逻辑删除的数据默认不会被查询
	 *
	 * SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0
	 */

	/**
	 * 查询所有
	 */
	@Test
	void test01(){
		List<User> users = userMapper.selectList(null);
		for (User user : users) {
			System.out.println(user);
		}
	}

	/**
	 * 插入
	 */
	@Test
	void insert(){
		//id值默认使用雪花算法生成,且主键默认是id,如果要让主键变为uid就要用@TableId("uid")来设置
		//在实体类user里设置
		User user = new User(null, "fx", 2, "aaa@qq.com");
		int insert = userMapper.insert(user);
		System.out.println("受影响行数"+insert);
		//1511332162436071425
		System.out.println(user.getId());
	}
	/**
	 *根据id查询
	 */
	@Test
	void testDeleteById(){
		//DELETE FROM user WHERE id=?
		int result = userMapper.deleteById(1);
		System.out.println("受影响行数:"+result);
	}
	/**
	 *批量删除
	 */
	@Test
	void testDeleteBatchIds(){
		//DELETE FROM user WHERE id IN ( ? , ? , ? )
		List ids = new ArrayList();
		ids.add(1);
		ids.add(2);
		int result = userMapper.deleteBatchIds(ids);
		System.out.println("受影响行数:"+result);
	}
	/**
	 *根据Map删除
	 */
	@Test
	void testDeleteByMap(){
		//DELETE FROM user WHERE name = ? AND age = ?
		Map<String,Object> map=new HashMap<>();
		map.put("age",12);
		map.put("name","lisi");
		int result = userMapper.deleteByMap(map);
		System.out.println("受影响行数:"+result);
	}
	/**
	 *更新
	 */
	@Test
	void testUpdateById(){
		//SELECT id,name,age,email FROM user WHERE id=?
		User user = new User(1739925106510225409L, "hello", 12, null);
		int result = userMapper.updateById(user);
		//注意:updateById参数是一个对象
		System.out.println("受影响行数:"+result);
	}
	/**
	 *批量插入
	 */
	@Test
	void testSaveBatch(){
		// SQL长度有限制,海量数据插入单条SQL无法实行,
		// 因此MP将批量插入放在了通用Service中实现,而不是通用Mapper
		ArrayList<User> users = new ArrayList<>();
		for (int i = 0; i < 5; i++) {
			User user = new User();
			user.setName("lyl"+i);
			user.setAge(20+i);
			users.add(user);
		}
		//SQL:INSERT INTO t_user ( username, age ) VALUES ( ?, ? )
		userService.saveBatch(users);
	}
	/**
	 *条件查询
	 */
	@Test
	void test1(){
		//查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息
		//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE
		// is_deleted=0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
		QueryWrapper<User>queryWrapper=new QueryWrapper<>();
		queryWrapper.like("name","a")
				.between("age",20,30)
				.isNotNull("email");
		List<User> userList = userMapper.selectList(queryWrapper);
		userList.forEach(System.out::println);
	}

	/**
	 * 测试分页
	 */
	@Test
	void testPage(){
		//设置分页参数
		Page<User>page=new Page<>(1,3);
		//page里面不设计的话默认是当前页1, 每页显示的记录条数是10
		userMapper.selectPage(page,null);
		//获取分页数据
		List<User> list = page.getRecords();
		list.forEach(System.out::println);
		System.out.println("当前页:"+page.getCurrent());
		System.out.println("每页显示的条数:"+page.getSize());
		System.out.println("总记录数:"+page.getTotal());
		System.out.println("总页数:"+page.getPages());
		System.out.println("是否有上一页:"+page.hasPrevious());
		System.out.println("是否有下一页:"+page.hasNext());
	}

自动填充

数据库插入时间字段和更新时间字段

  • 只需要设定默认值CURRENT_TIMESTAMP,就能产生时间

方法二

1.删除数据库默认值

2.实体类的字段属性上需要加注解

@TableField(fill = FieldFill.INSERT)
private Date createTime;

@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
12345

3、编写处理器处理注解

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
 @Override
 public void insertFill(MetaObject metaObject) {
     // 起始版本 3.3.0(推荐使用)
     this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
 }

 @Override
 public void updateFill(MetaObject metaObject) {
     // 起始版本 3.3.0(推荐)
     this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
 }
}

注:使用Mybatisplus查询的表数据不包括插入时间和更新时间

常用注解

@TableName

  • 放在实体类User上,同步实体类名和数据库表名

  • @Data//lombok注解
    @NoArgsConstructor//无参构造器
    @TableName("t_user")//实体类类型的类名同步名称到要操作的表的表名
    public class User {
        private Long id;
        @TableField("username")//同步数据库字段名
        private String name;
        private Integer age;
        private String email;
    
  • 也可以通过全局配置解决问题,最下面一行

  • # mybatis-plus配置
    mybatis-plus:
      configuration:
        # 配置日志
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      global-config:
        db-config:
          # 配置MyBatis-Plus操作表的默认前缀
          table-prefix: t_
    

@TableId

  • MyBatis-Plus默认将Id作为主键列,在插入数据时,默认基于雪花算法生成id
  • 如果实体类和表中主键不是id(比如uid)则需要自己指定主键
  • 在属性id上添加注解 @TableId(“uid”)或 @TableId(value=“uid”)就行

@TableField

  • 若实体类中的属性使用的是驼峰命名风格,而表中的字段使用的是下划线命名风格
  • 例如实体类属性userName,表中字段user_name 此时MyBatis-Plus会自动将下划线命名风格转化为驼峰命名风格

如果不满足上面条件

  • 则需要添加注释手动同步

  • public class User {
        private Long id;
        @TableField("username")//同步数据库字段名
        private String name;
        private Integer age;
        private String email;
    
    

@TableLogic

  • 数据库中创建逻辑删除状态列,设置默认值为0

  • 只是根据逻辑值查询,不会删除数据

  • 实体类中添加逻辑删除属性

  • public class User {
        private Long id;
        @TableField("username")//同步数据库字段名
        private String name;
        private Integer age;
        private String email;
    
        //装配时间到该变量
        @TableField(fill = FieldFill.INSERT)
        private Date createTime;
    
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Date updateTime;
    
        //逻辑删除添加(其实是给一个标志位,为1就不查询)
        @TableLogic
        private Integer isDeleted;
        //getter  setter
    

条件构造器和常用接口

wapper介绍

  • Wrapper:条件构造器抽象类,最顶端父类
    • AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
      • QueryWrapper : 查询条件封装
      • UpdateWrapper : Update 条件封装
      • AbstractLambdaWrapper : 使用Lambda 语法
        • LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
        • LambdaUpdateWrapper : Lambda 更新封装Wrapper

image-20231227172614915

QueryWrapper

组装查询
@Test
void test1(){
    //查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息
    //SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE
    // is_deleted=0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
    QueryWrapper<User>queryWrapper=new QueryWrapper<>();
    queryWrapper.like("name","a")
            .between("age",20,30)
            .isNotNull("email");
    List<User> userList = userMapper.selectList(queryWrapper);
    userList.forEach(System.out::println);
}

组装排序条件
@Test
void test2(){
    //按年龄降序查询用户,如果年龄相同则按id升序排列
    // SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE
    // is_deleted=0 ORDER BY age DESC,id ASC
    QueryWrapper<User>queryWrapper=new QueryWrapper<>();
    queryWrapper.orderByDesc("age")//降序
            .orderByAsc("id");//升序
    List<User> userList = userMapper.selectList(queryWrapper);
    userList.forEach(System.out::println);
}

组装删除条件
@Test
void test3(){
    //删除email为空的用户
    //DELETE FROM t_user WHERE (email IS NULL)
    QueryWrapper<User>queryWrapper=new QueryWrapper<>();
    queryWrapper.isNull("email");
    int result = userMapper.delete(queryWrapper);
    System.out.println("受影响的行数:" + result);
}

条件优先级
@Test
void test4(){
    //将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改 年龄18,邮箱aaa@a.com
    //UPDATE t_user SET age=?, email=? WHERE (username LIKE ? AND age > ?
    // OR email IS NULL)
    QueryWrapper<User>queryWrapper=new QueryWrapper<>();
    queryWrapper.gt("age",20)
            .like("name","a")
            .or()
            .isNull("email");
    User user = new User();
    user.setAge(18);
    user.setEmail("aaa@a.com");
    int result = userMapper.update(user, queryWrapper);
    System.out.println("受影响的行数:" + result);
}

组装select子句
@Test
void test5(){
    //查询用户信息的username和age字段
    //SELECT username,age FROM t_user
    QueryWrapper<User>queryWrapper=new QueryWrapper<>();
    queryWrapper.select("name","age");
    List<User> userList = userMapper.selectList(queryWrapper);
    userList.forEach(System.out::println);
}
实现子查询
@Test
void test6(){
    //查询id小于等于3的用户信息
    //SELECT id,username AS name,age,email,is_deleted FROM t_user
    // WHERE (id IN (select id from t_user where id <= 3))
    QueryWrapper<User>queryWrapper=new QueryWrapper<>();
    queryWrapper.inSql("id", "select id from user where id <= 3");
    List<User> userList = userMapper.selectList(queryWrapper);
    userList.forEach(System.out::println);
}

UpdateWrapper

    @Test
    void test7(){
        //将(年龄大于20或邮箱为null)并且用户名中包含有a的用户信息修改
        //组装set子句以及修改条件
        UpdateWrapper<User>updateWrapper=new UpdateWrapper<>();
        //lambda表达式内的逻辑优先运算
        updateWrapper.set("age",16)
                .set("name","sgadssa")
                .like("name","a")
                .and(i->i.gt("age",20).or().isNull("email"));
        //这里必须要创建User对象,否则无法应用自动填充。如果没有自动填充,可以设置为null
        //UPDATE t_user SET username=?, age=?,email=? WHERE (username LIKE ? AND (age > ? OR email IS NULL))
//        User user = new User();
        //user.setName("张三");
        int result = userMapper.update(null, updateWrapper);
        System.out.println(result);
    }

condition

@Test
void test08UseCondition(){
    //定义查询条件,有可能为null(用户未输入或未选择)
    String name=null;
    Integer ageBegin=10;
    Integer ageEnd=20;
    QueryWrapper<User>queryWrapper=new QueryWrapper<>();
    //StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace)构成
    queryWrapper.like(StringUtils.isNotBlank(name),"name","a")
            .ge(ageBegin!=null,"age",ageBegin)//大于等于
            .le(ageEnd!=null,"age",ageEnd);//小于等于
    //SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >= ? AND age <= ?)
    List<User> userList = userMapper.selectList(queryWrapper);
    userList.forEach(System.out::println);
}

插件

分页插件

MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能

添加配置

@Configuration
@MapperScan("cn.frozenpenguin.mapper")//可将主类中的注解移到这里
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new
                PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

测试

@Test
void testPage(){
    //设置分页参数
    Page<User>page=new Page<>();
    userMapper.selectPage(page,null);
    //获取分页数据
    List<User> list = page.getRecords();
    list.forEach(System.out::println);
    System.out.println("当前页:"+page.getCurrent());
    System.out.println("每页显示的条数:"+page.getSize());
    System.out.println("总记录数:"+page.getTotal());
    System.out.println("总页数:"+page.getPages());
    System.out.println("是否有上一页:"+page.hasPrevious());
    System.out.println("是否有下一页:"+page.hasNext());
}

该查询不会返回插入时间和更新时间

测试结果:

User(id=5, name=Billie, age=24, email=test5@baomidou.com)
User(id=1511332162436071425, name=lisi, age=2, email=aaa@qq.com)
User(id=1511527300890456066, name=lisi, age=2, email=aaa@qq.com)
User(id=1511527301301497858, name=lyl0, age=20, email=null)
User(id=1511527301301497859, name=lyl1, age=21, email=null)
User(id=1511527301301497860, name=lyl2, age=22, email=null)
User(id=1511527301368606722, name=lyl3, age=23, email=null)
User(id=1511527301368606723, name=asgag, age=23, email=null)
当前页:1
每页显示的条数:10
总记录数:8
总页数:1
是否有上一页:false
是否有下一页:false

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值