Lecture07-请求-实验06-案例

文章目录

    [toc]

    单词词性音标含义
    Sameadj./seɪm/相同的(常用于“Same Origin Policy”,指同源策略,限制不同源的文档或脚本交互)
    Originn./ˈɒrɪdʒɪn/源(指URL的协议、域名、端口组合,同源即这三者完全一致;Origin请求头标识请求来源)
    Policyn./ˈpɒləsi/策略(如“Same Origin Policy”即同源策略,是浏览器的安全机制;也指配置策略)
    Crossadj./krɒs/跨域的(指不同源的交互,如“Cross-Origin”即跨域,常涉及跨域资源共享问题)
    resourcen./ˈriːsɔːs/资源(指网络中的文件、数据等,如“Cross-Origin Resource Sharing”中的资源)
    sharingn./ˈʃeərɪŋ/共享(“CORS”即“Cross-Origin Resource Sharing”,指跨域资源共享机制,允许跨域请求)
    Allowv./əˈlaʊ/允许(CORS响应头Access-Control-Allow-*用于指定允许的跨域操作,如允许的源、方法等)
    Credentialsn./krəˈdenʃlz/凭据(指Cookie、HTTP认证信息等;CORS中withCredentials控制是否发送凭据,需服务端允许)
    registryn./ˈredʒɪstri/注册中心(存储和管理注册信息的组件,如服务注册中心、Bean定义注册中心等)
    Configurern./kənˈfɪɡərə(r)/配置器(用于配置组件的类,如Spring中的WebMvcConfigurer用于配置MVC相关功能)

    总目标:单表增删改查

    一、物理建模

    # 创建数据库
    create database db_demo_crud CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    use db_demo_crud;
    
    # 创建数据库表
    create table t_tiger(
    	tiger_id int auto_increment primary key,
        tiger_name char(100),
        tiger_age int,
        tiger_salary double
    );
    
    # 插入测试数据
    INSERT INTO t_tiger (tiger_name, tiger_age, tiger_salary)
    VALUES ('王虎', 5, 500.00),
           ('李虎', 3, 800.50),
           ('张虎', 7, 200.75),
           ('赵虎', 4, 200.25),
           ('刘虎', 6, 900.00);
    

    image-20250825161837185


    二、后端工程

    1、创建工程

    ①pom.xml

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.12</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <dependencies>
        <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>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
    
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
    
        <!-- 根据需要加入 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
    

    ②主启动类

    image-20250529112047149

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

    ③创建主配置文件

    image-20250529112918327

    spring.application.name=module33-spring-mvc-demo
    spring.datasource.username=atguigu
    spring.datasource.password=atguigu
    spring.datasource.url=jdbc:mysql://192.168.100.100/db_demo_crud
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    

    ④逻辑建模

    image-20250529112236730

    @Data
    public class Tiger {
    
        private Integer tigerId;
        private String tigerName;
        private Integer tigerAge;
        private Double tigerSalary;
    }
    

    ⑤创建组件

    image-20250529112722522


    ⑥加入Result类

    package com.atguigu.demo.entity;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    /**
     * 我们封装 Result 这个类,让后端所有响应数据都按照这个格式返回
     * 为什么能涵盖所有响应结果的数据结构?
     * 因为响应无非是成功和失败:
     *      失败:不管增删改查什么操作,都返回 code 和 message
     *      成功:
     *          增删改成功,返回 code 和 message(OK)
     *          查询成功,返回 code 和 message(OK)还有数据
     * 友情提示:这个类不需要自己写,用的时候可以复制粘贴,理解就行
     * 泛型参数:指定当前响应结果要返回给前端的具体数据类型
     * @param <T>
     *
     * code 属性:使用编码的方式来精确定义返回结果是什么情况
     *      注意:这个 code 不是 HTTP 响应状态码,而是根据业务情况定义的『结果码』
     *      举例:登录失败的结果码是 8800;注册时指定的用户名已存在结果码是 8531;订单超时未支付被取消结果码是 9800
     *      核心关键特点:结果码是团队内部人为约定的,是和业务相关的一个编码,不是语法层面的编码
     *      区分语法错误和业务错误(逻辑错误),以登录失败为例:
     *          HTTP 响应状态码 200,语法层面处理请求成功
     *          结果码 8800,逻辑层面用户登录并没有成功
     * message 属性:当前请求处理结果的说明信息
     * data 属性:要返回给前端的查询结果
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Result<T> {
    
        public static final String RESULT_SUCCESS = "SUCCESS";
        public static final Integer CODE_SUCCESS = 10000;
    
        private Integer code;
        private String message = RESULT_SUCCESS;
        private T data;
    
        // 增删改操作(没有查询结果)成功
        public static Result success() {
            // 创建当前场景下 Result 对象返回
            return new Result<>(CODE_SUCCESS, RESULT_SUCCESS, null);
        }
    
        // 查询操作成功
        public static <QueryData> Result<QueryData> success(QueryData data) {
            // 创建当前场景下 Result 对象返回
            return new Result<>(CODE_SUCCESS, RESULT_SUCCESS, data);
        }
    
        // 任何一种操作失败
        public static Result failed(String message) {
            return new Result(55555, message, null);
        }
    }
    
    

    2、测试数据库连接

    ①创建测试类

    image-20250529113149277

    @SpringBootTest
    public class DemoTest {
    }
    

    需要注意以下三点:

    • 引入spring-boot-starter-test依赖
    • 测试类所在的包,必须是主启动类所在包或子包
    • 测试类上标记@SpringBootTest注解

    使用过程中,需要哪个bean,自动装配进来即可


    ②测试数据库连接

    @SpringBootTest
    public class DemoTest {
    
        @Autowired
        private DataSource dataSource;
    
        @Test
        public void test01Conn() throws SQLException {
            Connection connection = dataSource.getConnection();
            System.out.println("connection = " + connection);
        }
    
    }
    

    3、各个功能URL地址设置

    功能URL地址请求方式
    查询列表/tiger/listGET
    删除记录/tiger/{tigerId}DELETE
    新增记录(通过请求参数接收)/tiger?tigerName=xxx&tigerAge=xxx&tigerSalary=xxxGET
    查询单个对象/tiger/{tigerId}GET
    更新记录/tigerPUT

    这里为了练习“通过请求参数方式接收数据”新增记录没有遵循 REST 风格要求


    4、业务:返回数据列表

    ①需求

    没有查询条件,不考虑分页,给前端返回全部数据组成的 List 集合


    ②代码

    [1]Controller方法

        @Autowired
        private TigerService tigerService;
    
        @GetMapping("/tiger/list")
        public Result<List<Tiger>> showList() {
            try {
                List<Tiger> tigerList = tigerService.getList();
                return Result.ok(tigerList);
            } catch (Exception e) {
                return Result.failed(500, e.getMessage());
            }
        }
    

    [2]Service方法

        @Override
        public List<Tiger> getList() {
            return tigerDao.selectList();
        }
    

    [3]Dao方法

    @Override
    public List<Tiger> selectList() {
        String sql = "select tiger_id tigerId, tiger_name tigerName, tiger_age tigerAge, tiger_salary tigerSalary from t_tiger";
    
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Tiger.class));
    }
    

    ③测试

    image-20250529113149277

        @Autowired
        private TigerService tigerService;
    
        @Test
        public void test02GetList() {
            List<Tiger> tigerList = tigerService.getList();
            for (Tiger tiger : tigerList) {
                System.out.println("tiger = " + tiger);
            }
        }
    

    注:Controller方法通过浏览器测试


    5、业务:删除记录

    ①需求

    根据id执行删除

    在数据库中执行delete from ... SQL语句是物理删除

    实际项目中更多采用逻辑删除:

    update xxx set del_status=0 from where id=?


    ②代码

    [1]Controller方法

    @DeleteMapping("/tiger/{tigerId}")
    public Result<Void> doRemove(@PathVariable("tigerId") Integer tigerId) {
        try {
            tigerService.removeById(tigerId);
            return Result.ok();
        } catch (Exception e) {
            return Result.failed(500, e.getMessage());
        }
    }
    

    [2]Service方法

        @Override
        public void removeById(Integer tigerId) {
            tigerDao.deleteById(tigerId);
        }
    

    [3]Dao方法

        @Override
        public void deleteById(Integer tigerId) {
            String sql = "delete from t_tiger where tiger_id=?";
            jdbcTemplate.update(sql, tigerId);
        }
    

    6、业务:新增记录

    ①需求

    前端以请求参数的方式发送数据,后端也是基于请求参数来接收


    ②代码

    [1]Controller方法

        @GetMapping("/tiger")
        public Result<Void> saveTiger(Tiger tiger) {
    
            try {
                tigerService.saveTiger(tiger);
    
                return Result.ok();
            } catch (Exception e) {
                return Result.failed(DemoConstant.GLOBAL_ERROR_CODE, e.getMessage());
            }
        }
    

    [2]Service方法

    @Override
    public void saveTiger(Tiger tiger) {
        tigerDao.insertTiger(tiger);
    }
    

    [3]Dao方法

    @Override
    public void insertTiger(Tiger tiger) {
        String sql = """
                insert into t_tiger(tiger_name,
                                  tiger_age,
                                  tiger_salary) values(?, ?, ?)
                """;
        jdbcTemplate.update(sql, tiger.getTigerName(), tiger.getTigerAge(), tiger.getTigerSalary());
    }
    

    7、业务:查询单个对象

    ①需求

    根据前端提供的id,查询单个Tiger对象返回给前端

    ②代码

    [1]Controller方法

    @GetMapping("/tiger/{tigerId}")
    public Result<Tiger> getTigerById(@PathVariable("tigerId") String tigerId) {
        try {
            Tiger tiger = tigerService.getTigerById(tigerId);
            return Result.ok(tiger);
        } catch (Exception e) {
            return Result.failed(500, e.getMessage());
        }
    }
    

    [2]Service方法

        @Override
        public Tiger getTigerById(String tigerId) {
            return tigerDao.selectTigerById(tigerId);
        }
    

    [3]Dao方法

    @Override
    public Tiger selectTigerById(String tigerId) {
        String sql = """
                select tiger_id tigerId, tiger_name tigerName, tiger_age tigerAge, tiger_salary tigerSalary 
                from t_tiger
                where tiger_id=?
                """;
        return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Tiger.class), tigerId);
    }
    

    8、业务:执行更新

    ①需求

    前端提交一个Tiger对象,后端根据这个Tiger对象执行更新


    ②代码

    [1]Controller方法

        @PutMapping("/tiger")
        public Result<Void> doUpdate(@RequestBody Tiger tiger) {
            try {
                tigerService.updateTiger(tiger);
                return Result.ok();
            } catch (Exception e) {
                return Result.failed(500, e.getMessage());
            }
        }
    

    [2]Service方法

        @Override
        public void updateTiger(Tiger tiger) {
            tigerDao.updateTiger(tiger);
        }
    

    [3]Dao方法

        @Override
        public void updateTiger(Tiger tiger) {
            String sql = "update t_tiger set tiger_name=?, tiger_age=?, tiger_salary=? where tiger_id=?";
            jdbcTemplate.update(sql,
                    tiger.getTigerName(),
                    tiger.getTigerAge(),
                    tiger.getTigerSalary(),
                    tiger.getTigerId());
        }
    
    end