Spring Boot+Mybatis实现增删改查接口开发+测试(超详细建议收藏)

news2024/11/24 11:41:02

前言

Java也是测试必知必会的内容,特别是现在类似spring boot 等Java框架更是成为主流。之前实现的图书增删改查是用Python实现的,没看过的请移步:Flask+mysql 实现增删改查接口开发+测试(图文教程附源码),本次给大家带来用Java实现的后端接口版本,并根据之前的项目总结有做一些优化

一、接口回顾

Python版本增删改查成功示例

1.1 图书增加

请求示例:

{
    "title":"《Flask框架》",
    "author":"pycharm",
    "read_status":1
}

响应示例: 

{
    "message": "图书添加成功!",
    "status": "success"
}

完整截图:

1.2 图书删除

请求示例:

{
    "id":"49"
}

响应示例: 

{
    "message": "图书被删除!",
    "status": "success"
}

完整截图:

1.3 图书修改

请求示例:

{
    "id": 70,
    "title": "《默8888888888》",
    "author": "陈玉名",
    "read_status": "1"
}

响应示例: 

{
    "message": "图书已更新!",
    "status": "success"
}

完整截图:

1.4 图书查询

请求示例:

{
    "id":"55"
}

响应示例: 

{
    "data": [
        {
            "author": "罗贯中",
            "id": 55,
            "read_status": true,
            "title": "《三国演义》"
        }
    ],
    "message": "图书查询成功!",
    "status": "success"
}

完整截图:

1.5 分页查询

请求示例:

{
    "page": 1,
    "pagesize": 5
}

响应示例: 

{
    "data": [
        {
            "author": "罗贯中",
            "id": 50,
            "read_status": true,
            "title": "《三国演义》"
        },
        {
            "author": "罗贯中",
            "id": 51,
            "read_status": true,
            "title": "《三国演义》"
        },
        {
            "author": "罗贯中",
            "id": 52,
            "read_status": true,
            "title": "《三国演义》"
        },
        {
            "author": "罗贯中",
            "id": 53,
            "read_status": true,
            "title": "《三国演义》"
        },
        {
            "author": "罗贯中",
            "id": 54,
            "read_status": true,
            "title": "《三国演义》"
        }
    ],
    "message": "图书查询成功!",
    "page": 1,
    "pagesize": 5,
    "status": "success",
    "total": 22
}

完整截图:

 上面我们对相关接口的请求入参,响应复习了一遍,注意请求结构、字段类型、请求方法、请求地址、接口响应格式、接口响应字段类型

下面我们就要用spring boot分层开发实现以上几个接口,把查询和分页查询合并一个接口,这也是一个优化点

二、技术准备

之前博客有简单使用springboot、Mybatis等的文章,本篇文章再次讲述下相关知识

2.1 spring boot

Spring Boot 是一个用于快速开发应用程序的框架,它建立在 Spring 框架的基础之上,旨在简化配置和部署过程,使开发者能够更专注于业务逻辑的实现。

以下是 Spring Boot 的一些主要特点:

  1. 简化配置: Spring Boot 提供了默认的配置选项,减少了开发者在配置方面的工作量。它使用约定大于配置的原则,根据项目的依赖和类型,自动配置应用程序的不同部分。

  2. 内嵌式 Web 服务器: Spring Boot 集成了多种内嵌式 Web 服务器,如Tomcat、Jetty和Undertow,使得构建和运行 Web 应用变得更加简单。

  3. 自动化依赖管理: Spring Boot 可以管理项目的依赖,自动选择合适的版本,简化了依赖管理的复杂性。

  4. 自动化配置: Spring Boot 根据项目的依赖和需要,自动进行一些常见配置,如数据源、消息队列等,减少了手动配置的步骤。

  5. 开箱即用的功能: Spring Boot 提供了许多常见功能的开箱即用实现,如安全认证、缓存、监控等,减少了开发者编写大量样板代码的工作。

  6. 简化部署: Spring Boot 支持将应用程序打包为可执行的 JAR 或 WAR 文件,以及支持 Docker 容器化部署,使部署变得更加方便。

  7. 微服务支持: Spring Boot 能够与 Spring Cloud 结合使用,为微服务架构提供支持,帮助开发者构建分布式系统。

总之,Spring Boot 旨在简化 Spring 应用程序的开发、测试、部署和运维过程,使开发者能够更快速、更高效地开发出高质量的应用程序。它适用于各种规模的项目,从小型应用到大型企业级系统。

2.2 Mybatis

MyBatis(之前称为iBATIS)是一个开源的持久层框架,用于将 Java 对象映射到关系型数据库中的数据。它提供了一种将 SQL 查询、更新和删除等操作与 Java 对象之间的映射关系的方式,从而使数据库操作更加简化和灵活。

以下是 MyBatis 的一些主要特点:

  1. 灵活的SQL映射: MyBatis 允许开发者编写原生的 SQL 查询,而不需要过多的 ORM(对象关系映射)层。这使开发者能够更精确地控制 SQL 查询的执行,并更好地优化数据库操作。

  2. 参数绑定: MyBatis 提供了参数绑定的功能,可以将 Java 对象中的属性值绑定到 SQL 查询中的参数位置,从而更方便地执行动态 SQL 查询。

  3. 结果映射: MyBatis 可以将查询结果映射为 Java 对象,开发者可以通过配置来指定查询结果与 Java 对象之间的映射关系。

  4. 支持存储过程和高级映射: MyBatis 支持存储过程调用和一些复杂的结果映射,使开发者能够处理更复杂的数据库操作。

  5. 缓存支持: MyBatis 提供了一些级别的缓存支持,可以提高查询的性能。

  6. 插件机制: MyBatis 允许开发者编写插件来扩展框架的功能,例如添加自定义的拦截器、执行器等。

  7. XML配置和注解: MyBatis 支持通过 XML 配置文件或者注解的方式来配置映射关系和查询。

  8. 与各种数据库兼容: MyBatis 支持多种数据库,并且不需要太多的配置就可以适应不同数据库的特性。

总之,MyBatis是一个轻量级的持久层框架,它通过提供简洁的方式来操作数据库,使开发者能够更加灵活和高效地进行数据库操作,而无需深入学习复杂的 ORM 框架。

2.3 Maven

Maven 是一个流行的项目构建和管理工具,用于帮助开发团队管理项目的构建、依赖管理和文档生成等任务。它提供了一种标准化的方式来管理项目的生命周期,从而简化了项目构建、发布和维护过程。

以下是 Maven 的一些主要特点和功能:

  1. 项目结构标准化: Maven 鼓励使用一种特定的项目结构,包括源代码目录、资源文件目录、测试目录等。这有助于团队成员更容易理解和协同开发项目。

  2. 依赖管理: Maven 使用一个称为 "坐标" 的标识符来唯一标识项目的依赖库。通过在项目的配置文件中声明依赖,Maven 可以自动下载和管理所需的库文件。

  3. 自动构建: Maven 使用 POM(项目对象模型)文件来描述项目的构建配置,包括源代码目录、依赖、插件等。开发者只需编写 POM 文件,Maven 就能自动执行构建任务。

  4. 插件体系: Maven 提供了丰富的插件来执行各种任务,例如编译、测试、打包、部署等。开发者可以通过配置插件来定制项目的构建过程。

  5. 生命周期和阶段: Maven 将项目的构建过程划分为生命周期(Lifecycle),每个生命周期又分为不同的阶段(Phase)。例如,项目的生命周期包括 validate、compile、test、package、install、deploy 等阶段。

  6. 仓库管理: Maven 使用中央仓库作为默认的依赖库,开发者可以将自己的构建产物发布到本地仓库或者远程仓库。

  7. 多模块支持: Maven 支持将一个大型项目划分为多个子模块,每个模块可以有自己的 POM 文件和构建配置,从而更好地管理复杂的项目结构。

  8. 文档生成: Maven 可以通过插件自动生成项目的文档,包括代码文档、用户文档等。

总之,Maven 是一个用于项目构建和管理的工具,它提供了一套规范和标准,使开发者能够更高效地进行项目的构建、依赖管理和发布等任务。

2.4 MySQL

MySQL 是一种开源的关系型数据库管理系统(RDBMS),它是一种广泛使用的数据库技术,用于存储和管理结构化数据。MySQL 最初由瑞典公司 MySQL AB 开发,后来被 Sun Microsystems 收购,再后来 Sun Microsystems 被 Oracle Corporation 收购,因此 MySQL 也被称为 Oracle MySQL。

以下是 MySQL 数据库的一些主要特点和功能:

  1. 关系型数据库: MySQL 是一种关系型数据库,数据以表格的形式存储,表格中的数据之间通过键值关系相互连接。

  2. 开源和免费: MySQL 是开源软件,因此可以免费使用和分发。开源性质使得它受到广泛的社区支持和贡献。

  3. 跨平台支持: MySQL 可以在多种操作系统上运行,包括 Windows、Linux、macOS 等。

  4. 性能优化: MySQL 在读写速度上具有优异的性能,特别是对于高并发读取请求和大规模数据操作。

  5. 数据安全性: MySQL 提供了数据的访问控制、权限管理以及数据加密等功能,以确保数据的安全性和保密性。

  6. 事务支持: MySQL 支持事务,可以保证在一系列操作中的原子性、一致性、隔离性和持久性(ACID 属性)。

  7. 存储引擎: MySQL 支持多种存储引擎,如 InnoDB、MyISAM 等,不同的存储引擎提供了不同的性能和特性。

  8. SQL 支持: MySQL 使用结构化查询语言(SQL)作为与数据库进行交互的标准语言。

  9. 复制和集群: MySQL 提供了复制和集群功能,允许在多台服务器之间同步数据,实现高可用性和负载均衡。

  10. 备份和恢复: MySQL 支持数据备份和恢复,使用户能够定期保存数据以应对意外数据丢失。

总之,MySQL 是一款功能强大的开源关系型数据库管理系统,适用于各种规模的应用,从个人项目到大型企业应用都可以使用它来存储和管理数据。

三、工具准备

上面是理论,这一篇我们动手前检查一下环境

3.1 IDEA

首先装IDEA前,确保你本地有安装java环境,本篇才用jdk 17,安装教程有很多此处不再介绍

我用的是2020的pojie版本

3.2 Navicat

确保电脑安装了mysql,或者其他机器上。Navicat安装也简单此处也不再赘述

主要是为了验证数据方便

这里为了和Python版本区分,我另起了一个book表,之前的是books,并把read_status设计为int。在实际工作中也是这样,有多个枚举值的定义为int,后期好拓展

重新设计的表DDL如下:

CREATE TABLE `book` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自动递增id,唯一键',
  `title` varchar(80) NOT NULL COMMENT '书名',
  `author` varchar(120) NOT NULL COMMENT '作者',
  `read_status` int(11) DEFAULT NULL COMMENT '阅读状态,0未读,1已读',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=73 DEFAULT CHARSET=utf8;

四、项目工程结构

4.1 项目目录结构

 

4.2 项目目录文件解析

4.2.1 config

下有2个类:

DataSourceConfig.java、MyBatisConfig.java这2个是Mybatis调试过程中禁用了Spring Boot默认的数据源自动配置 学习的文件,暂时不用管。

4.2.2 controller

下有1个类:BookController ,用来处理前端请求的,前端或者postman请求首先会到达这一层,也叫控制层

4.2.3 domain

下有3个类:

BaseInput:封装请求用的,实际项目会用上,此处没时间封装请求

Book:最主要的实体类或者模型类,字段和setter、getter方法都在这里,注意,实际项目会采用@Data注解等去掉setter、getter方法

ResponseData :封装响应类,实际项目也会有

4.2.4 mapper

包下有1个类:BookMapper

持久层,主要与数据库进行交互。也叫DAO层,现在用 Mybatis 逆向工程生成的 mapper 层,其实就是 DAO 层。DAO 层会调用entity(domain)层,DAO 中会定义实际使用到的方法,比如增删改查

4.2.5 service

包下有1个类 BookService
,1个包impl,包下有服务实现类 BookServiceImpl、自定义异常类BookNotFoundException

4.2.6 启动类

和上面 包平级的BookApplication类,是spring boot项目默认生成的启动类

4.2.7 resources下的mapper

有1个xml文件BookMapper.xml和mapper(DAO)层交互,主要写的是SQL语句

 4.2.8 resources 根目录下的配置文件

application.properties文件或者application.yml文件。都可以,但是要注意2种文件的配置格式。这里采用的是yml文件,即application.yml

 4.2.9 依赖文件

pom.xml,存放一些依赖,可以在这里引入依赖,更改依赖

一定要注意目录结构,否则会有各种报错。

关于spring boot各层的详细解析参考我的博文:Spring Boot 各层作用与联系

五、增删改查接口开发

前端请求过来遵循controller->mapper->service->entity我们开始写代码

5.1 图书增加接口

controller.java

@RequestMapping("/api/book")
public class BookController {
    private static final Logger logger = LoggerFactory.getLogger(BookController.class);

    private final BookService bookService;

    @Autowired
    public BookController(BookService bookService){
        this.bookService = bookService;
    }

    @PostMapping("/add")
    public ResponseEntity<Map<String, String>> addBook(@RequestBody Book book) {
        try {
            //System.out.println("前端传值ReadStatus:"+book.getReadStatus());
            bookService.insertData(book);
            Map<String, String> response = new HashMap<>();
            response.put("message", "图书添加成功!");
            response.put("status", "success");
            return ResponseEntity.ok(response);
        } catch (IllegalArgumentException e) {
            Map<String, String> response = new HashMap<>();
            response.put("message", e.getMessage());
            response.put("status", "fail");
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
     }
    }

BookService.java

void insertData(Book book);

BookServiceImpl.java

    @Override
    public void insertData(Book book) {
        if (book.getTitle() == null || book.getAuthor() == null || book.getReadStatus() == null) {
            throw new IllegalArgumentException("title、author和read_status是必传参数!");
        }

    // Check for duplicate title
        String title = book.getTitle().trim();
        if (bookMapper.existsByTitle(title)) {
            throw new IllegalArgumentException("书名(title)重复!");
        }

        bookMapper.insertData(book);
}

BookMapper.java

    void insertData(Book book);

BookMapper.xml

    <!-- 增加 -->
    <insert id="insertData">
        INSERT INTO book (title, author, read_status)
        VALUES (#{title}, #{author}, #{readStatus})
    </insert>

5.2 图书删除接口

controller.java

    public ResponseEntity<Map<String, String>> deleteById(@RequestBody Map<String, Object> requestParams) {
        Map<String, Object> data = (Map<String, Object>) requestParams.get("data");
        Long id = Long.valueOf(data.get("id").toString());
        // 构建返回结果
        boolean isDeleted = bookService.deleteById(id);

        if (isDeleted) {
            Map<String, String> successResponse = new HashMap<>();
            successResponse.put("message", "图书被删除!");
            successResponse.put("status", "success");
            return ResponseEntity.ok(successResponse);
        } else {
            Map<String, String> failResponse = new HashMap<>();
            failResponse.put("message", "需要删除的图书不存在!");
            failResponse.put("status", "fail");
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(failResponse);
        }
    }

BookService.java

  boolean deleteById(Long id);
  boolean existsByTitle(@Param("title") String title);

BookServiceImpl.java

    @Override
    public boolean deleteById(Long id) {
        if (id == null || id <= 0) {
            throw new IllegalArgumentException("无效的图书 ID");
        }

        int rowsAffected = bookMapper.deleteById(id);
        return rowsAffected > 0;
    }

BookMapper.java

int deleteById(@Param("id") Long id);

BookMapper.xml

    <!-- 判断是否存在指定 title 的记录 -->
    <select id="existsByTitle" resultType="boolean" parameterType="java.lang.String">
        SELECT COUNT(*) > 0
        FROM book
        WHERE title = #{title}
    </select>
    <!-- 删除 -->
    <delete id="deleteById" parameterType="long">
        DELETE FROM book WHERE id = #{id}
    </delete>

5.3 图书修改接口

controller.java

    @PostMapping("/update")
    public ResponseEntity<ResponseData> updateBook(@RequestBody Book book) {
        try {
            bookService.updateData(book);
            ResponseData response = new ResponseData("success", "图书已更新!");
            return ResponseEntity.ok(response);
        } catch (IllegalArgumentException e) {
            ResponseData response = new ResponseData("fail", e.getMessage());
            return ResponseEntity.badRequest().body(response);
        } catch (BookNotFoundException e) {
            ResponseData response = new ResponseData("fail", "需要修改的书籍id不存在!");
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
     }
    }

BookService.java

    void updateData(Book book);

BookServiceImpl.java

    @Override
    public void updateData(Book book) {
        if (book.getId() == null || book.getTitle() == null || book.getAuthor() == null || book.getReadStatus() == null) {
            throw new IllegalArgumentException("id、title、author和read_status是必传参数!");
        }

        List<Book> bookList = bookMapper.selectAll();
        List<Integer> idList = bookList.stream().map(Book::getId).collect(Collectors.toList());

        if (!idList.contains(book.getId())) {
            throw new IllegalArgumentException("需要修改的书籍id不存在!");
        }

        String title = book.getTitle().trim();
        String author = book.getAuthor().trim();
        int readStatus = book.getReadStatus();

        if (title.isEmpty()) {
            throw new IllegalArgumentException("title不能为空!");
        }
        if (author.isEmpty()) {
            throw new IllegalArgumentException("作者不能为空!");
        }
        if (readStatus != 0 && readStatus != 1) {
            throw new IllegalArgumentException("阅读状态只能为0和1!");
        }

        bookMapper.updateData(book.getId().longValue(), title, author, readStatus);
    }

BookMapper.java

    void updateData(@Param("id") Long id, @Param("title") String title, @Param("author") String author, @Param("readStatus") Integer readStatus);

BookMapper.xml

    <!-- 修改 -->
    <update id="updateData">
        UPDATE book SET
        title = #{title},
        author = #{author},
        read_status = #{readStatus}
        WHERE id = #{id}
    </update>

5.4 图书查询接口

controller.java

    @PostMapping("/query")
    public ResponseData<List<Book>> queryBookByTitle(@RequestBody Map<String, Object> requestParams) {

        Map<String, Object> data = (Map<String, Object>) requestParams.get("data");

        String title = (String) data.get("title");
        int pagesize = data.get("pagesize") != null && !data.get("pagesize").toString().isEmpty() ? Integer.parseInt(data.get("pagesize").toString()) : 3;
        int page = data.get("page") != null && !data.get("page").toString().isEmpty() ? Integer.parseInt(data.get("page").toString()) : 1;
        List<Book> result = bookService.queryBookByTitle(title, pagesize, page);
        return new ResponseData<>("请求成功", "success", result);
    }

BookService.java

    List<Book> queryBookByTitle(String title, Integer pagesize, Integer page);

BookServiceImpl.java

    @Override
    public List<Book> queryBookByTitle(String title, Integer pagesize, Integer page) {
        int offset = pagesize * (page - 1);
        RowBounds rowBounds = new RowBounds(offset, pagesize);
        return bookMapper.queryBookByTitle(title, rowBounds);
    }

BookMapper.java

    List<Book> queryBookByTitle(@Param("title") String title, RowBounds rowBounds);

    List<Book> selectAll();

BookMapper.xml


    <!-- 查询 -->
    <select id="queryBookByTitle" resultType="com.example.book.domain.Book">
        SELECT id,title,author,read_status readStatus FROM book
        <where>
            <if test="title != null and title != ''">
                AND title LIKE CONCAT('%', #{title}, '%')
            </if>
        </where>
    </select>
    <!-- 查询所有图书 -->
    <select id="selectAll" resultType="com.example.book.domain.Book">
    SELECT id, title, author, read_status as readStatus
    FROM books

六、增删改查接口测试

CRUD,增删改查接口已经开发完毕,下面我们开始测试。注意,这里只列了部分场景。入参也做了校验,这里就不列截图了

6.1 图书增加测试

请求示例:

{
    "title":"《Spring高级编程》",
    "author":"李专家",
    "read_status":1
}

响应示例: 

{
    "message": "图书添加成功!",
    "status": "success"
}

完整截图:

数据库验证:

再次点击,报错:书名重复

 新增接口测试通过

6.2 图书删除测试

准备删除id为 67的书籍

请求示例:

{
	"data": {
		"id": "67"
	},
	"extra": {}
}

响应示例: 

{
    "message": "图书被删除!",
    "status": "success"
}

完整截图:

数据库验证:

 再次点击,报错:id不存在

 删除接口测试通过

6.3 图书修改测试

修改id 为68的书籍 

请求示例:

{
    "id": 68,
    "title": "《我是测试开发》",
    "author": "李同学",
    "read_status": "0"
}

响应示例: 

data是拓展字段,这里返回null没影响。前端页面暂时不用

{
    "message": "success",
    "status": "图书已更新!",
    "data": null
}

完整截图:

数据库验证:

再次修改书名 《我是测试》,点击修改返回成功

 

修改接口测试通过

6.4 图书查询测试

请求示例:

{
	"data": {
		"title": "",
		"pagesize": "10",
		"page": "1"
	},
	"extra": {}
}

响应示例:

{
    "message": "请求成功",
    "status": "success",
    "data": [
        {
            "id": 68,
            "title": "《我是测试》",
            "author": "李同学",
            "read_status": 0
        },
        {
            "id": 69,
            "title": "《三国演义20》",
            "author": "罗贯中",
            "read_status": 1
        },
        {
            "id": 70,
            "title": "《默》",
            "author": "李晓明",
            "read_status": 0
        },
        {
            "id": 71,
            "title": "《三国演义8》",
            "author": "罗贯中8",
            "read_status": 1
        },
        {
            "id": 72,
            "title": "《Java高级编程》",
            "author": "李专家",
            "read_status": 1
        },
        {
            "id": 73,
            "title": "《Spring高级编程》",
            "author": "李专家",
            "read_status": 1
        }
    ]
}

完整截图:

 

数据库验证:

查询过滤:

{
	"data": {
		"title": "编程",
		"pagesize": "10",
		"page": "1"
	},
	"extra": {}
}

结果:

{
    "message": "请求成功",
    "status": "success",
    "data": [
        {
            "id": 72,
            "title": "《Java高级编程》",
            "author": "李专家",
            "read_status": 1
        },
        {
            "id": 73,
            "title": "《Spring高级编程》",
            "author": "李专家",
            "read_status": 1
        }
    ]
}

 查询成功

分页查询:默认传3和1,title不传查所有

{
	"data": {
		"title": "",
		"pagesize": "",
		"page": ""
	},
	"extra": {}
}

响应:

{
    "message": "请求成功",
    "status": "success",
    "data": [
        {
            "id": 68,
            "title": "《我是测试》",
            "author": "李同学",
            "read_status": 0
        },
        {
            "id": 69,
            "title": "《三国演义20》",
            "author": "罗贯中",
            "read_status": 1
        },
        {
            "id": 70,
            "title": "《默》",
            "author": "李晓明",
            "read_status": 0
        }
    ]
}

 对照数据库

查询接口测试通过

七、总结

本次包括接口开发、测试、博文撰写断断续续写了几天,总体上比之前flask实现的要好一些。目前这种CRUD基本上可以在项目上使用了。优化点是可以加上更多的校验和更多的封装

八、期望

作为一名测试专家。Java是必须掌握的语言,而掌握java必须得有几个框架的项目实战,这样才能做好测试。拥有和开发同频对话能力,增加测试自信心,当然也是能力的体现。后续会根据项目分享更多的白盒测试、单元测试文章,共同进步,不说了,老婆叫我包饺子了。。。

九、源码

本来要放在git上的,这一次先全部贴出来。在公司电脑操作涉及安全,我还是先保住工作哈哈哈

9.1 DataSourceConfig.java

//这个文件是禁用了Spring Boot默认的数据源自动配置 ,
// 导致报错"Consider defining a bean of type 'javax.sql.DataSource' in your configuration"
//加了这个文件也能处理这个报错让程序启动
//package com.example.book.config;

//import org.springframework.boot.jdbc.DataSourceBuilder;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import javax.sql.DataSource;
//
//@Configuration
//public class DataSourceConfig {
//    @Bean
//    public DataSource dataSource() {
//        return DataSourceBuilder.create()
//                .url("jdbc:mysql://XX.XX.XXX.24:13300/z_liqiju_test?tinyInt1isBit=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true\n")
//                .username("XXX")
//                .password("XXX")
//                .driverClassName("com.mysql.cj.jdbc.Driver")
//                .build();
//    }
//}

9.2 MyBatisConfig.java

//package com.example.book.config;
//这个文件是解决报错Consider defining a bean of type 'org.apache.ibatis.session.SqlSessionFactory' in your configuration.
//这个config包的2个文件是我在启动类那里加了1行@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
//禁用了Spring Boot默认的数据源自动配置 ,导致报错"Consider defining a bean of type 'javax.sql.DataSource' in your configuration"。坑死个人
//import org.apache.ibatis.session.SqlSessionFactory;
//import org.mybatis.spring.SqlSessionFactoryBean;
//import org.mybatis.spring.annotation.MapperScan;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
//import org.springframework.core.io.support.ResourcePatternResolver;
//import javax.sql.DataSource;
//
//@Configuration
//@MapperScan("com.example.book.mapper") // Mapper接口所在的包路径
//public class MyBatisConfig {
//    private final DataSource dataSource;
//
//    public MyBatisConfig(DataSource dataSource) {
//        this.dataSource = dataSource;
//    }
//
//    @Bean
//    public SqlSessionFactory sqlSessionFactory() throws Exception {
//        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
//        factoryBean.setDataSource(dataSource);
//
//        // 指定MyBatis的Mapper XML文件所在的路径
//        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
//        factoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
//
//        return factoryBean.getObject();
//    }
//}

9.3 controller.java

package com.example.book.controller;

import com.example.book.domain.Book;
import com.example.book.domain.ResponseData;
import com.example.book.service.BookService;
import com.example.book.service.impl.BookNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/book")
public class BookController {
    private static final Logger logger = LoggerFactory.getLogger(BookController.class);

    private final BookService bookService;

    @Autowired
    public BookController(BookService bookService){
        this.bookService = bookService;
    }

    @PostMapping("/add")
    public ResponseEntity<Map<String, String>> addBook(@RequestBody Book book) {
        try {
            //System.out.println("前端传值ReadStatus:"+book.getReadStatus());
            bookService.insertData(book);
            Map<String, String> response = new HashMap<>();
            response.put("message", "图书添加成功!");
            response.put("status", "success");
            return ResponseEntity.ok(response);
        } catch (IllegalArgumentException e) {
            Map<String, String> response = new HashMap<>();
            response.put("message", e.getMessage());
            response.put("status", "fail");
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
     }
    }

    @PostMapping("/delete")
    public ResponseEntity<Map<String, String>> deleteById(@RequestBody Map<String, Object> requestParams) {
        Map<String, Object> data = (Map<String, Object>) requestParams.get("data");
        Long id = Long.valueOf(data.get("id").toString());
        // 构建返回结果
        boolean isDeleted = bookService.deleteById(id);

        if (isDeleted) {
            Map<String, String> successResponse = new HashMap<>();
            successResponse.put("message", "图书被删除!");
            successResponse.put("status", "success");
            return ResponseEntity.ok(successResponse);
        } else {
            Map<String, String> failResponse = new HashMap<>();
            failResponse.put("message", "需要删除的图书不存在!");
            failResponse.put("status", "fail");
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(failResponse);
        }
    }

//    @PostMapping("/update")
//    public void updateBook(@PathVariable Long id, @RequestBody Book book) {
//        bookService.updateData(id, book.getTitle(), book.getAuthor(), book.getReadStatus());
//    }
    @PostMapping("/update")
    public ResponseEntity<ResponseData> updateBook(@RequestBody Book book) {
        try {
            bookService.updateData(book);
            ResponseData response = new ResponseData("success", "图书已更新!");
            return ResponseEntity.ok(response);
        } catch (IllegalArgumentException e) {
            ResponseData response = new ResponseData("fail", e.getMessage());
            return ResponseEntity.badRequest().body(response);
        } catch (BookNotFoundException e) {
            ResponseData response = new ResponseData("fail", "需要修改的书籍id不存在!");
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
     }
    }


    @PostMapping("/query")
    public ResponseData<List<Book>> queryBookByTitle(@RequestBody Map<String, Object> requestParams) {

        Map<String, Object> data = (Map<String, Object>) requestParams.get("data");

        String title = (String) data.get("title");
        int pagesize = data.get("pagesize") != null && !data.get("pagesize").toString().isEmpty() ? Integer.parseInt(data.get("pagesize").toString()) : 3;
        int page = data.get("page") != null && !data.get("page").toString().isEmpty() ? Integer.parseInt(data.get("page").toString()) : 1;
        List<Book> result = bookService.queryBookByTitle(title, pagesize, page);
        return new ResponseData<>("请求成功", "success", result);
    }
}

9.4 BaseInput.java

//package com.example.book.domain;
//
//import io.swagger.annotations.ApiModelProperty;
//import lombok.Data;
//
//import java.util.Map;
//封装请求
//@Data
//public class BaseInput<T> {
//
//    @ApiModelProperty(value = "实际请求参数数据")
//    private T data;
//
//    @ApiModelProperty(value = "扩展数据,字典类型key-value结构")
//    private Map<String,Object> extra;
//}

9.5 Book.java

package com.example.book.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

//@ApiModelProperty干嘛的?
//@ApiModelProperty 是 Swagger 注解之一,用于在 API 文档中描述接口的各个字段(属性)的用途、数据类型、示例值等信息,以便生成更详细的 API 文档。
//Swagger 是一个用于生成和展示 API 文档的工具,可以帮助开发人员更好地了解和使用 API。
//具体来说,@ApiModelProperty 注解可以用于类的属性上,用于描述 API 接口的请求或响应对象的字段。这些描述将会被 Swagger 生成的 API 文档所使用。主要用途包括:
//描述字段用途: 通过 @ApiModelProperty 注解,你可以为每个字段添加描述,解释该字段的用途、作用等,使文档更加易读和易理解。
//指定数据类型: 你可以通过 dataType 参数来指定字段的数据类型,例如字符串、整数、浮点数等。
//提供示例值: 使用 example 参数可以为字段提供示例值,帮助使用者更好地理解字段的期望值。
//控制是否必填: 通过 required 参数,你可以指定字段是否为必填项。
//其他属性: 还可以设置一些其他属性,如是否允许空值、是否隐藏该字段等。
@TableName(value ="books")
@Data
public class Book implements Serializable {
    /**
     * 自动递增id,唯一键
     */
    @TableId(type = IdType.AUTO)
    private Integer id;

    @ApiModelProperty(value = "书名")
    private String title;

    @ApiModelProperty(value = "作者")
    private String author;

    @ApiModelProperty(value = "阅读状态")
    @JsonProperty("read_status") // 指定 JSON 字段名,使得前端传参和read_status对应readStatus
    private Integer readStatus;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;

    //构造函数
    public Book(Integer id, String title, String author, Integer readStatus) {
        this.id = id;
        this.title = title;
        this.author = author;
        this.readStatus = readStatus;
    }

}

9.6 ResponseData.java

package com.example.book.domain;

import lombok.Data;

//ResponseData是一个用于封装响应数据的泛型类。
//它用于统一返回给前端的响应格式,包含三个属性:message、status和data。
//属性说明:
//message:表示响应的消息或描述信息,通常用于描述请求的处理结果或返回的状态信息。
//status:表示响应的状态,通常用于标识请求处理的状态,例如"success"表示成功,"error"表示失败等。
//data:表示响应的数据,它是一个泛型参数,可以用于存储任意类型的数据,比如查询结果、实体对象等。
//例如,在查询数据库时,可以将查询结果存储在data中返回给前端;在处理表单提交时,可以将表单提交的数据存储在data中返回。
//构造函数:
//ResponseData(String message, String status, T data):该构造函数用于创建ResponseData对象,
//并初始化message、status和data属性的值。
//@Data是一个Lombok注解,它可以自动为类生成一些常用的方法,如toString()、equals()、hashCode()、getter和setter方法。使用@Data注解可以简化Java类的编写,减少样板代码。
//具体来说,@Data注解为类的所有非静态字段生成以下方法:
//toString(): 生成一个默认的toString()方法,用于将对象转换为字符串表示。该方法会按照字段的名称和值生成字符串。
//equals(): 生成一个默认的equals()方法,用于比较对象是否相等。该方法会比较对象的所有字段是否相等。
//hashCode(): 生成一个默认的hashCode()方法,用于计算对象的哈希码。该方法基于对象的所有字段计算哈希码。
//getter和setter: 为所有非静态字段生成getter和setter方法,用于获取和设置字段的值。
//使用@Data注解的类通常称为"数据类",它主要用于封装数据,而不包含业务逻辑。在编写POJO(Plain Old Java Object)类时,使用@Data注解可以简化代码,提高代码的可读性和可维护性。
@Data
public class ResponseData<T> {
    private String message;
    private String status;
    private T data;

    public ResponseData(String message, String status, T data) {
        this.message = message;
        this.status = status;
        this.data = data;
    }

    public ResponseData(String message, String status) {
        this.message = message;
        this.status = status;
    }
}

9.7 BookService.java

package com.example.book.service;


import com.example.book.domain.Book;

import java.util.List;

/**
 *
 */
public interface BookService {
    void insertData(Book book);

    boolean deleteById(Long id);
;
    void updateData(Book book);

    List<Book> queryBookByTitle(String title, Integer pagesize, Integer page);


}

9.8 BookServiceImpl.java

package com.example.book.service.impl;

import com.example.book.domain.Book;
import com.example.book.mapper.BookMapper;
import com.example.book.service.BookService;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

//创建了BooksServiceImpl类,并将其标记为@Service,以便Spring能够识别并注册它作为bean。
//同时,也要确保在BooksServiceImpl的构造函数中使用了@Autowired来注入BooksMapper。
//在MyBatis中,分页应该使用RowBounds或者使用PageHelper来实现
@Service
public class BookServiceImpl implements BookService  {

    private final BookMapper bookMapper;

    @Autowired
    public BookServiceImpl(BookMapper booksMapper) {
        this.bookMapper = booksMapper;
    }

    @Override
    public void insertData(Book book) {
        if (book.getTitle() == null || book.getAuthor() == null || book.getReadStatus() == null) {
            throw new IllegalArgumentException("title、author和read_status是必传参数!");
        }

    // Check for duplicate title
        String title = book.getTitle().trim();
        if (bookMapper.existsByTitle(title)) {
            throw new IllegalArgumentException("书名(title)重复!");
        }

        bookMapper.insertData(book);
}

    @Override
    public boolean deleteById(Long id) {
        if (id == null || id <= 0) {
            throw new IllegalArgumentException("无效的图书 ID");
        }

        int rowsAffected = bookMapper.deleteById(id);
        return rowsAffected > 0;
    }

//    @Override
//    public void updateData(Long id, String title, String author, Integer readStatus) {
//        bookMapper.updateData(id, title, author, readStatus);
//    }

    @Override
    public void updateData(Book book) {
        if (book.getId() == null || book.getTitle() == null || book.getAuthor() == null || book.getReadStatus() == null) {
            throw new IllegalArgumentException("id、title、author和read_status是必传参数!");
        }

        List<Book> bookList = bookMapper.selectAll();
        List<Integer> idList = bookList.stream().map(Book::getId).collect(Collectors.toList());

        if (!idList.contains(book.getId())) {
            throw new IllegalArgumentException("需要修改的书籍id不存在!");
        }

        String title = book.getTitle().trim();
        String author = book.getAuthor().trim();
        int readStatus = book.getReadStatus();

        if (title.isEmpty()) {
            throw new IllegalArgumentException("title不能为空!");
        }
        if (author.isEmpty()) {
            throw new IllegalArgumentException("作者不能为空!");
        }
        if (readStatus != 0 && readStatus != 1) {
            throw new IllegalArgumentException("阅读状态只能为0和1!");
        }

        bookMapper.updateData(book.getId().longValue(), title, author, readStatus);
    }


    @Override
    public List<Book> queryBookByTitle(String title, Integer pagesize, Integer page) {
        int offset = pagesize * (page - 1);
        RowBounds rowBounds = new RowBounds(offset, pagesize);
        return bookMapper.queryBookByTitle(title, rowBounds);
    }
}

9.9 BookNotFoundException.java

package com.example.book.service.impl;

public class BookNotFoundException extends RuntimeException {
    public BookNotFoundException(String message) {
        super(message);
    }
}

9.10 BookMapper.java

package com.example.book.mapper;

import com.example.book.domain.Book;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.RowBounds;

import java.util.List;

/**
 * @Entity com.example.book.domain.Books
 */
@Mapper
public interface BookMapper extends BaseMapper<Book> {
    void insertData(Book book);
    boolean existsByTitle(@Param("title") String title);

    int deleteById(@Param("id") Long id);

    void updateData(@Param("id") Long id, @Param("title") String title, @Param("author") String author, @Param("readStatus") Integer readStatus);

    List<Book> queryBookByTitle(@Param("title") String title, RowBounds rowBounds);

    List<Book> selectAll();
}




9.11 BookApplication.java

package com.example.book;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
@MapperScan("com.example.book.mapper")
public class BookApplication {
    public static void main(String[] args) {
        SpringApplication.run(BookApplication.class, args);
    }

}

9.12 BookMapper.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.example.book.mapper.BookMapper">


    <resultMap id="BaseResultMap" type="com.example.book.domain.Book">
            <id property="id" column="id" jdbcType="INTEGER"/>
            <result property="title" column="title" jdbcType="VARCHAR"/>
            <result property="author" column="author" jdbcType="VARCHAR"/>
            <result property="readStatus" column="read_status" jdbcType="INTEGER"/>
    </resultMap>

    <sql id="Base_Column_List">
        id,title,author,
        read_status
    </sql>
    <!-- 增加 -->
    <insert id="insertData">
        INSERT INTO book (title, author, read_status)
        VALUES (#{title}, #{author}, #{readStatus})
    </insert>
    <!-- 判断是否存在指定 title 的记录 -->
    <select id="existsByTitle" resultType="boolean" parameterType="java.lang.String">
        SELECT COUNT(*) > 0
        FROM book
        WHERE title = #{title}
    </select>
    <!-- 删除 -->
    <delete id="deleteById" parameterType="long">
        DELETE FROM book WHERE id = #{id}
    </delete>
    <!-- 修改 -->
    <update id="updateData">
        UPDATE book SET
        title = #{title},
        author = #{author},
        read_status = #{readStatus}
        WHERE id = #{id}
    </update>
    <!-- 查询 -->
    <select id="queryBookByTitle" resultType="com.example.book.domain.Book">
        SELECT id,title,author,read_status readStatus FROM book
        <where>
            <if test="title != null and title != ''">
                AND title LIKE CONCAT('%', #{title}, '%')
            </if>
        </where>
    </select>
    <!-- 查询所有图书 -->
    <select id="selectAll" resultType="com.example.book.domain.Book">
    SELECT id, title, author, read_status as readStatus
    FROM books
</select>

</mapper>

9.13 application.properties

注意我加了个1在后缀,2个文件选择1个生效即可

spring.datasource.url = jdbc:mysql://XX.XX.XXX.24:13300/z_liqiju_test?tinyInt1isBit=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true
spring.datasource.username = XXX
spring.datasource.password = XXX
mybatis.mapper-locations = classpath*:mapper/*.xml
server.port = 5002
logging.level.root=info
#logging.level.root=DEBUG
#logging.level.org.mybatis=DEBUG

9.14 application.yml

原来是的项目是5001 端口,这一次改为5002端口启动防止端口冲突

spring:
    datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://XX.XX.XXX.24:13300/z_liqiju_test?tinyInt1isBit=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true
        username: XXX
        password: XXX

mybatis:
    mapper-locations: classpath*:mapper/*.xml

server:
    port: 5002

logging:
    level:
        root: DEBUG
#        org:
#          mybatis: INFO

9.15 pom.xml

<?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>3.0.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>book</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>book</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!-- spring-boot-starter-jdbc 驱动 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!-- spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </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-annotation</artifactId>
            <version>3.3.1</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-core</artifactId>
            <version>3.3.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-extension</artifactId>
            <version>3.3.1</version>
        </dependency>
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.6.6</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.2.5.RELEASE</version>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.1</version>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2023年8月12日 -深圳笔

有疑问直接评论区见

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/868476.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

教你如何使用AES对接口参数进行加密

教你如何使用AES对接口参数进行加密 前言 我们作为程序猿&#xff0c;在浏览网站的时候偶尔也会打开控制台看看请求的接口&#xff0c;我们会发现有些接口的传输是 “乱码” &#xff0c;那么这个乱码究竟是什么呢&#xff1f;为什么要这么做&#xff1f; 其实这个所谓的 “…

无涯教程-Perl - qq函数

描述 可以使用此函数代替双引号。这实际上不是一个函数,更像是一个运算符,但是如果您在其他程序员的程序中看到它却不记得它是什么,那么可能会在这里看。实际上,您可以使用任何一组定界符,而不仅仅是括号。 语法 以下是此函数的简单语法- qq ( string )返回值 该函数返回双…

De Bruijin序列与魔术(三)——De Bruijin序列的拓展思考

早点关注我&#xff0c;精彩不错过&#xff01; 在前面的文章中&#xff0c;我们已经介绍完经典DeBruijin序列的原理和魔术&#xff0c;相关内容请戳&#xff1a; De Bruijin序列与魔术&#xff08;二&#xff09;——魔术《De Bruijin序列》 De Bruijin序列与魔术&#xff08;…

Chord diagram | 啧啧啧!~人人必会的Chord diagram你不来学一学吗!?

1写在前面 啊啊啊啊啊&#xff01;&#xff01;&#xff01;&#xff01;~终于值完夜班休息了。&#x1f62d; 最近是大搞医疗反腐的日子&#xff0c;㊗️各位执法人员成绩满满&#xff01;~&#x1f912; 听说以后医务人员要年薪制了&#xff0c;完全搞不懂这些东西的初衷和理…

七夕好物分享,哪些礼物适合送男/女朋友?这几款好物最为合适!

七夕是个值得纪念的日子&#xff0c;牛郎织女鹊桥相会的故事百年流传&#xff0c;七夕是一个表达爱意的节日&#xff0c;送礼物是必不可少的&#xff0c;情侣们可以选择一份有意义的礼物&#xff0c;也可以选择对方需要的东西当做礼物来赠送&#xff0c;总的来说&#xff0c;送…

STM32F429IGT6使用CubeMX配置按键检测

1、硬件电路 2、设置RCC&#xff0c;选择高速外部时钟HSE,时钟设置为180MHz 3、配置GPIO引脚 4、生成工程配置 5、部分代码 /* USER CODE BEGIN 0 */ //按键检测函数 void KEY_Test(void) {if(SET HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)){while(SET HAL_GPIO_ReadPin(…

HCIP 链路聚合技术

1、链路聚合概述 为了保证网络的稳定性&#xff0c;仅仅是设备进行备份还不够&#xff0c;我们需要针对我们的链路进行备份&#xff0c;同时也增加了链路的利用率&#xff0c;提高带宽。避免一条链路出现故障&#xff0c;导致网络无法正常通信。这就可以使用链路聚合技术。 以…

PyTorch翻译官网教程-NLP FROM SCRATCH: GENERATING NAMES WITH A CHARACTER-LEVEL RNN

官网链接 NLP From Scratch: Generating Names with a Character-Level RNN — PyTorch Tutorials 2.0.1cu117 documentation 使用字符级RNN生成名字 这是我们关于“NLP From Scratch”的三篇教程中的第二篇。在第一个教程中</intermediate/char_rnn_classification_tutor…

走进知识图谱(二)【世界知识图谱篇】知识表示的经典模型与平移模型及基于复杂关系建模的知识表示学习

上篇文章提到&#xff0c;该系列文章将主要围绕世界知识图谱和语言知识图谱这两大类知识图谱进行展开&#xff0c;并且提到知识图谱的主要研究包括了知识表示学习、知识自动获取和知识的推理与应用三大部分。今天主要介绍世界知识图谱的知识表示学习&#xff0c;其中包括经典的…

netty基础与原理

Netty线程模型和Reactor模式 简介&#xff1a;reactor模式 和 Netty线程模型 设计模式——Reactor模式&#xff08;反应器设计模式&#xff09;&#xff0c;是一种基于 事件驱动的设计模式&#xff0c;在事件驱动的应用中&#xff0c;将一个或多个客户的 服务请求分离&#x…

跑步运动耳机哪个牌子好、跑步运动耳机推荐

随着生活质量的提高&#xff0c;运动健身已经成为一种新的潮流&#xff0c;而跑步更是如今众多人参与的热门运动项目之一。在谈到跑步时&#xff0c;很多人习惯性地戴上耳机&#xff0c;在奔跑的过程中播放自己喜欢的音乐&#xff0c;以免运动变得枯燥&#xff0c;并增添一些轻…

开工大吉|华润鞋业二期自动化改造项目开工典礼圆满举行

2023年8月10日上午&#xff0c;山东百华鞋业有限公司择良辰吉时隆重举行了华润鞋业二期厂房动工仪式&#xff0c;公司总经理郭兴梅女士携公司管理层代表和施工单位代表参加了动工仪式。 根据公司发展规划&#xff0c;对未来发展的美好期许&#xff0c;以及公司生产与研发保持的…

从 Zebec Protocol 长期布局看,ZBC 通证的潜在应用场景

流支付协议 Zebec Protocol 在去年被推出以来&#xff0c;始终保持着较为迅猛的市场进展&#xff0c;与此同时其还基于 ZBC 构建了全新的治理体系&#xff0c;并上线了以 ZBC 资产为核心的治理系统&#xff0c;让生态逐渐走向 DAO。

题目大解析(3)

题目 字符串中的第一个唯一字符 字符串中的第一个唯一字符 原题链接&#xff1a;字符串中的第一个唯一字符 计数法&#xff1a; class Solution { public:int firstUniqChar(string s) {int arr[130] {0};for(auto x : s){arr[x-0];}int i 0;for(auto x : s){if(arr[x-0] …

《算法竞赛·快冲300题》每日一题:“造电梯”

《算法竞赛快冲300题》将于2024年出版&#xff0c;是《算法竞赛》的辅助练习册。 所有题目放在自建的OJ New Online Judge。 用C/C、Java、Python三种语言给出代码&#xff0c;以中低档题为主&#xff0c;适合入门、进阶。 文章目录 题目描述题解C代码Java代码Python代码 “ 造…

使用python对图像加噪声

加上雨点噪声 import cv2 import numpy as npdef get_noise(img, value10):#生成噪声图像>>> 输入&#xff1a; img图像value 大小控制雨滴的多少 >>> 返回图像大小的模糊噪声图像noise np.random.uniform(0, 256, img.shape[0:2])# 控制噪声水平&#xff…

Arduino ESP32 v2 使用记录:开发环境搭建

文章目录 目的开发环境搭建程序下载测试使用VS Code进行开发批量烧录固件到模块中总结 目的 在之前的文章 《使用Arduino开发ESP32&#xff08;01&#xff09;&#xff1a;开发环境搭建》 中介绍了使用Arduino开发ESP32的开发环境搭建内容&#xff0c;只不过当时的 Arduino co…

写一个函数返回参数二进制中 1 的个数(c语言三种实现方法)

&#xff08;本文旨在自己做题时的总结&#xff0c;我会给出不同的解法&#xff0c;后面如果碰到新的题目还会加入其中&#xff0c;等于是我自己的题库。 1.写一个函数返回参数二进制中 1 的个数。 比如&#xff1a; 15 0000 1111 4 个 1 方法一&#xff1a; #include…

Kubernetes 调度约束(亲和性、污点、容忍)

目录 一、Pod启动典型创建过程 二、调度流程 三、指定调度节点 1.使用nodeName字段指定调度节点 2.使用nodeSelector指定调度节点 2.1给对应的node节点添加标签 2.2修改为nodeSelector调度方式 3.通过亲和性来指定调度节点 3.1节点亲和性 3.2Pod亲和性与反亲和性 3.2…

SAP MM学习笔记18- SQVI 工具

Tr-cd SQVI 是一个SAP数据库工具。 使用这个工具&#xff0c;可以自己构建查询界面中的条件&#xff0c;查询对象&#xff0c;从而自由查询数据&#xff0c;实现标准没有的功能。 1&#xff0c;SQVI 和 SQ1&#xff0c;SQ2&#xff0c;SQ3 的不同 SQ1&#xff0c;2&#xff0…