Spring Boot 集成MyBatis-Plus

文章目录
    • 一、背景说明
    • 二、集成过程
      • 2.1 引入 maven 依赖
      • 2.2 增加属性配置
      • 2.3 自动配置类
    • 三、验证集成
      • 3.1 控制器
      • 3.2 服务类
      • 3.3 Mapper接口类
      • 3.4 实体类
      • 3.4 不要忘记XML文件
      • 3.5 发起请求
    • 四、技巧拓展
      • 4.1 如何打印sql语句?
      • 4.2 如何对参数增加非空验证?
      • 4.3 如何查看 Maven 依赖?
      • 4.3 如何解决时间格式问题?
      • 4.4 如何快速增加 `serialVersionUID` 属性?
    • 五、总 结
一、背景说明

大部分的项目都需要进行数据的持久化,所以必然会使用到数据库。而关系型数据库是其中比较常见的数据库类型。目前使用比较多的关系型数据库有:MySQL、PostgreSQL和Oracle等。

在古早的应用开发中,需要开发人员写许多的DAL(数据访问层)代码,需要自己管理数据库的连接与关闭,还需要自己从ResultSet中获取数据,然后再将其组装为对象。在其中会编写大量非业务代码,并且这些代码往往充满了重复。

ORM的出现解决了前面提到的一系列问题,ORM的全称是 Object Relational Mapping ,翻译为对象关系模型。这里所说的对象是指业务领域的对象,关系 则指的是关系数据库(具体而言就是数据表和字段)。

ORM可以实现自动将数据库中的表字段映射为对象的属性,其采用的方式是:使用映射元数据 来描述对象关系的映射。ORM充当了应用程序的业务逻辑层和数据库之间的桥梁。常见的ORM中间件有:Hibernate和ibatis(目前已更名为 MyBatis)。

本篇所提及的 MyBatis-PlusMyBatis 的功能进行了增强。

在这里插入图片描述

二、集成过程

在Spring Boot项目中集成 MyBatis-Plus 过程比较简单,大概分为三个步骤:

  • 引入maven依赖
  • 增加属性配置
  • 增加相关的自动配置类

如果不知道如何创建Spring Boot项目,可以参考以往的文章:

[Spring Boot实战] 如何快速地创建spring boot项目

2.1 引入 maven 依赖

为了使用 MyBatis-Plus,需要添加相关依赖:

 <!--mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybaits-plus.version}</version></dependency>

同时因为需要数据库进行数据的存储,所以还需要添加下面的依赖(我们选择的MySQL数据库):

 <!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency>

如果仅限于集成 MyBatis-Plus,这两个依赖就已经足够了。

2.2 增加属性配置

相关的属性配置到 application.yml 文件中:

spring:application:name: "demo-api"datasource:# MySql 8.0以上版本driver-class-name: com.mysql.cj.jdbc.Driver# 兼容以前的配置jdbc-url: jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&useTimezone=true&serverTimezone=GMT%2B8&allowMultiQueries=trueurl: ${spring.datasource.jdbc-url}username: your_usernamepassword: your_password
2.3 自动配置类

前面我们已经提过:ORM使用映射元数据 来描述对象关系的映射。所以我们需要对相关信息进行配置,比如:

  • 使用的是什么数据库
  • 数据库连接是什么
  • Mapper 接口文件在哪里
  • MyBatis 所依赖的XML文件到哪里去找
  • MyBatis 的 SqlSessionFactory 如何创建(依赖数据源、MyBatis插件)

Spring Boot 中的配置一般以 @Configuration 进行标识。相关代码如下:

@Configuration
@MapperScan(basePackages = "com.xhm.demo.api.mapper")
public class DataSourceConfig {@ConfigurationProperties(prefix = "spring.datasource")@Beanpublic DataSource dataSource() {return DataSourceBuilder.create().build();}@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor());//注册乐观锁插件interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource, MybatisPlusInterceptor interceptor) throws Exception {MybatisSqlSessionFactoryBean ssfb = new MybatisSqlSessionFactoryBean();ssfb.setDataSource(dataSource);ssfb.setPlugins(interceptor);//到哪里找xml文件ssfb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/*Mapper.xml"));return ssfb.getObject();}
}

配置类的 @MapperScan(basePackages = "com.xhm.demo.api.mapper") 指定了 Mapper 接口的存放位置。

第一个 Bean 用于创建数据源,会根据相关依赖自动判断数据库类型。@ConfigurationProperties(prefix = "spring.datasource") 会将以 spring.datasource 开头的属性配置赋值给数据源对象的对应属性。

第二个 Bean 用于注册 MyBatis-Plus 的相关插件(这里暂时只注册了 分页插件和 乐观锁插件)

第三个 Bean 用于创建 SqlSessionFactory 的实例。 SqlSessionFactory 是一个接口,负责数据库Session(会话)的管理,数据库会话通俗点讲就是客户端与数据库的单次连接。其依赖下面的信息:

  • 数据源
  • MyBatis-Plus 插件
  • xml文件的存放位置(资源路径)
三、验证集成

下面是常见的三层架构的目录结构:

api
├── controller
├── dto
├── entity
├── mapper
└── service

其中最主要的目录自上而下依次是:

  • controller :控制器目录(表示层)
  • service:服务类目录(业务逻辑层)
  • mapper:DAO目录(数据访问层)

另外两个目录的作用:

  • entity:实体类目录
  • dto:数据传输对象(DTO)目录

创建对应的表:

create table t_card_puncher
(id                   int(10) not null auto_increment,name                 varchar(15) comment '打卡人姓名',nick                 varchar(50) comment '打卡人昵称',avatar               varchar(100) comment '头像',status               smallint(2) default 10 comment '打卡人状态(10-已保存;20-已启用;0-已作废)',account_id           int(10) comment '对应账号id',remark               varchar(500) comment '备注',added_by             int(10) comment '新增人id',added_by_name        varchar(20) comment '新增人姓名',added_time           datetime default CURRENT_TIMESTAMP comment '新增时间',last_modified_by     int(10) comment '最后修改人id',last_modified_by_name varchar(20) comment '最后修改人姓名',last_modified_time   datetime default CURRENT_TIMESTAMP comment '最后修改时间',last_modified_ip     varchar(50) comment '最后修改IP',valid                smallint(1) default 1 comment '是否有效(1-有效;0-无效)',primary key (id)
);alter table t_card_puncher comment '习惯打卡人表';

依次在上面的目录中创建相关类,实现一个简单的接口。我们将按照从上而下的顺序创建相关的类。

3.1 控制器

代码清单3.1-1:

// CardPuncherController.java@RestController
@RequestMapping("/cardPuncher")
public class CardPuncherController {@Resourceprivate CardPuncherService service;@PostMapping("/queryById")public BaseResponse<CardPuncher> queryById(@RequestBody @Valid IdRequest request){CardPuncher dto = service.queryCardPuncherById(request.getId());return BaseResponse.ok(dto);}
}

控制器类依赖下面三个类:

  1. IdRequest (DTO)
  2. CardPuncher (实体类)
  3. CardPuncherService (服务类–接口)

代码清单3.1-2:

// IdRequest.javaimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;import java.io.Serializable;
import javax.validation.constraints.NotNull;@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class IdRequest implements Serializable {private static final long serialVersionUID = 4111263664475615283L;/*** id*/@NotNull(message = "ID不能为空!")private Integer id;
}
3.2 服务类

控制器一般不含任何的业务逻辑,它只是将请求委托给相关的服务类。并且为了保持依赖的松散,控制器是不直接依赖于具体类的,而是依赖于接口。

代码清单3.2-1:

//打卡人服务类 :CardPuncherService.javaimport com.baomidou.mybatisplus.extension.service.IService;
import com.xhm.demo.api.entity.CardPuncher;public interface CardPuncherService extends IService<CardPuncher> {/*** 根据id查询打卡人** @param id 打卡人id* @return 打卡人DTO*/CardPuncher queryCardPuncherById(Integer id);
}

代码清单3.2-2,就是接口的对应实现:

// 打卡人服务实现类:CardPuncherServiceImpl.java
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xhm.demo.api.entity.CardPuncher;
import com.xhm.demo.api.enums.ValidEnum;
import com.xhm.demo.api.mapper.CardPuncherMapper;
import com.xhm.demo.api.service.CardPuncherService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;@Service
@Slf4j
public class CardPuncherServiceImpl extends ServiceImpl<CardPuncherMapper, CardPuncher> implements CardPuncherService {@Overridepublic CardPuncher queryCardPuncherById(Integer id) {LambdaQueryWrapper<CardPuncher> queryWrapper = Wrappers.lambdaQuery();queryWrapper.eq(CardPuncher::getValid, ValidEnum.VALID.getCode());queryWrapper.eq(CardPuncher::getId, id);return this.getOne(queryWrapper);}
}

服务实现类依赖下面的类:

  1. ServiceImpl(MyBatis-Plus内置的服务实现类)
  2. CardPuncherService (服务接口类)
  3. CardPuncherMapper(Mapper接口)
  4. CardPuncher
  5. Wrappers (条件构造器)
3.3 Mapper接口类

Mapper接口类比较简单,因为我们没有自定义脚本,而是调用了 ServiceImpl.getOne 方法。所以 Mapper 接口类中是没有自己的方法的。

代码清单3.3-1:

// 打卡人 Mapper 接口类:CardPuncherMapper.javaimport com.baomidou.mybatisplus.core.mapper.BaseMapper;public interface CardPuncherMapper extends BaseMapper<CardPuncher> {}
3.4 实体类

实体类和数据表的字段是一一对应的,所以其是一个比较重要的类。相关代码如下:

@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("t_card_puncher")
public class CardPuncher extends BaseEntity {private static final long serialVersionUID = 1L;/*** 打卡人姓名*/private String name;
}

其中需要重点强调的是:

当数据库中table的名称和实体类名称不一致时,一定需要使用注解 @TableName 进行显示声明表的名称。

否则会提示相关的表不存在(例子中默认认为表名是 card_puncher,而我们的表是有前缀 t_ 的)

其中的注解 @Accessors(chain = true) 的作用如下:

被该注解修饰的类,setters方法返回的该类的实例(即this),而不是void。所以可以链式地调用setters方法。

链式调用代码如下:

CardPuncher entity = new CardPuncher().setName("老书生");

上述类之间的关系如下图所示:
在这里插入图片描述

3.4 不要忘记XML文件

在讲自动配置类时,我们提到过需要指定xml文件的存放位置:

"classpath:/mapper/*Mapper.xml"

那么我们必须在mapper中创建对应的xml文件,否则就会报如下错误:

org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'cardPuncherController'
: Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException
: Error creating bean with name 'cardPuncherServiceImpl'
: Unsatisfied dependency expressed through field 'baseMapper'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException
: Error creating bean with name 'cardPuncherMapper' defined in file
...
nested exception is java.io.FileNotFoundException: class path resource [mapper/] cannot be resolved to URL because it does not exist

主要错误信息:

java.io.FileNotFoundException: class path resource [mapper/] cannot be resolved to URL because it does not exist

XML文件(CardPuncherMapper.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.xhm.demo.api.mapper.CardPuncherMapper"></mapper>

启动Spring Boot 项目,观察控制台,发现多了MyBatis的banner。并且没有任何报错信息。
在这里插入图片描述
移步到端点,可以发现多一个db项:
db
表明项目中使用了数据库,并且数据库的类型是MySQL。

3.5 发起请求

使用 Postman 发起接口请求。
在这里插入图片描述
因为此时表中还没有数据,所以结果如下:
在这里插入图片描述
插入1条数据:

insert into my_database.t_card_puncher(`name`,nick,added_by,added_by_name,last_modified_by,last_modified_by_name)
values('老书生','leon',-1,'系统',-1,'系统');

再次请求,结果如下:
在这里插入图片描述
可以成功查出数据。到这里 MyBatis 的集成已经初步成功了。

之所以说初步成功,是因为还有一些问题需要解决。如上图中时间格式的问题,2024-06-21T09:31:00 这种时间格式有自己的专有名词:ISO日期时间格式 / ISO 8601

还有一个问题:当Id为空时,没有返回期望的错误信息,而是报400错误:
400

四、技巧拓展
4.1 如何打印sql语句?

发送请求后,在项目的控制台没有出现期望的sql语句,那么该如何处理才能成功打印sql语句呢?

其实方法也很简单,在 application.yml 中添加如下配置:

# 查看sql
logging:level:com.xhm.demo.api.mapper: debug

其中的 com.xhm.demo.api.mapper 是 Mapper 接口所在的包名。设置其日志级别为debug

打印的sql脚本如下:

2024-06-23 00:26:49.147 DEBUG 40300 --- [nio-8080-exec-6] c.x.d.a.m.CardPuncherMapper.selectOne    : ==>  Preparing: SELECT id,name,added_by,added_by_name,added_time,last_modified_by,last_modified_by_name,last_modified_time,last_modified_ip,valid FROM t_card_puncher WHERE (valid = ? AND id = ?)
2024-06-23 00:26:49.188 DEBUG 40300 --- [nio-8080-exec-6] c.x.d.a.m.CardPuncherMapper.selectOne    : ==> Parameters: 1(Integer), 1(Integer)
2024-06-23 00:26:49.226 DEBUG 40300 --- [nio-8080-exec-6] c.x.d.a.m.CardPuncherMapper.selectOne    : <==      Total: 1

脚本是出现了,但是似乎不那么完美。如果查询条件比较多,拼接起来还是比较麻烦的。有无其他办法能够查看拼接好的脚本呢?

可以试试一款IDEA插件:MyBatis Log Free
在这里插入图片描述
借助这款插件,可以查看完整的sql语句:
在这里插入图片描述
如果是第一次使用该插件,可以通过如下方式打开:

【Tools】-> 【MyBatis Log Plugin】

4.2 如何对参数增加非空验证?

非空校验不生效,有两种情况:

  1. 空条件直接传到sql中
  2. 接口返回400错误,控制台有 WARN 日志

第一种情况

如果 @NotNull注解在请求类的属性 (如IdRequest类)上。检查Controller的方法的入参是否遗漏 @Valid 注解。
在这里插入图片描述
如果已经增加了注解,非空校验依然没有生效,则有可能是maven依赖不完整,可能 pom 中只增加了如下依赖:

<dependency><groupId>javax.validation</groupId><artifactId>validation-api</artifactId>
</dependency>

有两种解决方法:

  1. 增加 hibernate-validator 依赖
    validation-api 中只定义了相关注解,具体的校验逻辑在 hibernate-validator 中。所以需要添加如下依赖:

    org.hibernate.validator hibernate-validator

  2. 去除 validation-api 依赖,以下面的依赖进行替换:

    org.springframework.boot spring-boot-starter-validation

第二种情况

因为没有对 MethodArgumentNotValidException 异常进行处理而导致校验信息没有返回。具体的现象是响应报文的结果如下:
异常02

所以需要增加 统一异常处理类 。相关代码如下:

// exception/CommonExceptionHandler.java@Slf4j
@ControllerAdvice
public class CommonExceptionHandler {@ExceptionHandler({MethodArgumentNotValidException.class})@ResponseBodypublic BaseResponse<?> bindMethodArgumentNotValidException(MethodArgumentNotValidException ex) {String traceId = MDC.get("traceId");StringBuilder errorMessage = new StringBuilder();List<FieldError> errors = ex.getBindingResult().getFieldErrors();for (FieldError error : errors) {//只取错误信息errorMessage.append(error.getDefaultMessage()).append(";");}errorMessage.deleteCharAt(errorMessage.length() - 1);log.error(String.format("traceId: %s, Exception: [%s] %s, request error, parameters invalid:%s", traceId, ex.getParameter().getParameterName(), ex, errorMessage));return BaseResponse.error(BaseResultCodeEnum.ERROR.getCode(), errorMessage.toString());}
}

BaseResponse.java代码如下:

@Data
public class BaseResponse<T> implements Serializable {private static final long serialVersionUID = -5330932746124338859L;/*** 响应状态码*/private String code;/*** 响应消息*/private String message;/*** 响应数据*/private T data;public static <K> BaseResponse<K> ok() {return BaseResponse.result(BaseResultCodeEnum.SUCCESS.getCode(), BaseResultCodeEnum.SUCCESS.getMessage());}public static <K> BaseResponse<K> ok(K data) {BaseResponse<K> response = BaseResponse.ok();response.setData(data);return response;}public static <K> BaseResponse<K> error(String message) {return BaseResponse.result(BaseResultCodeEnum.ERROR.getCode(), message);}public static <K> BaseResponse<K> error(String code, String message) {return BaseResponse.result(code, message);}public static <K> BaseResponse<K> result(String code, String message) {BaseResponse<K> response = new BaseResponse<>();response.setCode(code);response.setMessage(message);return response;}
}
4.3 如何查看 Maven 依赖?

有两种查看 Maven 依赖的方式:

  1. mvn dependency:tree命令
  2. 使用Idea的 Dependency Analyzer

其中第二种方式依赖 IDEA的插件: Maven Helper

第一种方式的演示:

在终端输入如下命令:

mvn dependency:tree

输出结果如下:

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building demo-api 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ demo-api ---
[INFO] com.xhm:demo-api:jar:0.0.1-SNAPSHOT
...
[INFO] +- org.projectlombok:lombok:jar:1.18.20:compile (optional)
...
[INFO] +- mysql:mysql-connector-java:jar:8.0.23:runtime
[INFO] - org.springframework.boot:spring-boot-starter-validation:jar:2.3.10.RELEASE:compile
[INFO]    +- org.glassfish:jakarta.el:jar:3.0.3:compile
[INFO]    - org.hibernate.validator:hibernate-validator:jar:6.1.7.Final:compile
[INFO]       +- jakarta.validation:jakarta.validation-api:jar:2.0.2:compile
[INFO]       +- org.jboss.logging:jboss-logging:jar:3.4.1.Final:compile
[INFO]       - com.fasterxml:classmate:jar:1.5.1:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.018 s
[INFO] Finished at: 2024-06-25T16:12:36+08:00
[INFO] Final Memory: 26M/304M
[INFO] ------------------------------------------------------------------------

第二种方式的演示:

打开pom文件,从 Text 模式切换到 Dependency Analyzer 模式:

  • 选择 All Dependencies as Tree ,点击 “Refresh UI” 按钮
  • 收缩树形结构(全部)
  • 展开 spring-boot-starter-validation ,可以看到其依赖项。

如图所示,其依赖于 hibernate-validator,而 hibernate-validator 又依赖于 jakarta.validation-api (替换validation-api 包含注解 javax.validation.constraints.NotNull

在这里插入图片描述

4.3 如何解决时间格式问题?

前面我提到过接口返回的日期时间格式默认是:ISO日期时间格式。而很多时候我们需要指定的日期时间格式。如:

  • 当提到出生日期是,时间精度至少需要精确到天,在中国一般的日期格式是:2024-06-20
  • 但是当我们需要获知订单的创建时间时,可能需要这样的时间格式:2024-06-21 08:30:50

要解决这个问题,其实也比较简单,可以使用 jackson-annotation 包中的注解 @JsonFormat 对日期时间格式进行自定义。

下面使用该注解对 BaseEntity 进行功能增强:

@Data
public class BaseEntity implements Serializable {
.../*** 新增时间*/@JsonFormat(pattern = "yyyy-MM-dd")private LocalDateTime addedTime;.../*** 最后修改时间*/@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime lastModifiedTime;
}

这里只是为了演示的方便,生产代码不建议这样写:

直接将实体类暴露给最终用户不是一个好的实践,因为一旦字段发生变动,就需要客户代码进行相应的调整。
普遍的做法是:引入DTO作为接口的数据载体。所以 @JsonFormat 注解也是打在DTO类上。

4.4 如何快速增加 serialVersionUID 属性?

对于可序列化的类,一般强烈建议显式声明 serialVersionUID 值。因为默认计算得到的 serialVersionUID 值可能会引发 InvalidClassException 。那么有没有什么快速的方法去完成这件事情呢?

如果你使用的是 IDEA ,可以参考下面的方法:

  • 打开 Settings 窗口,输入搜索词: serial
  • 在【Editor】-【Inspections】中找到:【Java】- 【Serializatioin issues】。
  • 然后勾选其中的 Serializable class without serialVersionUID

在这里插入图片描述
上面设置的作用是:如果一个类实现了Serializable接口,但是没有声明 serialVersionUID 值,就会告警。
xx
F2 定位到告警信息后,再组合按键 Alt + Enter ,弹出上图的信息后,再按一次 Enter 后,就可以快速插入 serialVersionUID 值了。使用该方式生成的 serialVersionUID 能很好地避免重复的问题。

五、总 结

本文详细讲述了如何在Spring Boot 项目中集成 MyBatis-Plus,其中的设置和依赖都遵循最小功能集的原则。

增加集成相关的配置和依赖后,我们又创建了一个简单的接口,用于验证本次集成是否成功。其中谈到了SSM项目中基本的项目结构,相关代码都是从生产代码中抽象出来的,具有很强的参考意义。文中还以类图的形式给出了各个类之间的关系,可以帮助读者更好地理解代码结构。

接口测试时,我们使用了post工具,Postman是其中比较常见的一款。通过接口可以成功返回数据,但是数据的格式有一些问题。文章对这些问题的解决进行了细致地阐述。

最后还分享了实际开发中可能会用到的一些小技巧,希望可以启动抛砖引玉的作用。有交流意愿的小伙伴可以在评论区进行留言。


参考资料:

  • iBatis详解以及和MyBatis区别
  • MyBatis-Plus
  • maven的mvn dependency依赖分析和常用命令介绍
  • Spring Boot 中 validation-api…
  • Oracle、SqlServer、Mysql、PostgreSQL数据库的选型
  • ORM是什么?如何理解ORM

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣hot100——排序链表(常见方法,归并排序)

解题思路&#xff1a; 分解&#xff08;Divide&#xff09;&#xff1a;将待排序的列表递归地分成两半&#xff0c;直到每个子列表只包含一个元素&#xff08;此时每个子列表都是有序的&#xff09;。解决&#xff08;Conquer&#xff09;&#xff1a;递归地对每个子列表进行排…

技术解析 | 适用于TeamCity的Unreal Engine支持插件,提升游戏构建效率

龙智是JetBrains授权合作伙伴、Perforce授权合作伙伴&#xff0c;为您提供TeamCity、Perforce Helix Core等热门的游戏开发工具及一站式服务 TeamCity 是游戏开发的热门选择&#xff0c;大家选择它的原因包括支持 Perforce、可以进行本地安装&#xff0c;并提供了多种配置选项。…

Three.js 快速入门教程【二】透视投影相机

系列文章目录 系列文章目录 Three.js 快速入门教程【一】开启你的 3D Web 开发之旅 Three.js 快速入门教程【二】透视投影相机 Three.js 快速入门教程【三】渲染器 Three.js 快速入门教程【四】三维坐标系 Three.js 快速入门教程【五】动画渲染循环 Three.js 快速入门教程【六…

无人机仿真、感知、规划

文章目录 1.仿真环境1.1 博客教学1.2 教学视频1基础无人机仿真教学视频介绍2 XTDrone无人机仿真与控制技术全面教程3 ROS机器人集群仿真与实践教程 1.3 开源项目及插件1 ROS2-Gazebo Drone Simulation Plugin2 RotorS_UAV_Gazebo_Simulator3 自主无人机与Aruco导航教程4 基于 A…

php文件包含

文章目录 基础概念php伪协议什么是协议协议的格式php中的协议file协议http协议ftp协议php://input协议php://filter协议php://data协议 php文件上传机制高级文件包含nginx文件日志包含临时文件包含session文件包含pear文件包含远程文件包含 基础概念 文件包含&#xff0c;相当…

【超详细】神经网络的可视化解释

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…

微软CEO-纳德拉访谈-AGI计划

在与知名科技播客主播 Dwarkesh Patel 的深度访谈中,微软 CEO 萨提亚・纳德拉围绕 AI、量子计算、微软发展等多方面分享深刻见解,下面是针对访谈内容的介绍,其中还是有很多值得我们学习的地方。 1 AI 领域见解 影响力评估:纳德拉直言行业所标榜的 AGI 里程碑是 “无意义的基…

HAProxy介绍与编译安装

目录 1、HAProxy介绍 2、HAProxy编译安装 Centos 基础环境 Ubuntu 基础环境 编译安装HAProxy 验证HAProxy版本 HAProxy启动脚本 配置文件 启动haproxy 验证haproxy状态 查看haproxy的状态页面 1、HAProxy介绍 HAProxy是法国开发者 威利塔罗(Willy Tarreau) 在2000年…

细说STM32F407单片机2个ADC使用DMA同步采集各自的1个输入通道的方法

目录 一、示例说明 二、工程配置 1、RCC、DEBUG、CodeGenerator 2、USART6 3、TIM3 &#xff08;1&#xff09;Mode &#xff08;2&#xff09;参数设置 &#xff08;3&#xff09; TRGO &#xff08;4&#xff09;ADC1_IN0 1&#xff09;ADCs_Common_Settings 2&a…

从零开始用react + tailwindcs + express + mongodb实现一个聊天程序(一)

项目包含5个模块 1.首页 (聊天主页) 2.注册 3.登录 4.个人资料 5.设置主题 一、配置开发环境 建立项目文件夹 mkdir chat-project cd chat-project mkdir server && mkdir webcd server npm init cd web npm create vitelatest 创建前端项目时我们选择javascrip…

idea从远程gitee拉取项目

文章目录 从gitee上面拿到项目地址填写远程地址,并且设置项目保存位置拉取成功 从gitee上面拿到项目地址 填写远程地址,并且设置项目保存位置 拉取成功

大数据学习之PB级音乐数据中心数仓综合项目(1)-理论知识和项目需求、歌曲热度与歌手热度排行

一、理论知识和项目需求 1.课程介绍 2.数据库与ER建模_数据库三范式 3.数据库与ER建模_ER实体关系模型 4.数据库与维度建模_数据仓库(DATA WAREHOUSE) 5.数据库与维度建模_数据库与数据仓库区别 6.数据库与维度建模_数据仓库的发展历程 7.数据库与维度建模_维度建模 8.数据库与…

数据结构之队列

1. 队列的概念及结构 1.1 队列的概念 队列&#xff1a;只允许在一段进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出FIFO(First In First Out) 入队列&#xff1a;进行插入操作的一端称为队尾 出队列&#xff1a;进行删除操…

计算机网络-面试总结

计算机网络 从输入一个URL到页面加载完成的过程 整体流程 DNS查询过程SSL四次握手HTTP 的长连接与短连接 HTTP 的 GET 和 POST 区别浏览器访问资源没有响应&#xff0c;怎么排查? OSI七层参考模型 TCP/IP四层参考模型比较 TCP/IP 参考模型与 OSI 参考模型 TCP三次握手&四…

kafka消费能力压测:使用官方工具

背景 在之前的业务场景中&#xff0c;我们发现Kafka的实际消费能力远低于预期。尽管我们使用了kafka-go组件并进行了相关测试&#xff0c;测试情况见《kafka-go:性能测试》这篇文章。但并未能准确找出消费能力低下的原因。 我们曾怀疑这可能是由我的电脑网络带宽问题或Kafka部…

正式页面开发-登录注册页面

整体路由设计&#xff1a; 登录和注册的切换是切换组件或者是切换内容&#xff08;v-if和 v-else)&#xff0c;因为点击两个之间路径是没有变化的。也就是登录和注册共用同一个路由。登录是独立的一级路由。登录之后进到首页&#xff0c;有三个大模块&#xff1a;文章分类&…

Oracle 深入理解Lock和Latch ,解析访问数据块全流程

Oracle 锁机制介绍 根据保护对象的不同&#xff0c;单实例Oracle数据库锁可以分为以下几大类&#xff1a; DML lock&#xff08;data locks&#xff0c;数据锁&#xff09;&#xff1a;用于保护数据的完整性&#xff1b; DDL lock&#xff08;dictionary locks&#xff0c;字典…

Codes 开源免费研发项目管理平台 2025年第一个大版本3.0.0 版本发布及创新的轻IPD实现

Codes 简介 Codes 是国内首款重新定义 SaaS 模式的开源项目管理平台&#xff0c;支持云端认证、本地部署、全部功能开放&#xff0c;并且对 30 人以下团队免费。它通过创新的方式简化研发协同工作&#xff0c;使敏捷开发更易于实施。并提供低成本的敏捷开发解决方案&#xff0…

aws(学习笔记第二十九课) aws cloudfront hands on

aws(学习笔记第二十九课) 使用aws cloudfront 学习内容&#xff1a; 什么是aws cloudfront练习使用aws cloudfront 1. 什么是aws cloudfront aws cloudfront的整体架构 这里可以看出&#xff0c;aws引入了edge location的概念&#xff0c;用户的client与edge location进行是…

写大论文的word版本格式整理,实现自动生成目录、参考文献序号、公式序号、图表序号

前情提要&#xff1a;最近开始写大论文&#xff0c;发现由于内容很多导致用老方法一个一个改的话超级麻烦&#xff0c;需要批量自动化处理&#xff0c;尤其是序号&#xff0c;在不断有增添删减的情况时序号手动调整很慢也容易出错&#xff0c;所以搞一个格式总结&#xff0c;记…