spring,mybatis,springboot,springmvc简单入门

一、软件架构 - 两种经典模式

  1. Client/Server(C/S)结构
    • 简单说就是 “客户端 + 服务器” 模式。比如手机上的微信 App、电脑上的 QQ,这些都属于 “客户端软件”。
    • 特点:需要专门安装客户端(像微信得下载 App),好处是能充分利用手机 / 电脑本地的硬件性能(比如让客户端处理一些数据计算),减少网络传输的压力。适合对交互性、性能要求高的场景(比如游戏、专业软件)。
  2. Browser/Server(B/S)结构
    • 也就是 “浏览器 + 服务器” 模式。现在常用的网页应用(比如在线文档、网页版邮箱)都属于这种 —— 只要电脑 / 手机装个浏览器(Chrome、Edge 等),直接通过网址访问就行。
    • 特点:不用专门装客户端,维护和更新更方便(服务器端改了,浏览器直接生效)。核心功能都在服务器上,客户端(浏览器)只负责显示和简单交互。适合大范围推广、跨设备使用的场景(比如企业办公系统、在线教育平台 )。

二、WEB 框架 - 开发效率神器

  • 可以理解为 “半成品的开发工具包”。比如盖房子时,框架就像预先搭好的 “钢筋骨架”,开发者不用从头开始设计整个结构,直接在上面填砖加瓦(写业务逻辑)。
  • 作用:提供现成的代码模板、工具(比如处理网络请求、操作数据库的通用代码),让程序员不用重复造轮子。像常见的 Spring Boot(Java)、Django(Python)都是 Web 框架,能快速搭建网站后台,处理登录、数据查询这些功能。

总结一下:

  • C/S 和 B/S 是软件的两种部署、使用模式,决定了用户怎么访问系统(装客户端 vs 用浏览器)。
  • Web 框架 是开发阶段的 “助力工具”,帮程序员更快写出 Web 应用,少写重复代码。
    本质是为了让开发更高效、维护更方便,适应不同的使用场景~

三、maven和springboot项目的创建

Maven 是 Apache 的一个顶级项目,是一个项目管理和构建自动化工具,主要用于 Java 项目,也可用于其他类型项目。

主要用于项目构建和依赖管理,右边的是Maven的依赖,可以通过搜索添加对应的依赖Maven Repository: org.springframework

Maven项目中首先需要看配置的本地仓库是否有对应的依赖,如果没有的话然后再看远程仓库是否有对应的依赖,如果还没有就去中央仓库看是否有需要的依赖项

依赖坐标

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
<!--  项目相关描述,公司名字路径和项目名-->
    公司域名
  <groupId>com.hualan</groupId>
	项目名称
  <artifactId>testmvn</artifactId>
    版本
  <version>1.0-SNAPSHOT</version>
<!--  项目写好之后的打包方式, 如果是web项目,就是以war包形式打包
父子项目时打包 pom包, 大多数时刻都是jar包-->
  <packaging>jar</packaging>

  <name>testmvn</name>
  <url>http://maven.apache.org</url>

  <properties>
<!--    设置项目的属性-->
      配置属性
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
<!--    项目依赖(maven管理):开发i项目时所需要的jar包都在这个结构里面
对pom文件更改后需要重新加载一下maven-->

<!--    下面是引入的jar包,jar包的配置,项目中添加单元测试模块包-->
      jar包
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
<!--      scope作用域:值test意思是只在test中使用,compile默认-->
        <scope>test</scope>
    </dependency>
<!--    mysql包:把jar的坐标写出来-->
    <dependency>
        公司域名
          <groupId>com.mysql</groupId>
        项目名
          <artifactId>mysql-connector-j</artifactId>
          <version>8.0.33</version>
<!--        作用域:运行时-->
        <scope>runtime</scope>
    </dependency>
      <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>RELEASE</version>
          <scope>test</scope>
      </dependency>
  </dependencies>
</project>

这个和SpringBoot的依赖大致是相同的,Spring Boot是为了简化Spring开发而诞生的,使用Spring boot能快速创建Spring应用,简化开发,约定大于配置。

在创建Springboot项目时选择的镜像

Spring官方镜像: https://start.spring.io/
阿里镜像(主推):https://start.aliyun.com

四、Spring的IOC、DI、AOP

Spring 是一款目前主流的 Java EE 轻量级开源框架,是 Java 世界最为成功的框架之一。Spring 由“Spring 之父”Rod Johnson(罗宾·约翰逊) 提出并创立,其目的是用于简化 Java企业级应用的开发难度和开发周期。

Spring的核心:

  • IOC控制反转
  • AOP面向切面编程

1. IOC是控制反转

在java中普遍创建一个类再使用new来创建一个对象,代码冗余过大,现在通过一个@Component注解修饰一个实体类,将这个实体类交给IOC容器管理,在IOC容器里存放相对应的Bean对象。创建一个类用@Configuration和@ComponentScan("包路径")修饰,想要创建对象时,调用ioc容器,并获取里面相对应的bean对象,最后交给一个实例。(只能创建出一个实体类对应的对象。

  • 简而言之就是通过Spring来管理类对应的一个对象,使用时将IOC容器初始化获取里面的Bean对象即可。(单例模式,工厂模式)

  • Spring IoC 容器创建、组装、管理的 Java 对象被称为Spring Bean对象

image-20250809102918122

对象依旧保存在堆内存中

  • 方法1:

@Component + @Configuration注解创建对象的方式

@COmponent注解:将类交给Spring IOC容器管理并创建对应的Bean对象。
@Configuration注解:标注这个类是配置类。(也归IOC管理)
@ComponentScan(路径)注解:扫描路径中是否有被@Component注解修饰得类,如果有的话交给IOC容器管理并创建对应的Bean对象。

创建一个King类

//将类交给ioc容器管理,创建一个bean对象
//声明创建对象时,如果没有指定名字,会以类首字母代表对象名字
@Component(value = "aa")    //value是默认的对象名,指定的名字是aa类似于King aa = new King();
public class King {
    public void eat(){
        System.out.println("King正坐在吃东西");
    }
}

创建配置类

//表示是一个配置类,里面存放的都是bean对象
@Configuration
//@ComponentScan(basePackages = "com.hualan.springboot01.bean")   //扫描指定包中添加了Component注解的类,
// 让Component实现Bean对象的创建,放进ioc容器中.使用的时候直接在bean中取就可以了
@ComponentScan(value = "com.hualan.springboot01.bean")
public class MyConfig {

}

测试类

@SpringBootTest
class SpringBoot01ApplicationTests {

    @Test
    public void testKing(){
        //创建一个IOC容器,通过配置类扫描配置类路径中的所有被@Component修饰的类,并创建对应的Bean对象
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        //痛过getBean方法获取到对应的bean对象
        King king = context.getBean("aa", King.class);
        //调用对象的方法
        king.eat();
    }
	
/*
*   创建这个ioc容器会指定写入的配置类文件,然后会自动调用Scan的注解取扫描指定的包里带有Component注解的类
*   并创建相对应的bean对象,如果Component中没有设置value值的话,默认的对象名就是类名小写;有value值的话就是默认值
*   bean对象在ioc容器中管理
* */
}
  • 方法2:

使用Configuration + Bean注解创建对象

创建一个实体类

public class User {
    public void sayHello() {
        System.out.println("Hello World");
    }
}

创建一个配置类

//表示是一个配置类,里面存放的都是bean对象
@Configuration
//@ComponentScan(basePackages = "com.hualan.springboot01.bean")   //扫描指定包中添加了Component注解的类,
// 让Component实现Bean对象的创建,放进ioc容器中.使用的时候直接在bean中取就可以了
@ComponentScan(value = "com.hualan.springboot01.bean")
public class MyConfig {

    //在配置类追踪创建一个对象并标注是bean
    @Bean(value = "user")
    public User test(){
        return new User();
    }
}

创建测试类通过IOC容器获取

@SpringBootTest
class SpringBoot01ApplicationTests {

    @Test
    public void testKing(){
        //创建一个IOC容器,通过配置类扫描配置类路径中的所有被@Component修饰的类,并创建对应的Bean对象
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        //痛过getBean方法获取到对应的bean对象
        King king = context.getBean("aa", King.class);
        //调用对象的方法
        king.eat();

        User user = context.getBean("user", User.class);
        user.sayHello();
    }

}

这个方法我感觉还没有上个方法好,

2. DI依赖注入

依赖注入就是实现IOC控制反转的一种方式,就是给一个类属性赋值的过程(取消手动初始化IOC容器的过程,可以直接通过Autowired自动装配在其他IOC管理的类中获取其他类的对象)。

但是通过手动初始化IOC容器的方式现在用的都不多了,因为现在不需要用代码再初始化IOC对象获取Bean了,启动类的注解已经包含了扫描的包和配置的类,会默认创建一个IOC容器,只需要对需要IOC容器管理的类加上@Component或者是他的子注解就可以了,在启动类或者是被@SpringBootTest修饰的测试类启动时,会自动扫描包创建IOC容器管理对应的类。需要调用时直接用@Autowired从IOC容器中取出对应的类的对象即可。可以直接对这个变量进行使用。

@SpringBootTest注解注意点

@SpringBootTest 本质是通过自动识别项目主配置、触发 Spring Boot 自动配置流程、整合测试框架生命周期,实现了 “零配置” 创建测试用 IOC 容器的效果。
这使得开发者无需手动编写容器初始化代码,就能在测试中直接使用与生产环境一致的 Bean,大幅简化了测试代码的编写
  • @Autowired自动装配(在IOC容器中通过类来获取对应的对象)Spring框架中的注解;
  • @Resource是默认按照名字获取对象的,如果名字不对应的话再按照类,是java中的注解。
  • 下面是ai给我的补充
需要明确的是,在现代 Spring Boot 应用中,手动初始化 IOC 容器的场景已极为罕见。这是因为:

1. 应用的启动类通过`@SpringBootApplication`注解(其内部包含`@ComponentScan`等元注解)指定了需要扫描的包路径和配置类,也会初始化IOC容器;
2. 当应用启动时(包括被`@SpringBootTest`修饰的测试环境启动时),框架会自动完成 IOC 容器的初始化,并根据类上的`@Component`及其派生注解(如`@Service`、`@Controller`等)识别需要纳入容器管理的 Bean,完成这些 Bean 的创建与注册;
3. 当某个 Bean 需要使用其他 Bean 时,开发者无需手动从容器中获取,只需在依赖字段、构造方法或 setter 方法上使用`@Autowired`等注解,容器便会在初始化当前 Bean 时自动完成依赖注入,开发者可直接使用注入后的对象进行业务操作。

用的比较多的还是@Autowired注解

  • DI就是给属性赋值的一个方式;

  • 说白了依赖注入实现的一种方式就是将ioc容器里面的对象给到另一个对象的属性里面;

3. 依赖注入的方式

  1. 构造器注入

    @Service
    public class UserService {
        private final UserRepository userRepository;
        
        // 构造方法注入
        @Autowired  // Spring 4.3+ 对单构造方法可省略此注解
        public UserService(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    }
    
  2. Setter方法注入

    @Service
    public class OrderService {
        private PaymentService paymentService;
        
        // Setter方法注入
        @Autowired
        public void setPaymentService(PaymentService paymentService) {
            this.paymentService = paymentService;
        }
    }
    
  3. 字段注入

    特点:
        代码简洁,不需要构造器和Setter方法
        
    @Controller
    public class UserController {
        // 字段注入
        @Autowired
        private UserService userService;
    }
    
  4. 通过注解注入

    • 创建一个实体类
    @Component
    //交给IOC容器并创建对应的对象
    public class Teacher {
        @Value("美汁汁")
        private String name;
        @Value("19")
        private int age;
        @Value("男")
        private String sex;
    
        @Override
        public String toString() {
            return "Teacher{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", sex='" + sex + '\'' +
                    '}';
        }
    }
    
    • 创建一个测试类
    @SpringBootTest
    public class TestNote {
        //通过@Autowired自动装配获取Bean中的Teacher对象,使用环节
        @Autowired
        Teacher teacher;
    
        @Test
        public void testTeacher() {
            //测试类将teacher对象的内容输出
            System.out.println(teacher);
        }
    }
    
  5. 在配置文件里配置(yml文件)

    • 创建一个实体类
    @Component
    @ConfigurationProperties(prefix = "teacher")
    //交给IOC容器并创建对应的对象
    public class Teacher {
    /*    @Value("美汁汁")
        private String name;
        @Value("19")
        private int age;
        @Value("男")
        private String sex;*/
    
        @Value("${teacher.name}")
        private String name;
        @Value("${teacher.age}")
        private int age;
        @Value("${teacher.sex}")
        private String sex;
    
        @Override
        public String toString() {
            return "Teacher{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", sex='" + sex + '\'' +
                    '}';
        }
    }
    
    • 在配置文件中写需要写进去的内容(yaml)
    teacher:
      name: 美滋滋
      age: 20
      sex: 男
    
    • 创建一个测试类
    @SpringBootTest
    //标识的是启动类中的@SpringBootApplication注解,启动项目,会扫描启动类所在的包下所有的包和子包(其实就是扫描整个项目的包)
    public class TestNote {
        //通过@Autowired自动装配获取Bean中的Teacher对象
        @Autowired
        Teacher teacher;
    
        @Test
        public void testTeacher() {
            //测试类将teacher对象的内容输出
            System.out.println(teacher);
        }
    }
    

多种方法现在用的最多的还是第三种直接在字段上直接添加@Autowired注解直接注入。

说一下@Autowired和@Resource两者的区别

@Autowired:
默认按照类型(byType)进行自动装配
当容器中存在多个相同类型的 Bean 时,会抛出 NoUniqueBeanDefinitionException
可以结合 @Qualifier 注解指定要注入的 Bean 的名称(byName)
@Qualifier:
不能单独使用,必须与 @Autowired 配合
指定要注入的 Bean 的名称,解决同类型多个 Bean 的冲突问题
(如果有相同的类型的话可以用@Qualifier注解指定名)
    
----------------------------------------------------------------
@Resource(注意是 javax.annotation.Resource,不是 Spring 的注解):
默认按照名称(byName)进行装配
如果找不到对应名称的 Bean,会再按照类型(byType)进行装配
可以通过 name 属性指定要注入的 Bean 的名称

五、Spring整合Mybatis

重点:持久层框架持久指的是什么(通过Mybatis可以将对象的数据存储到数据库里面);mybatis的概念;ORM什么;如何实现;

动态参数

1. Mybatis介绍

  • MyBatis 是一个优秀的 Java 持久层框架,用于简化jdbc开发,半自动化ORM框架,通过 XML 或注解方式将 SQL 语句与Java 方法进行映射,简化数据库操作,提供了灵活的 SQL 管理能力,同时保留了对原生 SQL 的完全控制。MyBatis 本是 Apache 的一个开源项目 iBatis,2010年这个项目由Apache Software Foundation迁移到了Google Code,并且改名为MyBatis。
  • 本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件 + 配置几个sql映射文件,易于学习,易于使用。通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
ORM:对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与
关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映
射的元数据,将程序中的对象自动持久化到关系数据库中

通常业务上将java项目分为三层

1、controller 控制层 (响应前端)

2、service 业务层 (写逻辑代码)

3、dao 持久层(其实就是写数据的,创建对象的)

2. Mybatis的快速入门

在程序中,数据库表里面的字段名需要与实体类中字段名都相同(java中更支持用对应的包装类),sql表中的一行内容对应的是java中的一个对象,通常用对象来接收一行内容,那么多行就需要用到一个集合来接收这整个表里面对应的内容,

在idea中使用Mybatis查询数据库表

1. 需要导入一些包将Mybatis整合进Spring项目中

<!--        mysql驱动包-->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.0.33</version>
            <scope>runtime</scope>
        </dependency>
<!--        引入mybatis包启动器-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.1</version>
        </dependency>

2. yaml文件负责连接本地数据库

jdbc:mysql://				是协议部分
localhost:3306/数据库名		  是端口号和数据库名
下面是完整的
spring:
  datasource:
    # 配置驱动类
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 数据库连接 jdbc:mysql://127.0.0.1:3306/数据库名
    url: jdbc:mysql://127.0.0.1:3306/user
    # 数据库的用户名和密码
    username: root
    password: 123456

3. 创建一个实体类

//实体类
@Data
//在编译时期为所有属性添加get/set方法和toString(),hashcode(),equals(),不包括无参和有参构造
@NoArgsConstructor
//提供一个无参构造器
@AllArgsConstructor
//提供一个有参构造
//需要在pom文件里面添加一个lombok依赖的jar包
public class User {
    private Integer id;
    private String name;
    private Short age;
    private Short gender;
    private String phone;

    /*public User() {
    }

    public User(Integer id, String name, Short age, Short gender, String phone) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.phone = phone;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Short getAge() {
        return age;
    }

    public void setAge(Short age) {
        this.age = age;
    }

    public Short getGender() {
        return gender;
    }

    public void setGender(Short gender) {
        this.gender = gender;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                ", phone='" + phone + '\'' +
                '}';
    }*/
}

4. 在项目包目录下创建一个mapper包,并创建UserMapper接口然后编写sql语句(复杂的sql语句需要在resource包下创建一个mapper.xml文件里面连接对应的mapper接口在里面编写sql语句)

@Mapper
//运行时框架会自动生成实现类对象,并且将对象交给IOC容器管理
public interface UserMapper {
    //查询全部用户信息
    @Select("select * from user")
    public List<User> getUser();
//接口会动态生成对应的实现类对象
}

5. 单元测试:在测试类中测试userMapper接口里面的方法

@SpringBootTest
//整合单元测试的注解,会默认创建测试的IOC容器
class HeimaMybatisApplicationTests {
    //自动装配
    @Autowired
    UserMapper userMapper;

    @Test
    public void getUser(){
        //获取到的是一个集合,返回的都是对象,
        for (User user : userMapper.getUser()) {
            System.out.println(user);
        }
    }
}

3. 什么是jdbc?

sun公司提供的使用java语言操作关系型数据库的API就是jdbc, 但是还是各个厂商提供jdbc的实现(Mysql,oracle...),我们直接使用jdbc提供的接口就可以了,厂商写的实现统称为驱动,

JDBC(Java Database Connectivity)是 Java 语言操作数据库的标准 API(应用程序编程接口),它为 Java 程序提供了一套统一的方法来访问各种关系型数据库(如 MySQL、Oracle、SQL Server 等)

六、Mybatis的基本操作(增删改查CRUD)

  • 进行增删改查的基本顺序就是下面,但是基本先把配置文件写好
# 数据库驱动
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/shifangzhiyu
    username: root
    password: 123456

mybatis:
  # 映射文件
  mapper-locations: classpath:mapper/*.xml
  # 包别名
  type-aliases-package: com.hualan.pojo
  configuration:
    # 配置日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 让表字段(有下划线)与对象的属性(小驼峰)形成映射关系,否则会因为数据字段属性名不同导致映射关系失败
    map-underscore-to-camel-case: true
    # 进程死锁时候,设置sql超时时间,单位是秒(s)
    default-statement-timeout: 20

# 分页插件
pagehelper:
  reasonable: true
  • 还有就是pom.xml文件里需要导入需要的jar包的坐标
        <!--mysql驱动包-->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.0.33</version>
            <scope>runtime</scope>
        </dependency>
        <!--mybatis启动包-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.1</version>
        </dependency>
        <!--lombok包-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

1. 开始进行mybatis的基本操作

以上是基本操作 , 下面才开始真正的增删改查操作哈哈哈,开始下手吧

  1. 先创建实体类
    • 实体类的属性与数据库表的数据类型相同
    • 属性名以大驼峰形式,数据库名有 '_' 下划线表示
  2. 再创建对应的Mapper接口文件
    • Mapper接口文件里面写方法;
    • 接口文件里面的方法写几个映射文件里就需要写几个sql语句;
  3. 然后在resource包里创建一个Mapper映射文件
    • 映射文件里面写SQL语句;
  4. 最后在测试类中测试写的方法

实体类Student写在pojo包里 , 接口文件写在mapper包里 , 两个包都在项目名下

  • 创建一个Student实体类
//实体类
@Data	//get/set/toString()/hashCode()/equals()方法
@NoArgsConstructor  //空参构造
@AllArgsConstructor //有参构造
public class Student {
    private Integer id;
    private String name;
    private Integer age;
    private String gender;
    private Date birthday;
    private String homeAddress;

}
  • 再创建对应的mapper接口文件
    • @Mapper 注解是 MyBatis 框架的注解 , 其核心作用是标识该接口是 MyBatis 的映射接口,告诉 MyBatis 框架 “需要为这个接口生成动态代理实现类”,将动态代理实现类交给创建成bean对象并交给ioc容器管理
//交给ioc容器管理(可以创建接口的动态代理对象)
@Mapper
//属于mybatis里面的注解,关联到对应的xml文件之后,
// 在映射文件中必须写出与方法对应的sql语句
//增删改返回值类型都是int(影响到xx行数据)查询的结果是对应的自定义数据类型
public interface StudentMapper {
    //增
    public abstract int addStudent(Student student);

    //删
    //删除一条学生信息
    public abstract int deleteStudentById(Integer id);

    //改
    //修改指定学生信息
    public abstract int updateStudent(Student student);

    //查

    //根据姓名找到学生信息
    public abstract List<Student> getInfoByName(String name);

    //查询所有的学生信息
    public abstract List<Student> StudentList();
    //查询student对象总数
    public abstract Integer getStudentCount();
    //根据学生id获取对应的学生信息
    public abstract Student getInfoById(Integer id);

    //根据姓名模糊查询
    public abstract List<Student> getInfoByNameLike(String name);
}
  • 在resource包下创建一个mapper包并创建一个对应的Mapper映射文件

    • 在这个mapper映射文件里面写sql语句,而且需要将接口文件里面的所有抽象方法都实现了;

    • namespace : 命名空间,填充的是接口文件的类路径 , 让xml文件与接口文件之间产生关联 , 需要写出对应的mapper接口文件的类地址 , 如 : edu.henu.mapper.xxMapper

    • resultType : 对应的是返回值类型的类地址(自定义类型需要将类地址全部都写出来,如:edu.henu.pojo.xx) 一般集合元素返回值数据类型写的是泛型里规定的数据类型

      image-20250811211601526

    • parameterType:对应的是接口文件里面方法里写的形式参数的数据类型的类地址 (一般都是实体类的类型)

<?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.hualan.mapper.StudentMapper">
<!--    namespace:命名空间,值是接口文件的类路径,让xml文件与接口文件之间产生关联-->
<!--    接口中的方法必须在xml文件中有对应的SQL语句-->
<!--    返回值类型是自定义类对象的数据类型需要写类的类路径-->
<!--    增 insert-->
    <insert id="addStudent" parameterType="Student">
        insert into student values( null, #{name}, #{age}, #{gender}, #{birthday})
    </insert>

<!--    删 delete-->
    <delete id="deleteStudentById" parameterType="Integer">
        delete from student where id = #{id};
    </delete>

<!--    改 update-->
    <update id="updateStudent" parameterType="Student">
        <!---->
        update student set name = #{name}, age = #{age}, gender = #{gender}, birthday = #{birthday} where id = #{id};
    </update>

<!--    查 select-->
    <!--获取学生总数-->
    <select id="getStudentCount" resultType="Integer">
        select count(*) from student;
    </select>

    <select id="getInfoByName" parameterType="java.lang.String" resultType="Student">
        select * from student where name = #{name};
    </select>

<!--    java接收数据的时候用的是一个对象接收,所以用的是一个Student类型的数据,
返回值类型:resultType:写的是一个类路径(从公司域名开始写)-->
    <!--获取所有的学生信息 当返回的是一个集合时,返回值类型resultType直接写泛型的数据类型即可-->
    <select id="StudentList" resultType="Student">
        select * from student order by birthday desc;
    </select>

<!--    parameterType代表的是接受的参数类型-->
    <!--根据id获取学生信息-->
    <select id="getInfoById" resultType="Student" parameterType="Integer">
<!--  #{形参名字}是条件语句 预编译的模式-->
        select * from student where id = #{id};
    </select>

----------------------------------模糊查询的方式------------------------------------------
        用concat()方式用多了
<!--    查询方式一: 用concat()函数拼接的方式进行模糊查询-->
<!--    <select id="getInfoByNameLike" parameterType="string" resultType="Student">
        select * from student where name like concat('%', #{name}, '%');
    </select>-->
    <!--查询方式2: -->
<!--    <select id="getInfoByNameLike" parameterType="string" resultType="Student">
        select * from student where name like '%' #{name} '%';
    </select>-->
    <!--查询方式3: 用的不多-->
    <select id="getInfoByNameLike" parameterType="string" resultType="Student">
        select * from student where name like '%${name}%';
    </select>

</mapper>
        
形式参数

resultType:是自动映射,可以将数据库表中的字段自动对应到java实体类属性对应的属性上

resultMap是手动映射,下面再写。

需要知道#{} 与 ${}的区别?

传参传进去的参数在SQL语句中可以用 #{参数名} 代表一个值,select * from student where id = 1等同于 select * from student where id=#{id},前提是id的传进去的参数

同理传进去的对象,可以用#{对象属性}代表一个值, 类似于 name=#{name} ,后面的#{name}是传入参数的对象属性

#{} 与 ${}的区别?

1. #{}:预编译处理(参数绑定)
MyBatis 会将 #{} 替换为 SQL 中的 ? 占位符,然后通过 JDBC 的 PreparedStatement 进行预编译,再将参数值填充到占位符中。
示例:
SQL 映射中写 SELECT * FROM user WHERE id = #{id},实际执行时会变为:
SELECT * FROM user WHERE id = ?(预编译后,参数 id 的值通过 setInt() 等方法传入)。

2. ${}:字符串拼接(直接替换)
MyBatis 会直接将 ${} 替换为参数的字符串值,相当于简单的字符串拼接,不会进行预编译处理。
示例:
SQL 映射中写 SELECT * FROM user WHERE psd = ${psd},若参数 psd 为 ' or 1 ',则实际执行的 SQL 是:
SELECT * FROM user WHERE psd = '' or 1 ''(参数值直接拼接到 SQL 中)。导致sql注入


简单说:
   ${参数} 会导致字符串拼接,存在sql注入的风险,可能会拼接sql语句
   #{参数} 预编译阶段,较为安全,防止sql注入,传递的是数值
   
   
   
#{} 是 MyBatis 的预编译参数占位符,会转为 ? 并通过 JDBC PreparedStatement 安全传
参,能有效防止 SQL 注入,推荐用于传递字段值等。
${} 是 MyBatis 的字符串替换符,会直接将变量值拼接到 SQL 语句中,不进行预编译或转
义,存在 SQL 注入风险,一般只用于动态表名、列名等可信场景
  • 创建一个测试类
@SpringBootTest
public class TestCRUD {

    @Autowired
    private StudentMapper studentMapper;

    //添加学生信息
    @Test
    public void testStudent(){
        //创建学生对象
        Student student = new Student();
        student.setName("张无忌");
        student.setGender("男");
        student.setAge(18);
        student.setBirthday(new Date());
        //添加学生信息
        int result = studentMapper.addStudent(student);
        System.out.println(result);
    }

    //删除学生信息
    @Test
    public void testDeleteStudent(){
        int result = studentMapper.deleteStudentById(1);
        System.out.println(result);
    }

    //修改学生信息
    @Test
    public void testUpdateStudent(){
        //修改学生信息需要先获取到要修改的学生对象,然后对这个对象的某些属性进行修改
        Student student = studentMapper.getInfoById(5);
        student.setName("朱重八");
        student.setAge(20);
        int result = studentMapper.updateStudent(student);
        System.out.println(student);
        System.out.println(result);
    }

    @Test
    public void testGetStudentByName(){
        List<Student> list = studentMapper.getInfoByName("张无忌");
        System.out.println(list);
    }

    @Test
    public void testShowStudent(){
        List<Student> students = studentMapper.StudentList();
        for (Student student : students) {
            System.out.println(student);
        }
    }

    //根据姓名模糊查询
    @Test
    public void selectLikeByName(){
        List<Student> students = studentMapper.getInfoByNameLike("张");
        for (Student student : students) {
            System.out.println(student);
        }
    }
    @Test
    public void testSelect(){
        //获取学生信息
        System.out.println(studentMapper.StudentList());
        //获取学生总数
        System.out.println(studentMapper.getStudentCount());
        //根据学生id获取学生信息
        System.out.println(studentMapper.getInfoById(1));
    }
}

2. Mapper接口多个参数的模糊查询

Mapper接口多个参数的查询方式我用了3种

  1. 通过传入一个对象,对对象属性进行模糊查询
  2. 传入一个Map集合,定义对应的字段进行查询
  3. 传入多个参数,与多个参数进行对比(多个参数需要用多个@Param("别名")来重命名参数)
  4. 通过传进去

1、通过传入一个对象的方式,解决多个条件查询问题

  • 实体类Teacher
@Data   //相当于getter/setter方法和hashCode()和equals()还有toString()方法
@NoArgsConstructor  //无参构造器
@AllArgsConstructor //有参构造器
public class Teacher {
    private Integer id;
    private String name;
    private Integer age;
}
  • 编写对应的Mapper接口文件
@Mapper
public interface TeacherMapper {
    //根据姓名模糊和年龄具体查询
    public abstract List<Teacher> getTeachersByNameAndAge(Teacher teacher);

    //根据姓名模糊和年龄范围查询
    public abstract List<Teacher> getTeachersByNameAndRangeAge(Map<String, Object> params);

    //注解的方式
    /*低版本的Mybatis不支持多参数直接使用别名,所以说需要使用@Param注解设置一个别名*/
    public abstract List<Teacher> getTeachersByNameAndRangeAge02(@Param("name") String name, @Param("minAge") Integer minAge,@Param("maxAge") Integer maxAge);
}
  • 编写对应的Mapper.xml文件
<?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.hualan.mapper.TeacherMapper">

    //通过传进来一个对象进行多参查询
    <select id="getTeachersByNameAndAge" parameterType="Teacher" resultType="teacher">
        select * from teacher where name like concat('%', #{name}, '%')
                                and age = #{age};
    </select>
	//通过传进一个map集合进行多参查询
    <select id="getTeachersByNameAndRangeAge" resultType="teacher" parameterType="java.util.Map">
        select * from teacher where name like concat('%', #{name}, '%')
                                and age between #{minAge} and #{maxAge}
    </select>
	//通过传进多个参数进行查询
    <select id="getTeachersByNameAndRangeAge02" resultType="teacher">
        select * from teacher where name like concat('%', #{name}, '%')
                                and age between #{minAge} and #{maxAge}
    </select>

</mapper>
  • 编写测试类
@SpringBootTest
public class TestTeacher {
    @Autowired
    private TeacherMapper teacherMapper;

    @Test
    public void testTeacher01() {
        Teacher teacher = new Teacher();
        teacher.setName("云");
        teacher.setAge(50);
        //获取到名字里带云的,并且年龄等于50岁。
        List<Teacher> teachers = teacherMapper.getTeachersByNameAndAge(teacher);
        for (Teacher teacher1 : teachers) {
            System.out.println(teacher1);
        }
    }
}

2、通过传进去一个map集合进行多参查询

  • 依旧使用Teacher实体类
  • 依旧使用上面的Mapper接口文件创建方法 getTeachersByNameAndRangeAge
  • 上面的Mapper.xml文件里面的,可以将map集合里面的key名直接用#{key名}包裹代表一个值可以映射到value,与数据库字段名进行对比
	//通过传进一个map集合进行多参查询
    <select id="getTeachersByNameAndRangeAge" resultType="teacher" parameterType="java.util.Map">
        select * from teacher where name like concat('%', #{name}, '%')
                                and age between #{minAge} and #{maxAge}
    </select>
  • 编写测试类
    @Autowired
    private TeacherMapper teacherMapper;
    //用Map集合传参
    @Test
    public void testTeacher02() {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "云");
        map.put("minAge", 20);
        map.put("maxAge", 60);
        teacherMapper.getTeachersByNameAndRangeAge(map);
    }

3、通过写进去多个参数用@Param("别名") 给单个属性设置别名可以用写的别名在xml映射文件中使用

​ 注意:

1. 多个参数时可以使用@Param给形参一个别名,如果不给的话就是arg0,arg1....可读性不高;
2. 亦或者参数是一个集合泛型,也需要用@Param给集合设置一个别名。
  • 上面接口文件中第三个查询方法就是通过多个参数设置别名
    //注解的方式
    /*低版本的Mybatis不支持多参数直接使用别名,所以说需要使用@Param注解设置一个别名*/
    public abstract List<Teacher> getTeachersByNameAndRangeAge02(@Param("name") String name, @Param("minAge") Integer minAge,@Param("maxAge") Integer maxAge);
  • 映射文件里面的内容如下
	//通过传进多个参数进行查询
    <select id="getTeachersByNameAndRangeAge02" resultType="teacher">
        select * from teacher where name like concat('%', #{name}, '%')
                                and age between #{minAge} and #{maxAge}
    </select>
  • 测试类测试内容
    @Test
    public void testTeacher03(){
        List<Teacher> teachers = teacherMapper.getTeachersByNameAndRangeAge02("云", 20, 60);
        System.out.println(teachers);
    }

3. 分页插件

  • 导入依赖包
<!--手动引入pagehelper的启动器-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.1</version>
</dependenc
  • 在配置类中配置分页内容(用的是yaml文件,所以说里面很注重缩进的内容)
# pagehelper配置
pagehelper:
#开启合理化分页
	reasonable: true
  • 对上面写的Student实体类进行查询并且使用分页插件进行分页
@Authord
StudentMapper studentMapper;
@Test
public void fenye(){
// 1 设置分页信息
	PageHelper.startPage(2,2);
// 2 查询
	List<Student> students = studentMapper.selectAll();
    // 3 动态获取分页后相关的数据参数
    PageInfo<Student> pageInfo = new PageInfo<>(students);
    System.out.println(pageInfo.getTotal()); // 总条数
    System.out.println(pageInfo.getPageNum()); // 页码
    System.out.println(pageInfo.getPages()); // 总页数
    System.out.println(pageInfo.getPageSize()); // 每页展示条数
    System.out.println(pageInfo.getList()); // 数据集合
}

4. 动态SQL查询

主要是因为平时的条件查询就是通过将条件写死,不能修改直接进行查询,如果需要根据其他条件进行查询的时候需要再编写一条查询语句,所以说就需要动态sql查询

主要就是在xml映射文件里面进行修改,例如在进行修改的时候通过 xx如果条件判断为true的话,就会执行对应的sql语句

    <!--改-->
    <update id="updateCertificate" parameterType="certificate">
        update tb_certificate
        <set>
            <if test="certiName != null and certiName!=''">certi_name = #{certiName},</if>
            <if test="image != null and image != ''">image = #{image},</if>
            <if test="id != counselorId">counselor_id = #{counselorId}</if>
        </set>
        where id = #{id}
    </update>

还有就是用xx这样的结构也可以进行动态sql查询

    <update id="updateCertificate" parameterType="certificate">
        select* from tb_certificate
        <where>
            <if test="certiName != null and certiName!=''">certi_name = #{certiName} and</if>
            <if test="image != null and image != ''">image = #{image} and</if>
            <if test="id != counselorId">counselor_id = #{counselorId}</if>
        </where>
    </update>

如果只有一条sql条件满足的话标签可以将sqlwhere条件筛选中多余的and不识别。

image-20250814202747905

1. if条件判断语句,判断这段语句是否参与sql查询,只有满足条件才会被拼接到sql语句中
<select id="findUsers" resultType="User">
	SELECT * FROM user
	<where>
		<if test="name != null">
			AND name = #{name}
		</if>
		<if test="age != null">
			AND age = #{age}
		</if>
	</where>
</select>
2. —— 多路选择(类似switch-case),我没用过,如果类似于switch-case那是不是就可以用if替代了哈哈哈
<select id="findActiveUser" resultType="User">
SELECT * FROM user
<where>
<choose>
<when test="status == 1">
AND active = 1
</when>
<when test="status == 0">
AND active = 0
</when>
<otherwise>
AND deleted = 0
</otherwise>
</choose>
</where>
</select>
3. 标签都可以消除拼接的sql语句中其他的类似于逗号and一类的关键字
4.循环语句,可以用于批量删除等操作,主要是对输入的集合参数进行循环遍历,一一拼接到sql语句中,写这个集合参数的时候需要用@Param(“”)设置别名
<select id="findByIds" resultType="User">
	SELECT * FROM user
		WHERE id IN
		<foreach collection="ids" item="id" open="(" separator="," close=")">
			#{id}
		</foreach>
</select>

上面代码中foreach中各个属性代表的含义分别是
	collection="集合名",这里的集合名就是参数设置的别名,如果没有设置别名的话好像用arg0表示
	item="",这个item表示的是遍历到的集合元素的名字
	open="(",就是将 in()这个前面的半个括号补全
	close=")",就是将in()后面的半个括号补全
	separator=","意思是元素与元素之间的分隔符
最后都会参与sql语句拼接
5. 还有一个自定义裁剪字符串,我没用过
<trim prefix="WHERE" prefixOverrides="AND |OR ">
	<if test="name != null">AND name = #{name}</if>
	<if test="age != null">OR age = #{age}</if>
</trim>
6. sql片段,就是查询语句中因为用select * from....星符号对比将所有字段写出来会浪费性能,所以说为了避免多次重复写所有字段导致代码冗余度过高,用标签将会重复多次出现的代码都包括在一起,并设置一个 属性 id="", 根据这个属性对这个sql片段进行调用即可,用标签属性refid=""使用对应的sql片段
<!-- 定义一个sql,来定义其通用的查询的前半部分 -->
<sql id="sl">
		select id,name,sex,age from student
</sql>

<select id="selectByLikeName3" parameterType="java.lang.String"
resultType="student">
<include refid="sl"></include>
where name like '%${name}%'
</select>

七、表与表之间的关系

学习mysql时知道表与表之间的关系主要分为三种

  1. 一对一

    • 用association标签进行一对一实体类属性和数据库表字段之间的映射
  2. 一对多

    • 一对多主表对应的实体类扩展私有属性为从表对象对应的集合
    • 自定义一对多之间的映射
  3. 多对多(查询的时候需要中间表关联)

    • 看待单个对象时也是一对多的关系,在私有属性中添加从表对象对应的集合
    • 标签映射从表对应的属性

多对多映射中,对于一张表中的一个对象而言,另一张表跟它之间就是一对多的关系,返回的是一个集合

解决多表问题时通常建立在一下流程上(数据库中已经有对应的表)

1. 分析两张表之间的关系,确定主表,在navicat上先把sql语句写出来确定自己需要的表内容和字段,最后写的时候可以根据这个内容进行判断
2. 在主表的实体类私有属性上添加对应的从表的对象或者是对象对应的集合泛型
3. 编写Mapper接口文件
4. 编写Mapper.xml映射文件,在映射文件中用resultMap进行手动映射

注意:多表查询的时候先确定主表,分析出多张表之间的主表然后利用表与表之间的关系进行

在多表联查的关系中不能再用到 resultType进行自动映射 , 需要用到 resultMap进行手动映射,先拿一段代码把映射的规则说一下

  • 首先就是把结果的返回值resultType换成resultMap,再在外面通过双标签里面的自定义属性值id的值给到resultMap
  • 自定义的resultMap标签返回的值是mapper接口文件里面方法返回的返回值类型
  • 然后可以把要查的属性一一对应,主键用标签绑定,其他的属性用标签绑定,column对应的是数据库表中的字段名,property对应的是实体类的属性名。(如果数据库里面设置了别名,那就一定要用别名,防止字段不能映射到)
  • 如果是一对一的关系的话将从表对应的实体类在主表对应的实体类中创建出对应的对象的私有属性,然后在resultMap自定义映射中,用标签将另一个实体类中的字段与属性一一对应上(起别名用别名)
  • 在一对一映射中,association标签获取返回值类型用的是javaType,一对多中collection标签属性用的是ofType获取返回值类型

下面是自定义映射关系有一对一,一对多的关系中使用collection标签和association分别映射的例子,不用看sql语句,看映射关系就行

			# 对应第一点说的自定义id属性值    
<resultMap id="consultant_contant_level" type="consultantLevel">
        <id property="id" column="id"></id>
        <result property="levelName" column="cl_name"></result>

        <collection property="consultantList" ofType="consultant">
            <id property="id" column="id"></id>
            <result property="name" column="name"></result>
            <result property="image" column="image"></result>
            <result property="age" column="age"></result>
            <result property="levelId" column="level_id"></result>
            <result property="intro" column="intro"></result>
            <result property="isCertified" column="ct_ic"></result>
            <result property="createdAt" column="created_at"></result>
            <result property="updatedAt" column="updated_at"></result>
        </collection>
    </resultMap>
	# 第一点说的resultMap标签里的自定义id值对应的就是这个resultMap属性对应的值
    <select id="getConsultLevel" resultMap="consultant_contant_level">
        select cl.*, ct.*, cl.level_name cl_name, ct.name, ct.image, ct.is_certified ct_ic from consultant_level cl
            left join consultant ct on ct.level_id=cl.id;
    </select>


------------------------------------------------------------------------------------------------------
    <resultMap id="response_consult" type="consultService">
        <id property="id" column="id"/>
        <result property="userId" column="user_id"/>
        <result property="askContent" column="cs_ac"/>
        <result property="consultantId" column="consultant_id"/>
        <result property="status" column="status"/>
        <result property="addTime" column="cs_at"/>
        <result property="endTime" column="end_time"/>
        <!--一对一-->
        <association property="psyResponse" javaType="PsyResponse">
            <id property="id" column="id"/>
            <result property="replyContent" column="pr_rc"/>
            <result property="replyTime" column="pr_rt"/>
            <result property="userId" column="user_id"/>
            <result property="consultId" column="consult_id"/>
        </association>
        <association property="consultationRating" javaType="consultationRating">
            <id property="id" column="id"/>
            <result property="userId" column="user_id"/>
            <result property="consultId" column="consult_id"/>
            <result property="startNum" column="start_num"/>
            <result property="judgeContent" column="judge_content"/>
            <result property="judgeTime" column="judge_time"/>
            <result property="consultId" column="consult_id"/>
        </association>
    </resultMap>
    <select id="getConsultServiceByUser" resultMap="response_consult">

        SELECT cs.*, cs.add_time cs_at, cs.ask_content cs_ac, pr.reply_content pr_rc, pr.reply_time pr_rt, cr.*
        from consult_service cs left join psy_response pr ON cs.id = pr.consult_id LEFT join consultation_rating cr on cs.id=cr.consult_id
        WHERE cs.user_id = 1 and cs.`status`= 1;
    </select>

在这种映射关系中可以只写出需要查询的字段属性他们的映射关系,其他不查询可以不映射。

注意:

1. 如果我用的是yaml文件进行配置的话,需要将properties配置文件里面的所有内容都删除,或者把properties文件删除,不然他的优先级高于yaml,会导致properties文件里面的内容先生效,等于说yaml文件配置不会生效;这也是我大意遇到的一个问题,发现映射文件找不到mapper,我以为yaml文件里面数据库连错了,发现还真是,结果我改回来之后还是不行,最后把所有接口文件写完,测试全部报错,最后误触了properties文件,发现里面的内容没有删除.........对自己好无语
2. 在多表联查中先将sql查询语句写出来,然后在编写接口文件中的方法和对应的mapper.xml文件中的方法,将映射关系一一对应出来,可以只展示部分只想展示的字段。

下载了mybatis-X之后可以直接生成对应的实体类,mapper接口文件,mapper.xml映射文件

image-20250815204405397

image-20250815204419350

只有第二张图的mapper文件需要改变路径;

八、springboot的基本使用

springboot框架主要就是简化spring框架的开发,等同于springMVC+Spring的效果,可以轻松写出来一个web接口(RequestMapping+方法),springboot内置了tomcat服务器,项目可以直接跑起来并通过http提供服务,不用额外部署

下面是我的理解:

在springboot项目跑起来之后会将项目部署到tomcat服务器,tomcat服务器主要就是负责监听客户端和服务端之间的数据请求和响应的过程,
前端请求时,tomcat会按照http协议解读请求内容,然后将内容交给后端服务器(就是springmvc)进行处理;
服务器会对前端的请求做出响应,这个时候,tomcat服务器会将相应的数据按照http协议发给前端。


-- 下面的是我刚听到时的理解
    tomcat监听前端发送过来的数据,并按照http协议规定的形式解读,解读完毕之后,将数据交给springmvc框架,
    springmvc框架将数据交给controller控制器类,将数据交给方法处理,通过tomcat将数据按照http协议响应出去,
    将处理结果返回给前端
    用一个@Controller或@RestController来标记一个类是控制层的类。

Spring Boot 内嵌了 Tomcat 服务器,因此在启动时无需额外部署,应用可直接通过内置的 Tomcat 监听 HTTP 请求,快速提供 Web 服务。

1. HTTP协议

我的理解http协议就是浏览器和服务器之间数据交互的一种协议,所有的数据都需要按照这个规定传输。

  • HTTP(HyperText Transfer Protocol),即超文本传输协议。这个协议详细规定了浏览器和万 维网服务器之间互相通信的规则。
  • HTTP协议的作用:HTTP就是一个通信规则,通信规则规定了客户端发送给服务器的内容格式,也 规定了服务器发送给客户端的内容格式。
  • 客户端发送给服务器的格式叫“请求协议”【也称之为请求报文】;
  • 服务器发送给客户端的格式叫“响应协议”【也称之为响应报文】;
1.请求协议的主要结构:
  1. 请求行:包括请求方式,请求资源路径,http协议版本
  2. 请求头:都是以键值对的形式存在的,记住其中一个 User-Agent,它可以显示发送请求的设备型号,通过具体的参数对本次请求进行详细的说明;
  3. 请求体:包含了请求的内容(只有post请求方式的请求内容才放在请求体中)

image-20250817104357720

2. 请求方式:

image-20250817104430983

目前我初学者用得比较多的还是增删改查对应的四个请求方式:

讲解一下post和get两种请求方式有什么不同点:

  1. get请求主体在地址栏,post请求主体在请求体中
  2. post请求对比get请求方式更加安全
  3. 编码方式不同,get请求用的是地址栏编码,post用的是二进制编码,将参数转换成二进制进行传递
  4. 参数长度不一,get请求方式请求长度有限,但是post请求方式理论上是可以无限长的。
3. 响应数据的主要结构:
  1. 响应行:包含协议和状态码
  2. 响应头:
  3. 响应体:函数的返回参数

状态码如下:

image-20250817105049991

目前见过最常见的就是:

500:服务器出现错误,也可能是后端代码写错了
404:请求路径找不到
405:请求的方式与测试用的请求方式有误
400:前端请求内容错了
200:测试成功通过

3xx:重定向(我的理解就是平时地址栏只搜一个baidu,他就会直接给你弄到www.baidu.com我认为这样可能就是重定向)

http协议中定义了浏览器发送请求的打包方式,加密方式,传输方式,传输信息,传输速度等等标准 URL(统一资源定位符)格式: http://ip:端口号/资源信息

4. 三层架构
  • MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部
    分:模型层(Model)、视图层(View)和控制层(Controller)。
    M:Model(通常包含entity实体类层、service业务逻辑处理层、dao(mapper)持久化层);
    V:View,视图层,视图层主要的技术(JSP、HTML、FreeMaker、Themeleaf、Vue、小程序);
    C:Controller,控制层。控制层不负责具体数据、逻辑的处理和运算,它只负责将Model层的结果返回
    给对应的视图层去展示

image-20250817103614260

2. Controller控制层

​ 通过一个@RestController注解标识一个类是controller层(一个@Controller注解也可以但是不会把方法返回值作为响应主体返回出去),@RestController相当于是@ResponseBody注解和@Controller注解的合并,所以说@RestController注解的主要作用就是标识一个类是控制类,并且还可以将服务器响应数据作为响应主体返回给前端

@RestController的作用有哪些
	1. 标识一个类是控制器类
	2. 将函数返回值作为响应主体响应给前端
	3. 将对应的类初始化成bean对象存储在ioc容器中

​ 通常在controller层定义的方法和@RequestMapping被称为后端接口。

​ 在控制层还有一个日志输出较为常用的注解:@Slf4j 可以创建一个log对象,通过log.info("")代替sout的方式输出日志。

下面是ppt里一些对上面的注解的一些解释,上面是我的理解

@RestController
 1. Spring MVC 提供的注解,用于处理 HTTP 请求并返回数据,常用于前后端分离项目中为前端(如 
Vue/React/移动端)提供数据接口。
2. 用于构建RESTful API,自动将返回值序列化为JSON/XML并作为HTTP响应体返回。
3. 简化了RESTful服务开发,无需在每个方法上添加@ResponseBody。
单体项目:
	单体项目是将所有功能模块(包括前端、后端、数据库访问等)集成在一个统一的应用程序中开发的传统架构,适合小型项目,但扩展性和维护性较差。 
前后端分离项目:
	前后端分离项目是将前端界面与后端服务独立开发、部署和维护的现代架构,具有高灵活性、可扩展性和多端适配能力,是当前主流的开发模式。
@Controller 是 Spring MVC 中用于处理 HTTP 请求并返回视图(如 HTML/JSP/Thymeleaf 
页面)的注解,常用于单体项目中构建服务端渲染的传统 Web 应用。

Controller层代码如下:

@Slf4j
//这个注解可以直接使用日志对象输出日志,不用再创建对象了
@RestController
@RequestMapping("depts")
public class DeptController {
    //创建一个日志记录对象
    //private static Logger log = LoggerFactory.getLogger(DeptController.class);

    @Autowired
    private DeptService deptService;

    @GetMapping
    public Result list(){
        log.info("查询全部部门数据");
        List<Dept> depts = deptService.selectDept();
        return Result.success(depts);
    }

    //删除
    @DeleteMapping("{id}")
    //@pathVariable注解可以将请求数据的参数弄到接口路径上
    public Result delete(@PathVariable Integer id){
        log.info("根据id删除部门信息"+id);
        int i = deptService.delete(id);
        return Result.success();
    }

    //新增部门
    @PostMapping
    //@RequestBody 可以将前端的json数据封装到对象中
    public Result add(@RequestBody Dept dept){
        //输出日志
        log.info("新增部门" + dept);
        deptService.add(dept);
        return Result.success();
    }

    //根据id查询
    @GetMapping("{id}")
    public Result getById(@PathVariable Integer id){
        Dept dept = deptService.selectDeptById(id);
        return Result.success(dept);
    }

    //更新数据
    @PutMapping
    public Result update(@RequestBody Dept dept){
        log.info("更新成功"+ dept);
        int i = deptService.update(dept);
        return Result.success();
    }
}
1. 控制层常用到的注解主要有:
  1. @Slf4j 主要是创建一个日志对象,可以代替sout输出日志。
    • log.info("增删改查")
  2. @RestController 主要作用是标识一个类是控制类,并且可以将函数返回内容作为响应主体返回给前端。
    • @ResponseBody+@Controller两个注解的作用之和
  3. @RequestMapping 主要作用是定义一个接口路径,前端可以通过这个路径地址发送请求,这个注解可以修饰类也可以修饰方法(修饰方法有可替代的代码),默认是get方法,但是也可以是其他请求接收方法,
    • 可以用@GetMapping,@PostMapping,@PutMapping,@DeleteMapping....平替简化了书写
  4. @RequestBody 主要作用是将前端请求的 Json数据封装到java对象中,给 java服务器使用, 如果前端发送的有时间对象的,同时在实体类中用@DateTimeFormat注解和@JsonFormat三个注解同时使用的时候,规范时间格式的输入输出都有@JsonFormat规定
  5. @RequestParam 主要作用是识别前端请求的集合泛型,和mybatis中@Param识别多个参数用法一样,但是其实可以用数组代替传入容器一样的变量(主要作用就是与前端请求参数名保持一致,起别名的作用)
    • value属性可以给参数设置别名(如果说前端请求参数名与后端方法参数名不一致,那么可以用这个注解给方法写一个别名)
    • required属性可以设置参数是否必须要传进来,true是必须,false是可以传空。
    • defaultValue属性可以给参数设置默认值
  6. @PathVariable 可以将请求路径变成动态可变的,路径用{参数}代表,通常用id表示。
  7. @DateTimeFormat注解是规定前端发送的时间格式,默认是”yyyy/MM/dd“,通常用“yyyy-MM-dd”来规范。
  8. @JsonFormat注解是将响应给前端的响应体里面的数据规范化,不过需要改变时区,用属性timezone="GMT+8"即可表示东八区的时间。

目前我对controller层的理解就是什么呢,tomcat服务器监听启动程序,监听并解析前端发送的请求,发送到controller层,调用service层的接口方法后随后在service层中调用mapper接口里面的方法,从数据库实体类中属性字段映射,一一对应到并返回出去,最后返回到controller层,最后由controller层将数据响应到响应体中

上面有提到 Json数据,我的理解就是用 {} 封装的数据,Json字符串主要就是使内容变得井然有序有序,下面是一个Json格式的输出格式,都是键值对的格式,前端请求数据可以是Json格式,也可以是其他形式,所以说后端服务器接收到 Json数据的话就需要用@RequestBody注解将数据封装到对象当中,后端服务器返回出去的对象也是Json数据

{
    "code": 1,
    "msg": "success",
    "data": {
        "id": 1,
        "username": "jinyong",
        "password": "123456",
        "name": "金庸",
        "gender": 1,
        "image": "1.jpg",
        "job": 4,
        "entrydate": "2000-01-01",
        "deptId": 2,
        "createTime": "2025-08-17",
        "updateTime": "2025-08-17"
    }
}
2. 工具类Result

主要是将要返回给前端的数据封装在Result对象里,将状态码code,信息msg,装有数据的Object变量data封装到Result对象里一起作为方法返回值返回给前端

下面是Result类

Result类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
    private Integer code;//响应码,1 代表成功; 0 代表失败
    private String msg;  //响应信息 描述字符串
    private Object data; //返回的数据

    //增删改 成功响应
    public static Result success(){
        return new Result(1,"success",null);
    }
    //查询 成功响应
    public static Result success(Object data){
        return new Result(1,"success",data);
    }
    //失败响应
    public static Result error(String msg){
        return new Result(0,msg,null);
    }
}

controller层返回数据

    @PostMapping
    public Result save(@RequestBody Emp emp){
        //日志输出
        log.info("插入成功" + emp);
        empService.save(emp);
        //返回的是Result的方法,将所有数据都封装到这个类里面了
        return Result.success();
    }

3. M层(Service,实体类,mapper)

  • service层主要就是负责逻辑代码的书写,供controller层的service对象调用里面的方法;

  • mapper接口其实就是mybatis里面的书写内容了,主要是由service层调用里面的方法

里面用到的注解就是@Service,@Mapper

下面的就是一个简单的项目的结构

image-20250823104512961

4. V层视图层

我的理解就是前端页面里面的内容,目前了解不多

在三层架构中,V 层(View 层,视图层) 主要负责用户界面的展示和用户交互的处理,是用户直接可见和操作的部分。其核心作用是将数据以友好的形式呈现给用户,并接收用户的输入(如点击、输入文本等操作)。

5. @RESTful风格

就是将controller层里面的资源路径简化书写,由原本每个接口都需要一个路径访问改为通过不同的请求方式访问到不同的资源,甚至可以和@PathVariable注解将接口访问路径动态变化,主要就是简介,高效,安全

普通风格

@GetMapping("/{userId}") 定义了一个URL模板,其中 {userId} 是一个路径变量。
@PathVariable 注解告诉Spring MVC从URI中提取 userId 部分的值,并将其作为方法的参数。
这样,当请求如 /users/123 这样的URL时, 123 就会被作为 userId 参数传递给方法。
示例:
http://127.0.0.1/item/queryItem?id=1 	查询,GET
http://127.0.0.1/item/saveItem 			新增,POST
http://127.0.0.1/item/updateItem 		更新,POST
http://127.0.0.1/item/deleteItem?id=1 	删除,GET或POST

修改成restful风格后

http://127.0.0.1/item/1 	查询,GET
http://127.0.0.1/item 		新增,POST
http://127.0.0.1/item 		更新,PUT
http://127.0.0.1/item/1 	删除,DELETE

6. 数据库连接池的理解

  • 数据库连接池的核心作用是管理数据库连接的生命周期,避免频繁创建和关闭连接带来的性能损耗, 连接池在初始化时会创建一定数量的数据库连接,这些连接处于 "空闲" 状态但保持与数据库的物理连接, SQL 执行完成后,连接不会被关闭,而是归还给连接池并标记为 "空闲" 状态,等待下一次被复用。这避免了 TCP 三次握手 / 四次挥手的开销,以及数据库创建连接的资源消耗(如进程 / 线程创建)。

    ​ 我的理解就是,原本mapper.xml文件里面写的sql语句需要不断的向数据库发送请求,每每完成一次sql语句的执行就会将连接断开,现在有了数据库连接池,初始化数据库连接池以后,他可以默认提供一定数量的端口连接着mysql数据库,mapper.xml文件里面发送请求的话会调用里面的连接,通过连接就可以直接与数据库建立联系,执行完毕后会把连接归还给数据库连接池供其他请求使用,加快了执行的速度

使用的方法主要是

  1. 先在pom.xml导入相关的依赖jar包

    !--
    依赖坐标以spring-boot开头,表示该依赖坐标由springboot官方提供
    依赖坐标不以spring-boot开头,而是以依赖名开头,表示由依赖包所在公司提供
    -->
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.16</version>
    </dependency>
    
  2. 在yaml配置文件里面配置对应的属性等

    # 数据库驱动
    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/sfzy
        username: root
        password: 123456
        type: com.alibaba.druid.pool.DruidDataSource
        druid: 
    
      servlet:
        multipart:
          #配置单个文件最大上传大小
          max-file-size: 10MB
          #配置单个请求最大上传大小,一个请求可以上传多个文件
          max-request-size: 100MB
      web:
        # 前端服务查找路径
        resources:
          static-locations: D:\java培训代码\web_project\springboot03\src\main\resources\static\
    # 文件上传
    file:
      upload:
        path: D:\java培训代码\web_project\springboot03\src\main\resources\static\
        path-image: D:\java培训代码\web_project\springboot03\src\main\resources\static\
        path-file: D:\java培训代码\web_project\springboot03\src\main\resources\static\
    
    mybatis:
      # 映射文件
      mapper-locations: classpath:mapper/*.xml
      # 包别名
      type-aliases-package: com.hualan.pojo
      configuration:
        # 配置日志
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
        # 让表字段(有下划线)与对象的属性(小驼峰)形成映射关系,否则会因为数据字段属性名不同导致映射关系失败
        map-underscore-to-camel-case: true
        # 进程死锁时候,设置sql超时时间,单位是秒(s)
        default-statement-timeout: 20
    
    # 分页插件
    pagehelper:
      reasonable: true
    
    server:
      port: 8080
    
image-20250823110640780 image-20250823110539826

7. 文件上传

文件上传是Web开发中常见的功能之一,它允许用户将文件(如图片、文档、视频等)从他们的设备传输到服务器。

可以在yaml配置文件里面设置一个上传的地址,通过创建一个对象@Value依赖注入获取到这个地址,在前端发送请求时,请求体是一个上传文件内容,用MulpartFile 这个类型接收文件,一般都在service层进行逻辑处理,用字符串的方式截取到他的后缀(lastIndexOf和substring截取到后缀名,随后用UUID的一个方法randomUUID获取到一个随机的序列,把这个序列拼接到文件名后缀的前面,最后通过上传的文件对象调用transforTo方法把上传到目的地址和文件名拼接到一起)

service层逻辑处理

    @Value("${file.upload.path-image}")
    private String rootPath;    

	@Override
				//用MultipartFile类型接收文件对象
    public void addConsultant(Consultant consultant, MultipartFile file) throws IOException {
        //获取到文件后缀	.jpg等多种
        String substring = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
        //获取UUID与文件后缀拼接
        String newFileName = UUID.randomUUID() + substring;
        //文件上传到指定路径
        file.transferTo(new File(rootPath+newFileName));
        //consultant.setImage("images/"+newFileName);
        consultantMapper.addConsultant(consultant);
    }

在yaml文件中写上传路径和服务器访问的静态资源路径(静态资源路径一定在前面写一个file)

# 数据库驱动
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/springbootdb03
    username: root
    password: 123456
  servlet:
    multipart:
      #配置单个文件最大上传大小
      max-file-size: 10MB
      #配置单个请求最大上传大小,一个请求可以上传多个文件
      max-request-size: 100MB
  web:
    # 静态资源路径,前端服务查找路径,文件保存路径
    resources:
      static-locations: file:D:\static\
# 文件上传
file:
  upload:
    path: D:\static\
    path-image: D:\static\images\
    path-file: D:\static\files\

8. 异常类

在Spring Boot项目中,通过特定的注解和类来实现对所有异常的集中处理,而不是在每个可能出现异常 的地方都使用try-catch语句,如果都用try-catch语句写的话会很繁琐。这种处理方式可以确保异常信息的格式一致,方便前端开发人员理解和处理

通过两个注解:@RestControllerAdvice+@ExceptionHandle实现全局异常处理;

//@RestControllerAdvice将当前类标识为异常处理的组件
@RestControllerAdvice
 public class ExceptionController {
 	//@ExceptionHandler用于设置所标识方法处理的异常
	//@ExceptionHandler(ArithmeticException.class)
 	@ExceptionHandler(Exception.class)
 	//e表示当前请求处理中出现的异常对象
	public R handleException(Exception e){
 		System.out.println(e);
 		return R.error("服务器异常,请稍后再试!");
 	}
 }

@RestControllerAdvice 注解默认会作为全局的异常处理,所有的Controller中出现异常都会自动调用这里的方法去处理异常

@ExceptionHandler注解是标注处理的异常类型,用exception.class即可。

9. 密码加密

​ hash算法里面的md5算法加密(可以生成32位的和16位的),就是让密码的字符串生成对应的16位或32位哈希值,然后在数据库里面存储对应的hash值,用户输入密码时候,验证这个密码的hash值与查询到的数据库里的hash值是否一致,一样的话就登陆成功了。一般都写在service层

package com.hualan.utils;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * MD5加密工具类
 */
public class MD5Util {

    /**
     * 针对明文字符串执行MD5加密
     * @param source
     * @return
     */
    public static String encode(String source) {

        // 1.判断明文字符串是否有效
        if (source == null || "".equals(source)) {
            throw new RuntimeException("用于加密的明文不可为空");
        }

        // 2.声明算法名称
        String algorithm = "md5";

        // 3.获取MessageDigest对象
        MessageDigest messageDigest = null;
        try {
            messageDigest = MessageDigest.getInstance(algorithm);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        // 4.获取明文字符串对应的字节数组
        byte[] input = source.getBytes();

        // 5.执行加密
        byte[] output = messageDigest.digest(input);

        // 6.创建BigInteger对象
        int signum = 1;
        BigInteger bigInteger = new BigInteger(signum, output);

        // 7.按照16进制将bigInteger的值转换为字符串
        int radix = 16;
        String encoded = bigInteger.toString(radix).toUpperCase();

        return encoded;
    }

    public static void main(String[] args) {
        System.out.println(MD5Util.encode("123456"));
    }
}

下面的是在service层进行验证的代码

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public R login(String account, String password) {
        if (Objects.isNull(account) || account.equals("")) {
            return R.ok("账号不为空");
        }
        if (Objects.isNull(password) || password.equals("")) {
            return R.ok("密码不能为空");
        }
        //根据账号查找信息
        User user = userMapper.selectByAccount(account);
        if (Objects.isNull(user)) {
            return R.ok("账号不存在");
        }
        String encode = MD5Util.encode(password + account);
        System.out.println(encode);
        if(!user.getPassword().equals(encode)){
            return R.ok("密码错误");
        }

        return R.ok(user);
    }
}
	spring Boot 是 Pivotal 团队推出的一个框架,目标是让 Spring 应用的开发变得又快又简单。它通过自动配置和约定大于配置的思想,让开发者不用写一堆繁琐的配置,可以快速搭建出生产级的应用,甚至几行代码就能写出一个 HTTP 接口。可以理解为 Spring + Spring MVC 的“开箱即用”版本,而且它还内置了 Tomcat 服务器,你的应用可以直接跑起来并通过 HTTP 提供服务,不用额外部署。
	Spring Boot 内嵌了 Tomcat 服务器,因此在启动时无需额外部署,应用可直接通过内置的 Tomcat 监
听 HTTP 请求,快速提供 Web 服务。

增删改查练习心得:

1、就是在进行动态sql增删改查时候,采用动态sql添加数据如下所示,需要注意的有几点:

  • 不动态插入和动态插入时,如果数据库字段有默认值,不动态插入会用null替代原本的默认值,动态sql插入就不会被null替换;
  • 下面的代码还用到了sql片段,将字段写在sql标签里面,然后可以和if条件判断一起使用,可以灵活控制sql语句,通过include标签导入sql映射文件。
  • 可以用trim标签来消除里面的多余的前缀后缀字符,suffixOverrides去除后缀,prefixOverrides去除前缀
  • 用#{}防止发生sql注入
    <sql id="fields">
        <trim suffixOverrides="," prefixOverrides=",">
            <if test="name != null and name != ''">name,</if>
            <if test="age != null">age,</if>
            <if test="image != null and image != ''">image,</if>
            <if test="levelId != null">level_id,</if>
            <if test="introduction != null and introduction != ''">introduction,</if>
            <if test="isCertified != null">isCertified</if>
        </trim>
    </sql>

    <sql id="values">
        <trim suffixOverrides="," prefixOverrides=",">
            <if test="name != null and name != ''">#{name},</if>
            <if test="age != null">#{age},</if>
            <if test="image != null and image != ''">#{image},</if>
            <if test="levelId != null">#{levelId},</if>
            <if test="introduction != null and introduction != ''">#{introduction},</if>
            <if test="isCertified != null">#{isCertified}</if>
        </trim>
    </sql>
    <insert id="addConsultant" parameterType="consultant">
        insert into consultant (<include refid="fields"/>) values(<include refid="values"/>)
    </insert>

2、PageHelper插件的使用,简化了分页操作;

  • PageHelper.startPage(pageNum,pageSize)里面的参数分别代表的是当前页码数和每页总记录数;

  • 通过分页拦截

	@Autowired
    private ConsultantMapper consultantMapper;

   public PageBean selectByNameAgeLevel(Integer startPage, Integer pageSize, String name, Integer minAge,Integer maxAge, Integer levelId) {
        //分页拦截
        PageHelper.startPage(PageNum,pageSize);
        List<Consultant> consultants = consultantMapper.selectByNameAgeLevel(name,minAge,maxAge,levelId);
       //将查询到的集合数据用强转封装到Page集合里面,随后利用PageBean自定义类获取到数据总量和所有的数据内容
        Page<Consultant> pages = (Page<Consultant>) consultants;
        PageBean pagesBean = new PageBean(pages.getTotal(),pages.getResult());
        return pagesBean;
    }

PgaeBean实体类的创建

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageBean {
    private Long total;
    private List<Consultant> data;
}

3、

**统计功能**(20分)

- 按咨询师统计其开设的课程数量、总学习次数、平均评分

- 返回咨询师信息和对应的统计结果

按照咨询师进行统计所以说用到了分组group by并利用聚合函数查询平均值,总数等内容,映射出去的结果通过创建一个实体类接收到

    <resultMap id="map" type="PageBean">
        <result property="avg" column="avg"></result>
        <result property="studyCount" column="study_count"></result>
        <result property="count" column="total"></result>
    </resultMap>
    <select id="getByConsultant" resultMap="map">
        SELECT AVG(avg_rating) avg, COUNT(study_count) study_count, COUNT(*) total from courses GROUP BY consultant_id
    </select>

创建对应的实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageBean {
    private Double avg;				//平均值
    private Double studyCount;		//学习总数
    private Double count;			//平均评分
}

通过手动映射的方式将查询到的数据封装到对象的属性中去,最后将这个对象返回即可。

posted @ 2025-08-18 22:23  柠檬糖沉没  阅读(38)  评论(0)    收藏  举报