day01 -- MybatisPlus

1. MybatisPlus简介

有基础的同学可结合资源中的代码一起看
MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生

特性

通用的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

2. MybatisPlus快速入门

基于资源中提供的项目,实现下列功能:
新增用户功能
根据id查询用户
根据id批量查询用户
根据id更新用户
根据id删除用户

2.1 MybatisPlus常见注解

@TableName:用来指定表名
@TableId:用来指定表中的主键字段信息

  • IdType枚举
    • AUTO:数据库自增长
    • INPUT:通过set方法自行输入
    • ASSIGN_ID:分配 ID,接口IdentifierGenerator的方法nextId来生成id,默认实现类为DefaultIdentifierGenerator雪花算法

@TableField:用来指定表中的普通字段信息

  • 常见场景:
    • 成员变量名与数据库字段名不一致
    • 成员变量名以is开头,且是布尔值
    • 成员变量名与数据库关键字冲突
    • 成员变量不是数据库字段

MybatisPlus是如何获取实现CRUD的数据库表信息的?
默认以类名驼峰转下划线作为表名
默认把名为id的字段作为主键
默认把变量名驼峰转下划线作为表的字段名

2.2 MybatisPlus常见配置

mybatis-plus:# 别名扫描包type-aliases-package: com.example.mybatisplus.domain.po;# Mapper xml 文件位置mapper-locations: "classpath*:mapper/**/*.xml"configuration:# 配置驼峰命名map-underscore-to-camel-case: true# 配置二级缓存cache-enabled: falseglobal-config:db-config:# id用雪花算法生成id-type: assign_id# 更新策略 : 只更新非空字段update-strategy: not_null

2.3 MybatisPlus入门案例

  1. 引入MybatisPlus的起步依赖
 <!-- mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.6</version></dependency>
  1. 定义Mapper
    自定义的Mapper继承MybatisPlus提供的BaseMapper接口
@Mapper
public interface UserMapper extends BaseMapper<User> {}
  1. 定义实体类
@Data
@TableName(value = "tb_user")
public class User {/*** 用户id*/@TableId(value="id",type = IdType.AUTO)//mp默认采用雪花算法增加private Long id;/*** 用户名*/@TableField("username")private String username;/*** 密码*/private String password;/*** 注册手机号*/private String phone;/*** 详细信息*/private String info;/*** 使用状态(1正常 2冻结)*/private Integer status;/*** 账户余额*/private Integer balance;/*** 创建时间*/private LocalDateTime createTime;/*** 更新时间*/private LocalDateTime updateTime;/*** 性别(数据中没有的字段)*/@TableField(exist = false)private Integer sex;}
  1. 定义Service接口
public interface IUserService extends IService<User> {}
  1. 定义Service实现
@Service
public class IUserServiceImpl extends ServiceImpl<UserMapper,User> implements IUserService {}
  1. 定义Controller
@RequestMapping("/users")
@RestController
public class UserController {@AutoWiredprivate final IUserService userService;@PostMappingpublic void saveUser(@RequestBody User user) {userService.save(user);}@GetMapping("{id}")public User queryUserById(@PathVariable("id") Long id) {return userService.getById(id);}@GetMappingpublic List<User> queryUserList(@RequestParam("ids") List<Integer> ids) {return userService.listByIds(ids);}@PutMappingpublic void updateUser(@RequestBody User user) {userService.updateById(user);}@DeleteMapping("{id}")public boolean deleteUserById(@PathVariable("id") Long id) {return userService.removeById(id);}
}

3. MybatisPlus核心功能

3.1 条件构造器

  • QueryWrapper和LambdaQueryWrapper通常用来构建select、delete、update的where条件部分
  • UpdateWrapper和LambdaUpdateWrapper通常只有在set语句比较特殊才使用
  • 尽量使用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码

例:

    @Test // 查询出名字带o,存款大于1000元的人的id,username,info,balancevoid testQueryWrapper() {//1.构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<User>().select("id","username","info","balance").like("username","o").ge("balance",1000);//2.查询,打印userMapper.selectList(wrapper).forEach(System.out::println);}@Test // 查询出名字带o,存款大于1000元的人的id,username,info,balancevoid testLambdaQueryWrapper() {//1.构建查询条件LambdaQueryWrapper<User> wrapper = new QueryWrapper<User>().lambda().select(User::getId,User::getUsername,User::getInfo,User::getBalance).like(User::getUsername,"o").ge(User::getBalance,1000);//2.查询,打印userMapper.selectList(wrapper).forEach(System.out::println);}@Test // 更新用户名为Jack的余额为2000void testUpdateQueryWrapper() {// 1.更新的数据User user = new User();user.setBalance(2000);// 2.更新的条件QueryWrapper<User> wrapper  = new QueryWrapper<User>().eq("username","Jack");userMapper.update(user,wrapper);}@Test // 为id为1,2,4的用户余额-200void testUpdateListWrapper() {// 1.更新的条件UpdateWrapper<User> wrapper = new UpdateWrapper<User>().setSql("balance = balance - 200").in("id",List.of(1L,2L,4L));// 2.执行更新userMapper.update(null,wrapper);}

代码生成器

3.2 自定义SQL

我们可以利用MyBatisPlus的Wrapper来构建复杂的Where条件,然后自己定义SQL语句中剩下的部分
基于Wrapper构建where条件

    @Test // 为id为1,2,4的用户余额-200void testCustomSQLUpdate() {//自定义sql(结合mp)// 1.更新的条件List<Long> list = List.of(1L, 2L, 4L);int amount = 200;// 2。定义条件QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id",list);// 3.调用自定义方法userMapper.updateBalanceByIds(wrapper,amount);}

在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew

@Mapper
public interface UserMapper extends BaseMapper<User> {void updateBalanceByIds(@Param("ew") QueryWrapper<User> wrapper, @Param("amount") int amount);
}

自定义SQL,并使用Wrapper条件

    <update id="updateBalanceByIds">UPDATE tb_user SET balance = balance - #{amount} ${ew.customSqlSegment}</update>

4.扩展功能

4.1 代码生成

mybatis提供插件实现通用代码的自动生成有mybatis X等
如下下面这个插件
在这里插入图片描述
先配置数据库(一定要精确到具体的数据库)
在这里插入图片描述
再根据具体的数据表生成代码
在这里插入图片描述

4.2 静态工具类 – Db

    @Overridepublic UserVO getUserAndAddressById(Long id) {//1.查询用户User user = this.getById(id);if(user == null || user.getStatus() == UserStatus.FROZEN){throw new RuntimeException("用户不存在或状态异常");}//2.查询用户地址List<Address> addresses = Db.lambdaQuery(Address.class).eq(Address::getUserId, user.getId()).list();// 3.封装VO// 3.1 转UserPO为VOUserVO userVO = BeanUtil.copyProperties(user, UserVO.class);// 3.2 转地址VOif(CollUtil.isNotEmpty(addresses)) {userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));}return userVO;}@Overridepublic List<UserVO> getUserAndAddressByIds(List<Long> ids) {// 1.查询用户List<User> users = listByIds(ids);if(CollUtil.isEmpty(users)){//用户不存在,换回空集合return Collections.emptyList();}// 2. 查询用户地址// 2.1 获取已查询用户idList<Long> collect = users.stream().map(User::getId).collect(Collectors.toList());// 2.2 查询用户地址List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, collect).list();// 2.3 转换成地址VOList<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);// 2.4 用户地址集合分组处理,相同用户放入一个集合(组)中Map<Long, List<AddressVO>> listMap = new HashMap<>(0);if(CollUtil.isNotEmpty(addressVOList)){listMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));}// 3. 封装VO返回List<UserVO> userVOS = new ArrayList<>(users.size());for (User user : users) {// 3.1 转UserPO为VOUserVO userVO = BeanUtil.copyProperties(user, UserVO.class);// 3.2 获取用户地址userVO.setAddresses(listMap.get(user.getId()));// 3.3 添加到集合中userVOS.add(userVO);}return userVOS;}

4.3 逻辑删除

方式一

在原来的常见配置中添加逻辑删除字段

mybatis-plus:# 别名扫描包type-aliases-package: com.example.mybatisplus.domain.po;# Mapper xml 文件位置mapper-locations: "classpath*:mapper/**/*.xml"configuration:# 配置驼峰命名map-underscore-to-camel-case: true# 配置二级缓存cache-enabled: falseglobal-config:db-config:# id用雪花算法生成id-type: assign_id# 更新策略 : 只更新非空字段update-strategy: not_null# 逻辑删除字段logic-delete-field: deleted
方式二

在实体类的逻辑删除的字段上添加 @TableLogic注释

@TableLogic
private Boolean deleted;

任选一种即可

4.4 枚举处理器

  1. 创建枚举类
package com.example.mybatisplus.enums;import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;@Getter
public enum UserStatus {NORMAL(1,"正常"),FROZEN(2,"冻结"),;@EnumValue //对应数据库中的字段类型private Integer value;@JsonValue //在做json处理时(前端返回时)返回下面属性,不加默认枚举字段(@JsonValue注解desc,那么整个序列化结果就是desc的值)private String desc;UserStatus(Integer value, String desc) {this.value = value;this.desc = desc;}
}
  1. 配置全局枚举处理器
mybatis-plus:# 别名扫描包type-aliases-package: com.example.mybatisplus.domain.po;# Mapper xml 文件位置mapper-locations: "classpath*:mapper/**/*.xml"configuration:# 配置驼峰命名map-underscore-to-camel-case: true# 配置二级缓存cache-enabled: false# 配置枚举处理器default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.CompositeEnumTypeHandlerglobal-config:db-config:# id用雪花算法生成id-type: assign_id# 更新策略 : 只更新非空字段update-strategy: not_null# 逻辑删除字段logic-delete-field: deleted
  1. 在实体类中修改成枚举
    /*** 使用状态(1正常 2冻结)*/private UserStatus status;

4.5 JSON处理器

JSON没有统一全局字段,上面的枚举处理器就有全局字段
在这里插入图片描述
Java默认使用JacksonTypeHandler

  1. 配置类的属性添加注解
    在实体类上的属性上添加注解,配置开启json处理器自动转换
    /*** 详细信息*/@TableField(value = "info",typeHandler = JacksonTypeHandler.class)//解决指定数据库字段名与实体类属性名不一致问题,开启json处理器自动转换private UserInfo info;
  1. 配置类添加注解
@Data
@TableName(value = "tb_user",autoResultMap = true)//开启自动映射
public class User {
...
}

3.JSON对象的类

@Data
@NoArgsConstructor
@AllArgsConstructor(staticName = "of")
public class UserInfo {private Integer age;private String intro;private String gender;
}

5.插件

在这里插入图片描述

5.1 分页插件

  1. 在配置类中注册MyBatisPlus的核心插件,同时添加分页插件
package com.example.mybatisplus.config;import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyBatisConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 创建MybatisPlusInterceptor对象PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);// 设置数据库类型paginationInnerInterceptor.setMaxLimit(1000L);// 设置最大单页限制数量,默认 500 条,-1 不受限制interceptor.addInnerInterceptor(paginationInnerInterceptor);// 添加分页插件return interceptor;}
}
  1. 通过构建page对象去查询
@Testvoid testPageQuery() {//1.准备分页// 1.1 分页条件int pageNo = 1,pageSize = 2;Page<User> page = Page.of(pageNo, pageSize);//1.2 排序条件page.addOrder(OrderItem.desc("balance"));page.addOrder(OrderItem.asc("id"));// 2. 分页查询Page<User> userPage = iUserService.page(page);// 3.解析Long total = userPage.getTotal();System.out.println("total = " + total);long pages = userPage.getPages();System.out.println("pages = " + pages);List<User> records = userPage.getRecords();records.forEach(System.out::println);}

业务层实现基本如下:

    @Override//分页public PageDTO<UserVO> queryUserPage(UserQuery userQuery) {String name = userQuery.getName();Integer status = userQuery.getStatus();Integer pageNo = userQuery.getPageNo();Integer pageSize = userQuery.getPageSize();String sortBy = userQuery.getSortBy();Boolean isAsc = userQuery.getIsAsc();//1.构建查询条件Page<User> page = Page.of(pageNo, pageSize);if(StrUtil.isNotBlank(sortBy) && isAsc != null) {page.addOrder(new OrderItem().setAsc(isAsc).setColumn(sortBy));}else {// 默认按照更新时间排序page.addOrder(new OrderItem().setAsc(false).setColumn("update_time"));}// 2.分页查询Page<User> userPage = lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).page(page);//3.封装VOPageDTO<UserVO> dto = new PageDTO<>();dto.setTotal(userPage.getTotal());// 总条数dto.setPages(userPage.getPages());// 总页数List<User> records = userPage.getRecords();// 当前页数据if(CollUtil.isEmpty(records)){dto.setList(Collections.emptyList());return dto;}dto.setList(BeanUtil.copyToList(records, UserVO.class));// 4.返回return dto;};

5.2 通用分页实体

上面page显示可以封装,请求对象也可以封装

  1. 在PageDTO中定义方法,将MyBatisPlus中的Page结果转为PageDTO结果
package com.example.mybatisplus.domain.dto;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "分页结果")
public class PageDTO<V> {@ApiModelProperty(value = "总记录数")private Long total;@ApiModelProperty(value = "总页数")private Long pages;@ApiModelProperty(value = "数据列表")private List<V> list;/*** 返回空分页结果* @param p MybatisPlus的分页结果* @param <V> 目标VO类型* @param <P> 原始PO类型* @return VO的分页对象*/public static <V, P> PageDTO<V> empty(Page<P> p){return new PageDTO<>(p.getTotal(), p.getPages(), Collections.emptyList());}/*** 将MybatisPlus分页结果转为 VO分页结果* @param p MybatisPlus的分页结果* @param voClass 目标VO类型的字节码* @param <V> 目标VO类型* @param <P> 原始PO类型* @return VO的分页对象*/public static <V, P> PageDTO<V> of(Page<P> p, Class<V> voClass) {// 1.非空校验List<P> records = p.getRecords();if (records == null || records.size() <= 0) {// 无数据,返回空结果return empty(p);}// 2.数据转换List<V> vos = BeanUtil.copyToList(records, voClass);// 3.封装返回return new PageDTO<>(p.getTotal(), p.getPages(), vos);}/*** 将MybatisPlus分页结果转为 VO分页结果,允许用户自定义PO到VO的转换方式* @param p MybatisPlus的分页结果* @param convertor PO到VO的转换函数* @param <V> 目标VO类型* @param <P> 原始PO类型* @return VO的分页对象*/public static <V, P> PageDTO<V> of(Page<P> p, Function<P, V> convertor) {// 1.非空校验List<P> records = p.getRecords();if (records == null || records.size() <= 0) {// 无数据,返回空结果return empty(p);}// 2.数据转换List<V> vos = records.stream().map(convertor).collect(Collectors.toList());// 3.封装返回return new PageDTO<>(p.getTotal(), p.getPages(), vos);}
}
  1. 在PageQuery中定义方法,将PageQuery对象转为MyBatisPlus中的Page对象
package com.example.mybatisplus.domain.query;import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.mybatisplus.domain.po.User;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {@ApiModelProperty(value = "页码", example = "1")private Integer pageNo;@ApiModelProperty(value = "每页条数", example = "2")private Integer pageSize;@ApiModelProperty(value = "排序字段", example = "id")private String sortBy;@ApiModelProperty(value = "是否升序", example = "true")private Boolean isAsc;// PageQuery转pagepublic <T>  Page<T> toMpPage(OrderItem ... items){Page<T> page = Page.of(pageNo, pageSize);if(StrUtil.isNotBlank(sortBy) && isAsc != null) {page.addOrder(new OrderItem().setAsc(isAsc).setColumn(sortBy));}else if(items != null){// 默认按照排序page.addOrder(items);}return page;}// 按照某个条件排序public <T> Page<T> toMpPage(String defaultSortBy, boolean isAsc){
//        return this.toMpPage(new OrderItem(defaultSortBy, isAsc));return this.toMpPage(new OrderItem());}// 默认排序(按照创建时间降序)public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() {return toMpPage("create_time", false);}// 默认排序(按照更新时间降序)public <T> Page<T> toMpPageDefaultSortByUpdateTimeDesc() {return toMpPage("update_time", false);}
}
@Data
@EqualsAndHashCode(callSuper = true)
public class UserQuery extends PageQuery{@ApiModelProperty("用户名关键字")private String name;@ApiModelProperty("用户状态:1-正常,2-冻结")private Integer status;@ApiModelProperty("余额最小值")private Integer minBalance;@ApiModelProperty("余额最大值")private Integer maxBalance;
}

具体分页实现

    @ApiOperation("根据条件分页查询用户接口")@GetMapping("/page")public PageDTO<UserVO> queryUserPage(@ApiParam("分页请求参数") UserQuery userQuery) {return userService.queryUserPage(userQuery);}
 PageDTO<UserVO> queryUserPage(UserQuery userQuery);
    @Override//分页public PageDTO<UserVO> queryUserPage(UserQuery userQuery) {String name = userQuery.getName();Integer status = userQuery.getStatus();Page<User> page = userQuery.toMpPageDefaultSortByUpdateTimeDesc();// 2.分页查询Page<User> userPage = lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).page(page);// 3.返回return PageDTO.of(userPage, user -> {//1.拷贝基础属性UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);//2.处理特殊逻辑(如隐藏名字后两位)userVO.setUsername(user.getUsername().substring(0, user.getUsername().length() - 2) + "**");return userVO;});};

注:数据的批量加入需要数据库开启rewriteBatchedStatements=true参数,否则数据条条插入与批量插入差距不大

    url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true

测试:
条条插入 vs 批量插入

  private User buildUser(int i) {User user = new User();user.setUsername("user_"+i);user.setPassword("123");user.setPhone("18688990011");user.setBalance(200);
//        user.setInfo("{\"age\": 24, \"intro\": \"国文老师\", \"gender\": \"female\"}");user.setInfo(UserInfo.of(24,"国文老师", "female"));user.setCreateTime(LocalDateTime.now());user.setUpdateTime(LocalDateTime.now());return user;}@Testvoid testInsertOne() {//一条条插入long b = System.currentTimeMillis();for(int i = 0; i < 100000; i++) {iUserService.save(buildUser(i));}long e = System.currentTimeMillis();System.out.println("e - b = " + (e - b));}@Testvoid testInsertBatch() {List<User> list = new ArrayList<>(1000);long b = System.currentTimeMillis();for(int i = 1; i < 10000; i++) {list.add(buildUser(i));if(i % 1000 == 0) {iUserService.saveBatch(list);list.clear();}}long e = System.currentTimeMillis();System.out.println("e - b = " + (e - b));

记录:刚学没多久感觉都忘得差不多了
加油!

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

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

相关文章

私有化部署大模型最佳解决方案 Ollama (8B)模型

私有化部署大模型Ollama 为什么需要私有化部署大模型一、Ollama本地部署Llama3大模型二、Langchain4j调用Ollama本地部署模型API三、Ollama本地部署nomic向量模型四、Spring AI调用Ollama本地部署模型API 为什么需要私有化部署大模型 企业考虑成本和数据隐私问题&#xff0c;会…

021_Thermal_Transient_in_Matlab统一偏微分框架之热传导问题

Matlab求解有限元专题系列 固体热传导方程 固体热传导的方程为&#xff1a; ρ C p ( ∂ T ∂ t u t r a n s ⋅ ∇ T ) ∇ ⋅ ( q q r ) − α T d S d t Q \rho C_p \left( \frac{\partial T}{\partial t} \mathbf{u}_{\mathtt{trans}} \cdot \nabla T \right) \nab…

BM算法(手算版)

BM 算法 BM 算法是一种字符串匹配的算法。 与 KMP 相比&#xff0c;BM 算法不扫描全部输入字符&#xff0c;平均匹配时间 c・n, 常量 c <1 (随机或真实文本), 但最坏情况是 O (n・m). 可以将 BM 算法的最坏情况改进到 O (n)&#xff1a;通过记录文本后缀中最…

计算机系统简介

一、计算机的软硬件概念 1.硬件&#xff1a;计算机的实体&#xff0c;如主机、外设、硬盘、显卡等。 2.软件&#xff1a;由具有各类特殊功能的信息&#xff08;程序&#xff09;组成。 系统软件&#xff1a;用来管理整个计算机系统&#xff0c;如语言处理程序、操作系统、服…

群晖前面加了雷池社区版,安装失败,然后无法识别出用户真实访问IP

有nas的相信对公网都不模式&#xff0c;在现在基础上传带宽能有100兆的时代&#xff0c;有公网代表着家里有一个小服务器&#xff0c;像百度网盘&#xff0c;优酷这种在线服务都能部署为私有化服务。但现在运营商几乎不可能提供公网ip&#xff0c;要么自己买个云服务器做内网穿…

通过github创建自己网页链接的方法

文章目录 要使用GitHub创建静态网页链接&#xff0c;可以按照以下详细步骤进行操作&#xff1a;一、准备阶段二、创建仓库并配置三、准备并上传静态网站文件四、配置GitHub Pages五、访问和更新你的静态网页 要使用GitHub创建静态网页链接&#xff0c;可以按照以下详细步骤进行…

uniapp微信小程序调用百度OCR

uniapp编写微信小程序调用百度OCR 公司有一个识别行驶证需求&#xff0c;调用百度ocr识别 使用了image-tools这个插件&#xff0c;因为百度ocr接口用图片的base64 这里只是简单演示&#xff0c;accesstoken获取接口还是要放在服务器端&#xff0c;不然就暴露了自己的百度项目k…

Xshell使用密钥远程登录Ubuntu 22.04报错:所选的用户密钥未在远程主机上注册。请再试一次

报错截图如下&#xff1a; 问题原因&#xff1a; Ubuntu 22.04 不支持 Xshell使用的私钥。 查看系统支持的私钥&#xff1a;sudo sshd -T | egrep "pubkey" ~$ sudo sshd -T | egrep "pubkey" pubkeyauthentication yes pubkeyacceptedalgorithms ssh-ed…

基于SpringBoot+Vue的旅游服务平台【提供源码+答辩PPT+参考文档+项目部署】

&#x1f4a5; ① 前言&#xff1a;这两年毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的JavaWeb项目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff01; ❗② 如何解决这类问题&#xff1f; 让我们能够顺利通过毕业&#xff0c;我也一直在不断思考、…

ROS 的 urdf 中 link 和 joint 的子标签中 origin 的含义

主要参考文章——主要文章&#xff0c;官方关于urdf的介绍和官方文档的翻译解析 link标签里面的origin含义 link标签里面有三个主要的子标签&#xff0c;分别是visual——连杆的外观和坐标系&#xff0c;collisoin——连杆的碰撞属性和inertial——连杆的惯性设置 首先&…

【AIGC】AI如何匹配RAG知识库: Embedding实践,语义搜索

引言 RAG作为减少模型幻觉和让模型分析、回答私域相关知识最简单高效的方式&#xff0c;我们除了使用之外可以尝试了解其是如何实现的。在实现RAG的过程中Embedding是非常重要的手段。本文将带你简单地了解AI工具都是如何通过Embedding去完成语义分析匹配的。 Embedding技术简…

低空经济发展迅猛,无人机设计制造技术详解

低空经济的迅猛发展&#xff0c;为无人机设计制造技术带来了新的机遇和挑战。无人机作为低空经济中的重要组成部分&#xff0c;其设计制造技术直接关系到无人机的性能、安全性和应用场景的拓展。以下是对无人机设计制造技术的详细解析&#xff1a; 一、无人机设计技术 1. 气动…

【HTML + CSS 魔法秀】打造惊艳 3D 旋转卡片

HTML结构 box 类是整个组件的容器。item-wrap 类是每个旋转卡片的包装器&#xff0c;每个都有一个内联样式–i&#xff0c;用于控制动画的延迟。item类是实际的卡片内容&#xff0c;包含一个图片。 <template><div class"box"><div class"item…

STM32L010F4 最小系统设计

画一个 STM32L010F4 的测试板子...... by 矜辰所致前言 最近需要用到一个新的 MCU&#xff1a; STM32L010F4 &#xff0c;上次测试的 VL53L0X 需要移植到这个芯片上&#xff0c;网上一搜 STM32L010F4&#xff0c;都是介绍资料&#xff0c;没有最小系统&#xff0c;使用说明等。…

计算生物学与生物信息学漫谈-1-测序一路走来

最近工作中&#xff0c;反思自己计算生物学基础非常薄弱&#xff0c;然而作为一门非常新兴的交叉学科&#xff0c;涉及计算机、物理、生物、数学等多多学科&#xff0c;国内并没有这样完善的教程&#xff0c;因此想要自己做一个教程&#xff0c;使用费曼学习法学习&#xff0c;…

探讨淘宝商品 API 接口:运用及收益

在当今电子商务蓬勃发展的时代&#xff0c;淘宝作为全球领先的电商平台&#xff0c;拥有海量的商品资源和庞大的用户群体。而淘宝商品 API 接口的出现&#xff0c;为开发者和企业提供了一种强大的工具&#xff0c;能够实现对淘宝商品数据的高效获取和利用。本文将深入探讨淘宝商…

C语言 | Leetcode C语言题解之第492题构造矩形

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> constructRectangle(int area) {int w sqrt(1.0 * area);while (area % w) {--w;}return {area / w, w};} };

2024年PDF转JPG新趋势,4款常用编辑工具梳理,不容错过

嘿&#xff0c;大家好&#xff0c;我是你们的老朋友&#xff0c;今天咱们聊个超实用的技巧——把PDF文件变成JPG图片&#xff0c;这样分享起来就方便多了。不管是工作汇报、学习资料还是生活照片&#xff0c;这招都能让你事半功倍。 1. 福昕PDF编辑器 闪现 ✚ https://editor…

排序---java---黑马

排序算法 名称平均时间复杂度最好情况最坏情况空间复杂度稳定性冒泡排序 O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1) Y Y Y选择排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1) N N …

【JavaScript】网页交互的灵魂舞者

我的主页&#xff1a;2的n次方_ 1. JavaScript 的三种引入方式 引⼊⽅式 语法描述 ⽰例 ⾏内样式 直接嵌⼊到 html 元素内部 <input type"button" value"点我⼀下" οnclick"alert(haha)"> 内部样式 定义<script>标签&a…