微服务开发与实战Day01 - MyBatisPlus

一、微服务

概念:微服务是一种软件架构风格,它是以专注于单一职责的很多小型项目为基础,组合除复杂的大型应用。

课程安排:

https://www.bilibili.com/video/BV1S142197x7/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=26bfae91aa8be97ba3634570e2041c28

二、MybatisPlus

MyBatis-Plus 🚀 为简化开发而生

1. 快速入门

1.1 入门案例

需求:基于课前资料提供的项目,实现下列功能:

  • ①新增用户功能
  • ②根据id查询用户
  • ③根据id批量查询用户
  • ④根据id更新用户
  • ⑤根据id删除用户

(1)导入项目到IDEA

(2)执行SQL脚本

①启动SQL服务

②打开SQL命令行窗口,复制粘贴资料里的SQL脚本,执行

③在IDEA中打开数据库表

这里记得把项目中application.yaml中的数据库连接密码也改成自己的

(3)安装插件 - MyBatisx 

点击左侧的小鸟可以快速切换到XML文档

使用步骤:

1. 引入MyBatisPlus的起步依赖 - pom.xml

MyBatisPlus官方提供了starter,其中集成了MyBatis和MyBatisPlus的所有功能,并且实现了自动装配效果。因此,我们可以使用MyBatisPlus的starter代替MyBatis的starter:

<!--<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.1</version>
</dependency>-->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version>
</dependency>

2. 定义Mapper - userMapper.java

自定义的Mapper继承MyBatisPlus提供的BaseMapper,注意要指定泛形

package com.itheima.mp.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Mapper;@Mapper  // 不要忘记加上@Mapper注解
public interface UserMapper extends BaseMapper<User> {// ... ...   
}

如果在测试时遇到下面的问题,把pom.xml中的lombok依赖版本改为1.18.30(因为JDK22对应的lombok版本为1.18.30)

java: java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field 'com.sun.tools.javac.tree.JCTree qualid'

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version>
</dependency>

3. 删除src/main/resources/mapper/UserMapper.xml里的内容如下

4. 把UserMapperTest.java改成如下,亲测全部测试通过

package com.itheima.mp.mapper;import com.itheima.mp.domain.po.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.time.LocalDateTime;
import java.util.List;@SpringBootTest
class UserMapperTest {@Autowiredprivate UserMapper userMapper;@Testvoid testInsert() {User user = new User();user.setId(5L);user.setUsername("Lucy");user.setPassword("123");user.setPhone("18688990011");user.setBalance(200);user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");user.setCreateTime(LocalDateTime.now());user.setUpdateTime(LocalDateTime.now());userMapper.insert(user);}@Testvoid testSelectById() {User user = userMapper.selectById(5L);System.out.println("user = " + user);}@Testvoid testQueryByIds() {List<User> users = userMapper.selectBatchIds(List.of(1L, 2L, 3L, 4L));users.forEach(System.out::println);}@Testvoid testUpdateById() {User user = new User();user.setId(5L);user.setBalance(20000);userMapper.updateById(user);}@Testvoid testDeleteUser() {userMapper.deleteById(5L);}
}

1.2 常见注解

注解配置 | MyBatis-Plus

MyBatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库信息。

MyBatisPlus中比较常用的几个注解如下:

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

1.3 常见配置

使用配置 | MyBatis-Plus

MyBatisPlus的配置项继承了MyBatis原生配置和一些自己特有的配置。例如:

①修改application.yaml

spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 123456
logging:level:com.itheima: debugpattern:dateformat: HH:mm:ss
mybatis-plus:type-aliases-package: com.itheima.mp.domain.poglobal-config:db-config:id-type: auto

2. 核心功能

2.1 条件构造器

MyBatisPlus支持各种复杂的where条件,可以满足日常开发的所有需求。

案例1:基于QueryWrapper的查询

需求:

①查询出名字中带o的,存款大于等于1000元的人的id、username、info、balance字段

SELECT id, username, info, balance
FROM user
WHERE username LIKE ? > AND balance >= ?
    // 基于QueryWrapper@Testvoid testQueryWrapper() {// 1. 构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<User>().select("id", "username", "info", "balance").like("username", "o").ge("balance", 1000);List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);}
    // 基于LambdaQueryWrapper@Testvoid testLambdaQueryWrapper() {// 1. 构建查询条件LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().select(User::getId, User::getUsername, User::getInfo, User::getBalance).like(User::getUsername, "o").ge(User::getBalance, 1000);List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);}

②更新用户名为jack的用户的余额为2000

UPDATE userSET balance = 2000WHERE username = "jack"
    @Testvoid testUpdateByQueryWrapper() {// 1. 要更新的数据User user = new User();user.setBalance(2000);// 2. 更新的条件QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "jack");// 3. 执行更新userMapper.update(user, wrapper);}

案例2:基于UpdateWrapper的更新

需求:更新id为1,2,4的用户余额,扣200

UPDATE userSET balance = balance - 200WHERE id in (1, 2, 4)
    @Testvoid testUpdateWrapper() {List<Long> ids = List.of(1L, 2L, 4L);UpdateWrapper<User> wrapper = new UpdateWrapper<User>().setSql("balance = balance - 200").in("id", ids);userMapper.update(null, wrapper);}

总结

条件构造器的用法:

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

2.2 自定义SQL

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

案例1:自定义SQL

需求:将id在指定范围内的用户(如1、2、4)的余额扣减指定值

①基于Wrapper构建where条件

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

③自定义SQL,并使用Wrapper条件

2.3 Service接口

使用示例:

①自定义service接口继承 IService接口

IUserService.java

package com.itheima.mp.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.po.User;public interface IUserService extends IService<User> {
}

②自定义Service实现类,实现自定义接口并继承 ServiceImpl类

UserServiceImpl.java

package com.itheima.mp.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.mapper.UserMapper;
import com.itheima.mp.service.IUserService;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
}

IUserServiceTest.java

package com.itheima.mp.service;import com.itheima.mp.domain.po.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.time.LocalDateTime;
import java.util.List;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class IUserServiceTest {@Autowiredprivate IUserService userService;// 新增@Testvoid testSaveUser() {User user = new User();user.setUsername("Lily");user.setPassword("123");user.setPhone("18358995031");user.setBalance(20000);user.setInfo("{\"age\": 24, \"intro\": \"物理老师\", \"gender\": \"female\"}");user.setCreateTime(LocalDateTime.now());user.setUpdateTime(LocalDateTime.now());userService.save(user);}// 查询@Testvoid testQuery() {List<User> users = userService.listByIds((List.of(1L, 2L, 4L)));users.forEach(System.out::println);}}

案例1:基于Restful风格实现下列接口

需求:基于Restful风格实现下面的接口:

文档地址:Docs

①在项目中引入依赖 - pom.xml

<!--swagger-->
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi2-spring-boot-starter</artifactId><version>4.1.0</version>
</dependency>
<!--web-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>

②配置swagger信息 - application.yaml

knife4j:enable: trueopenapi:title: 用户管理接口文档description: "用户管理接口文档"email: zhanghuyi@itcast.cnconcat: 虎哥url: https://www.itcast.cnversion: v1.0.0group:default:group-name: defaultapi-rule: packageapi-rule-resources:- com.itheima.mp.controller

③UserController.java

package com.itheima.mp.controller;import cn.hutool.core.bean.BeanUtil;
import com.itheima.mp.domain.dto.UserFormDTO;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.service.IUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;import java.util.List;@Api(tags = "用户管理接口")
@RequestMapping("/users")
@RestController
@RequiredArgsConstructor
public class UserController {private final IUserService userService;@ApiOperation("新增用户接口")@PostMappingpublic void saveUser(@RequestBody UserFormDTO userDTO) {// 1. 把DTO拷贝到PO(hutool)User user = BeanUtil.copyProperties(userDTO, User.class);// 新增userService.save(user);}@ApiOperation("删除用户接口")@DeleteMapping("{id}")public void deleteUserById(@ApiParam("用户id") @PathVariable("id") Long id) {userService.removeById(id);}@ApiOperation("根据id查询用户接口")@GetMapping("{id}")public UserVO queryUserById(@ApiParam("用户id") @PathVariable("id") Long id) {// 1. 查询用户POUser user = userService.getById(id);// 2. 把PO拷贝到VOreturn BeanUtil.copyProperties(user, UserVO.class);}@ApiOperation("根据id批量查询用户接口")@GetMappingpublic List<UserVO> queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids) {// 1. 查询用户POList<User> users = userService.listByIds(ids);// 2. 把PO拷贝到VOreturn BeanUtil.copyToList(users, UserVO.class);}@ApiOperation("扣减用户余额接口")@PutMapping("/{id}/deduction/{money}")public void deductBalance(@ApiParam("用户id") @PathVariable("id") Long id,@ApiParam("扣减的金额") @PathVariable("money") Integer money) {userService.deductBalance(id, money);}
}

④IUserService.java

package com.itheima.mp.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.po.User;public interface IUserService extends IService<User> {void deductBalance(Long id, Integer money);
}

⑤UserServiceImpl.java

package com.itheima.mp.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.mapper.UserMapper;
import com.itheima.mp.service.IUserService;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic void deductBalance(Long id, Integer money) {// 1. 查询用户User user = getById(id);// 2. 校验用户状态if(user == null || user.getStatus() == 2) {throw new RuntimeException("用户状态异常!");}// 3. 校验用户余额是否充足if(user.getBalance() < money) {throw new RuntimeException("用户余额不足!");}// 4. 扣减余额baseMapper.deductBalance(id, money);}
}

⑥UserMapper.java

package com.itheima.mp.mapper;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;import java.util.List;@Mapper  // 不要忘记加上@Mapper注解
public interface UserMapper extends BaseMapper<User> {@Update("UPDATE tb_user SET balance = balance - #{money} WHERE id = #{id}")void deductBalance(@Param("ew") Long id, @Param("money") Integer money);
}

http://localhost:8080/doc.html#/home

案例2:IServiced的Lambda查询

需求:实现一个根据复杂条件查询用户的接口,查询条件如下:

  • name:用户名关键字,可以为空
  • status:用户状态,可以为空
  • minBalance:最小余额,可以为空
  • maxBalance:最大余额,可以为空

UserController.java

import com.itheima.mp.query.UserQuery;// ... ...@ApiOperation("根据复杂条件查询用户接口")@GetMapping("/list")public List<UserVO> queryUsers(UserQuery query) {// 1. 查询用户POList<User> users = userService.queryUsers(query.getName(), query.getStatus(), query.getMinBalance(), query.getMaxBalance());// 2. 把PO拷贝到VOreturn BeanUtil.copyToList(users, UserVO.class);}

UserQuery.java

package com.itheima.mp.query;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(description = "用户查询条件实体")
public class UserQuery {@ApiModelProperty("用户名关键字")private String name;@ApiModelProperty("用户状态:1-正常,2-冻结")private Integer status;@ApiModelProperty("余额最小值")private Integer minBalance;@ApiModelProperty("余额最大值")private Integer maxBalance;
}

IUserService.java

package com.itheima.mp.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.po.User;import java.util.List;public interface IUserService extends IService<User> {List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance);
}

UserServiceImpl.java

    @Overridepublic List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {return lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).ge(minBalance != null, User::getBalance, minBalance).le(maxBalance != null, User::getBalance, maxBalance).list();}

案例2:IService的Lambda更新

需求:改造根据id修改用户余额的接口,要求如下

①完成对用户状态校验

②完成对用户余额校验

③如果扣减后余额为0,则将用户status修改为冻结状态

UserServiceImpl.java

    @Override@Transactionalpublic void deductBalance(Long id, Integer money) {// 1. 查询用户User user = getById(id);// 2. 校验用户状态if(user == null || user.getStatus() == 2) {throw new RuntimeException("用户状态异常!");}// 3. 校验用户余额是否充足if(user.getBalance() < money) {throw new RuntimeException("用户余额不足!");}// 4. 扣减余额// baseMapper.deductBalance(id, money);int remainBalance = user.getBalance() - money;lambdaUpdate().set(User::getBalance, remainBalance).set(remainBalance == 0, User::getStatus, 2).eq(User::getId, id).eq(User::getBalance, user.getBalance())  // 乐观锁.update();}

案例3:IService批量新增

需求:批量插入1万条用户数据,并作出对比:

  • 普通for循环插入
  • IService的批量插入
    private User buildUser(int i) {User user = new User();user.setUsername("user_" + i);user.setPassword("123");user.setPhone("" + (18688190000L + i));user.setBalance(2000);user.setInfo("{\"age\": 24, \"intro\": \"语文老师\", \"gender\": \"female\"}");user.setCreateTime(LocalDateTime.now());user.setUpdateTime(LocalDateTime.now());return user;}@Testvoid testSaveOneByOne() {long b = System.currentTimeMillis();for (int i = 1; i <= 100000; i++) {userService.save(buildUser(i));}long e = System.currentTimeMillis();System.out.println("耗时:" + (e - b));}@Testvoid testSaveBatch() {// 我们每次批量插入1000条件,插入100次,即10万条数据// 1. 准备一个容量为1000的集合List<User> list = new ArrayList<>(1000);long b = System.currentTimeMillis();for (int i = 1; i <= 100000; i++) {// 2. 添加一个userlist.add(buildUser(i));// 3. 每隔1000条批量插入一次if(i % 1000 == 0) {userService.saveBatch(list);// 4. 清空集合,准备下一批数据list.clear();}}long e = System.currentTimeMillis();System.out.println("耗时:" + (e - b));}

测试完记得删除测试数据

开启rewriteBatchedStatements=true参数  - application.yaml

spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=truedriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 123456

3. 扩展功能

3.1 代码生成

代码生成器 | MyBatis-Plus

Mybatis X 插件 | MyBatis-Plus

MyBatisPlus插件装不了的直接进官网安装:MyBatisPlus - IntelliJ IDEs Plugin | Marketplace

①测试连接

②代码生成

注:在AddressServiceImpl里加上@Service注解

效果:

3.2 静态工具

案例1:静态工具查询

需求:

  • ①改造根据id查询用户的接口,查询用户的同时,查询除用户对应的所有地址
  • ②改造根据id批量查询用户的接口,查询用户的同时,查询出用户对应的所有地址

(1)改造根据id查询用户的接口,查询用户的同时,查询除用户对应的所有地址

出现service相互调用的情况。

①从Day01的资料里拷贝AddressVO.java到vo文件夹下,在UserVO.java中添加地址属性:

@ApiModelProperty("用户的收货地址")
private List<AddressVO> addresses;

②改写UserController.java下的 queryUserById方法

    @ApiOperation("根据id查询用户接口")@GetMapping("{id}")public UserVO queryUserById(@ApiParam("用户id") @PathVariable("id") Long id) {return userService.queryUserAndAddressById(id);}

③IUserService.java

UserVO queryUserAndAddressById(Long id);

④UserServiceImpl.java

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.toolkit.Db;// ... ...   @Overridepublic UserVO queryUserAndAddressById(Long id) {// 1. 查询用户User user = getById(id);if(user == null || user.getStatus() == 2) {throw new RuntimeException("用户状态异常!");}// 2. 查询地址List<Address> addresses = Db.lambdaQuery(Address.class).eq(Address::getUserId, id).list();// 3. 封装VO// 3.1 转User的PO为VOUserVO userVO = BeanUtil.copyProperties(user, UserVO.class);// 3.2 转地址VOif(CollUtil.isNotEmpty(addresses)) {userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));}return userVO;}

(2)改造根据id批量查询用户的接口,查询用户的同时,查询出用户对应的所有地址

①UserController.java

    @ApiOperation("根据id批量查询用户接口")@GetMappingpublic List<UserVO> queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids) {return userService.queryUserAndAddressByIds(ids);}

②IUserService.java

List<UserVO> queryUserAndAddressByIds(List<Long> ids);

③UserServiceImpl.java

    @Overridepublic List<UserVO> queryUserAndAddressByIds(List<Long> ids) {// 1. 查询用户List<User> users = listByIds(ids);if(CollUtil.isEmpty(users)) {return Collections.emptyList();}// 2. 查询地址// 2.1 获取用户id集合List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());// 2.2 根据用户id查询地址List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();// 2.3 转换地址voList<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);// 2.4 用户地址集合分组处理,相同用户的放入一个集合(组)中Map<Long, List<AddressVO>> addressMap = new HashMap<>(0);if(CollUtil.isNotEmpty(addressVOList)) {addressMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));}// 3. 转VO返回List<UserVO> list = new ArrayList<>(users.size());for (User user : users) {// 3.1 转换user的po为voUserVO vo = BeanUtil.copyProperties(user, UserVO.class);list.add(vo);// 3.2 转换地址vovo.setAddresses(addressMap.get(user.getId()));}return list;}

3.3 逻辑删除

逻辑删除就是基于代码逻辑模拟删除效果,但并不会真正删除数据。思路如下:

  • 在表中添加一个字段标记数据是否被删除
  • 当删除数据时把标记置为1
  • 查询时只查询标记为0的数据

例如,逻辑删除字段为deleted:

  • 删除操作:
UPDATE user SET deleted = 1 WHERE id = 1 AND deleted = 0
  • 查询操作:
SELECT * FROM user WHERE deleted = 0

MyBatisPlus提供了逻辑删除功能,无需改变方法调应的方式,而是在底层帮我们自动修改CRUD的语句。我们要做的就是在application.yaml文件中配置逻辑删除的字段名称和值即可:

mybatis-plus:global-config:db-config:id-type: autologic-delete-field: deleted  # 全局逻辑删除的实体字段名,字段类型可以是boolean、integerlogic-delete-value: 1  # 逻辑已删除值(默认为1)logic-not-delete-value: 0  # 逻辑未删除值(默认为0)

IAddressServiceTest.java

package com.itheima.mp.service;import com.itheima.mp.domain.po.Address;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class IAddressServiceTest {@Autowiredprivate IAddressService addressService;@Testvoid testLogicDelete() {// 1. 删除addressService.removeById(59L);// 2. 查询Address address = addressService.getById(59L);System.out.println("address = " + address);  // null}
}

注意:逻辑删除本身也有自己的问题,比如:

  • 会导致数据库表垃圾数据越来越多,影响查询效率
  • SQL中全都需要对逻辑删除字段做判断,影响查询效率

因此,不太推荐采用逻辑删除功能,如果数据不能删除,可以采用把数据迁移到其他表的方法。

3.4 枚举处理器

User类中有一个用户状态字段:

通用枚举

在application.yaml中配置全局枚举处理器,实现类型转换:

mybatis-plus:type-aliases-package: com.itheima.mp.domain.poconfiguration:default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler

UserStatus.java

package com.itheima.mp.enums;import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;@Getter
public enum UserStatus {NORMAL(1, "正常"),FROZEN(2, "冻结"),;@EnumValueprivate final int value;@JsonValueprivate final String desc;UserStatus(int value, String desc) {this.value = value;this.desc = desc;}
}

User.java

import com.itheima.mp.enums.UserStatus;/*** 账户余额*/private UserStatus balance;

UserVO.java

@ApiModelProperty("使用状态(1正常 2冻结)")
private UserStatus status;

把用到账户状态值的地方改成枚举变量 - UserServiceImpl.java

package com.itheima.mp.service.impl;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.itheima.mp.domain.po.Address;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.AddressVO;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.enums.UserStatus;
import com.itheima.mp.mapper.UserMapper;
import com.itheima.mp.service.IUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.*;
import java.util.stream.Collectors;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Override@Transactionalpublic void deductBalance(Long id, Integer money) {// 1. 查询用户User user = getById(id);// 2. 校验用户状态if (user == null || user.getStatus() == UserStatus.FROZEN) {throw new RuntimeException("用户状态异常!");}// 3. 校验用户余额是否充足if (user.getBalance() < money) {throw new RuntimeException("用户余额不足!");}// 4. 扣减余额// baseMapper.deductBalance(id, money);int remainBalance = user.getBalance() - money;lambdaUpdate().set(User::getBalance, remainBalance).set(remainBalance == 0, User::getStatus, UserStatus.FROZEN).eq(User::getId, id).eq(User::getBalance, user.getBalance())  // 乐观锁.update();}@Overridepublic List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {return lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).ge(minBalance != null, User::getBalance, minBalance).le(maxBalance != null, User::getBalance, maxBalance).list();}@Overridepublic UserVO queryUserAndAddressById(Long id) {// 1. 查询用户User user = getById(id);if(user == null || user.getStatus() == UserStatus.FROZEN) {throw new RuntimeException("用户状态异常!");}// 2. 查询地址List<Address> addresses = Db.lambdaQuery(Address.class).eq(Address::getUserId, id).list();// 3. 封装VO// 3.1 转User的PO为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> queryUserAndAddressByIds(List<Long> ids) {// 1. 查询用户List<User> users = listByIds(ids);if(CollUtil.isEmpty(users)) {return Collections.emptyList();}// 2. 查询地址// 2.1 获取用户id集合List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());// 2.2 根据用户id查询地址List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();// 2.3 转换地址voList<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);// 2.4 用户地址集合分组处理,相同用户的放入一个集合(组)中Map<Long, List<AddressVO>> addressMap = new HashMap<>(0);if(CollUtil.isNotEmpty(addressVOList)) {addressMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));}// 3. 转VO返回List<UserVO> list = new ArrayList<>(users.size());for (User user : users) {// 3.1 转换user的po为voUserVO vo = BeanUtil.copyProperties(user, UserVO.class);list.add(vo);// 3.2 转换地址vovo.setAddresses(addressMap.get(user.getId()));}return list;}
}

总结:

如何实现PO类中的枚举类型变量与数据库字段的转换?

①给枚举中的与数据库对应value值添加@EnumValue注解

②在配置文件中配置统一的枚举处理器,实现类型转换。

3.5 JSON处理器

①userInfo.java

package com.itheima.mp.domain.po;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor(staticName = "of")
public class UserInfo {private Integer age;private String intro;private String gender;
}

②User.java

package com.itheima.mp.domain.po;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 com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.itheima.mp.enums.UserStatus;
import lombok.Data;import java.time.LocalDateTime;@Data
@TableName(value = "tb_user", autoResultMap = true)
public class User {// ... .../*** 详细信息*/@TableField(typeHandler = JacksonTypeHandler.class)private UserInfo info;// ... ...
}

③设置用户信息改成

// user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
user.setInfo(UserInfo.of(24,"英文老师","female"));

④UserVO.java

@ApiModelProperty("详细信息")
private UserInfo info;

⑤UserFormDTO.java

@ApiModelProperty("详细信息,JSON风格")
private UserInfo info;

如果有出现下面的问题,把UserMapper.xml里的SQL语句改成如下,删除parameterType="com.itheima.mp.domain.po.User"

    <insert id="saveUser">INSERT INTO `tb_user` (`id`, `username`, `password`, `phone`, `info`, `balance`)VALUES (#{id}, #{username}, #{password}, #{phone}, #{info}, #{balance});</insert>

4. 插件功能

MyBatisPlus提供的内置拦截器有下面这些:

拦截器描述
TenantLineInnerInterceptor多租户插件
DynamicTableNameInnerInterceptor动态表名插件
PaginationInnerInterceptor分页插件
OptimisticLockerInnerInterceptor乐观锁插件
IllegalSQLInnerInterceptorSQL性能规范插件,检测并拦截垃圾SQL
BlockAttackInnerInterceptor防止全表更新和删除的插件

4.1 分页插件

①在配置类中注册MyBatisPlus的核心插件,同时添加分页插件:

package com.itheima.mp.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();// 1. 创建分页插件PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);paginationInnerInterceptor.setMaxLimit(1000L);  // 设置分页上限// 2. 添加分页插件interceptor.addInnerInterceptor(paginationInnerInterceptor);return interceptor;}
}

②使用分页的API

IUserServiceTest.java

    @Testvoid testPageQuery() {int pageNo = 1, pageSize = 2;// 1. 准备分页条件// 1.1 分页条件Page<User> page = Page.of(pageNo, pageSize);// 1.2 排序条件page.addOrder(new OrderItem("balance", true));page.addOrder(new OrderItem("id", true));// 2. 分页查询Page<User> p = userService.page(page);// 3. 解析long total = p.getTotal();System.out.println("total = " + total);long pages = p.getPages();System.out.println("pages = " + pages);List<User> users = p.getRecords();users.forEach(System.out::println);}

4.2 通用分页实体

案例1:简单分页查询案例

需求:遵循下面的接口规范,编写一个UserController接口,实现User的分页查询

①PageQuery.java

package com.itheima.mp.query;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {@ApiModelProperty("页码")private Integer pageNo;@ApiModelProperty("页大小")private Integer pageSize;@ApiModelProperty("排序字段")private String sortBy;@ApiModelProperty("是否升序")private Boolean isAsc;
}

②UserQuery继承PageQuery

package com.itheima.mp.query;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel(description = "用户查询条件实体")
public class UserQuery extends PageQuery{@ApiModelProperty("用户名关键字")private String name;@ApiModelProperty("用户状态:1-正常,2-冻结")private Integer status;@ApiModelProperty("余额最小值")private Integer minBalance;@ApiModelProperty("余额最大值")private Integer maxBalance;
}

③PageDTO.java

package com.itheima.mp.domain.dto;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.util.List;@Data
@ApiModel(description = "分页结果")
public class PageDTO<T> {@ApiModelProperty("总条数")private Long total;@ApiModelProperty("总页数")private Long pages;@ApiModelProperty("集合")private List<T> list;
}

④UserController.java

    @ApiOperation("根据复杂条件分页查询用户接口")@GetMapping("/page")public PageDTO<UserVO> queryUsersPage(UserQuery query) {return userService.queryUsersPage(query);}

⑤IUserService.java

PageDTO<UserVO> queryUsersPage(UserQuery query);

⑥UserSeerviceImpl.java

package com.itheima.mp.service.impl;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.itheima.mp.domain.dto.PageDTO;
import com.itheima.mp.domain.po.Address;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.AddressVO;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.enums.UserStatus;
import com.itheima.mp.mapper.UserMapper;
import com.itheima.mp.query.UserQuery;
import com.itheima.mp.service.IUserService;
import org.springframework.stereotype.Service;import java.util.*;
import java.util.stream.Collectors;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {// ... ...@Overridepublic PageDTO<UserVO> queryUsersPage(UserQuery query) {String name = query.getName();Integer status = query.getStatus();Integer minBalance = query.getMinBalance();Integer maxBalance = query.getMaxBalance();// 1. 构建查询条件// 1.1 分页条件Page<User> page = Page.of(query.getPageNo(), query.getPageSize());// 1.2 排序条件if(StrUtil.isNotBlank(query.getSortBy())) {// 排序字段不为空,按照排序字段排序page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));} else {// 排序字段为空,默认按照更新时间排序page.addOrder(new OrderItem("update_time", false));}// 2. 分页查询Page<User> p = lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).ge(minBalance != null, User::getBalance, minBalance).le(maxBalance != null, User::getBalance, maxBalance).page(page);// 3. 封装VO结果PageDTO<UserVO> dto = new PageDTO<>();// 3.1 总条数dto.setTotal(p.getTotal());// 3.2 总页数dto.setPages(p.getPages());// 3.3 当前页数据List<User> records = p.getRecords();if(CollUtil.isEmpty(records)) {dto.setList(Collections.emptyList());return dto;}// 3.4 拷贝user的VOdto.setList(BeanUtil.copyToList(records, UserVO.class));// 4. 返回return dto;}
}

案例2:通用分页实体

需求:

  • 在PageQuery中定义方法,将PageQuery对象转为MyBatisPlus中的page对象
  • 在PageDTO中定义方法,将MyBatisPlus中的Page结果转为PageDTO结果

①PageQuery.java

package com.itheima.mp.query;import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.mp.domain.po.User;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {@ApiModelProperty("页码")private Integer pageNo = 1;@ApiModelProperty("页大小")private Integer pageSize = 5;@ApiModelProperty("排序字段")private String sortBy;@ApiModelProperty("是否升序")private Boolean isAsc = true;public <T> Page<T> toMpPage(OrderItem ... items) {// 1 分页条件Page<T> page = Page.of(pageNo, pageSize);// 2 排序条件if(StrUtil.isNotBlank(sortBy)) {// 排序字段不为空,按照排序字段排序page.addOrder(new OrderItem(sortBy, isAsc));} else if(items != null) {// 排序字段为空,默认排序page.addOrder(items);}return page;}public <T> Page<T> toMpPage(String defaultSortBy, Boolean defaultAsc) {return toMpPage(new OrderItem(defaultSortBy, defaultAsc));}public <T> Page<T> toMpPageDefaultSortByCreateTime() {return toMpPage(new OrderItem("create_time", false));}public <T> Page<T> toMpPageDefaultSortByUpdateTime() {return toMpPage(new OrderItem("update_time", false));}
}

②PageDTO.java

package com.itheima.mp.domain.dto;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.UserVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;@Data
@ApiModel(description = "分页结果")
public class PageDTO<T> {@ApiModelProperty("总条数")private Long total;@ApiModelProperty("总页数")private Long pages;@ApiModelProperty("集合")private List<T> list;public static <PO, VO> PageDTO<VO> of(Page<PO> p, Class<VO> clazz) {PageDTO<VO> dto = new PageDTO<>();// 1 总条数dto.setTotal(p.getTotal());// 2 总页数dto.setPages(p.getPages());// 3 当前页数据List<PO> records = p.getRecords();if(CollUtil.isEmpty(records)) {dto.setList(Collections.emptyList());return dto;}// 4 拷贝user的VOdto.setList(BeanUtil.copyToList(records, clazz));// 5. 返回return dto;}public static <PO, VO> PageDTO<VO> of(Page<PO> p, Function<PO, VO> convertor) {PageDTO<VO> dto = new PageDTO<>();// 1 总条数dto.setTotal(p.getTotal());// 2 总页数dto.setPages(p.getPages());// 3 当前页数据List<PO> records = p.getRecords();if(CollUtil.isEmpty(records)) {dto.setList(Collections.emptyList());return dto;}// 4 拷贝user的VOdto.setList(records.stream().map(convertor).collect(Collectors.toList()));// 5. 返回return dto;}
}

③UserServiceImpl.java

package com.itheima.mp.service.impl;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.itheima.mp.domain.dto.PageDTO;
import com.itheima.mp.domain.po.Address;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.AddressVO;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.enums.UserStatus;
import com.itheima.mp.mapper.UserMapper;
import com.itheima.mp.query.UserQuery;
import com.itheima.mp.service.IUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.*;
import java.util.stream.Collectors;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic List<UserVO> queryUserAndAddressByIds(List<Long> ids) {// 1. 查询用户List<User> users = listByIds(ids);if(CollUtil.isEmpty(users)) {return Collections.emptyList();}// 2. 查询地址// 2.1 获取用户id集合List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());// 2.2 根据用户id查询地址List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();// 2.3 转换地址voList<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);// 2.4 用户地址集合分组处理,相同用户的放入一个集合(组)中Map<Long, List<AddressVO>> addressMap = new HashMap<>(0);if(CollUtil.isNotEmpty(addressVOList)) {addressMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));}// 3. 转VO返回List<UserVO> list = new ArrayList<>(users.size());for (User user : users) {// 3.1 转换user的po为voUserVO vo = BeanUtil.copyProperties(user, UserVO.class);list.add(vo);// 3.2 转换地址vovo.setAddresses(addressMap.get(user.getId()));}return list;}@Overridepublic PageDTO<UserVO> queryUsersPage(UserQuery query) {String name = query.getName();Integer status = query.getStatus();Integer minBalance = query.getMinBalance();Integer maxBalance = query.getMaxBalance();// 1. 构建查询条件Page<User> page = query.toMpPageDefaultSortByUpdateTime();// 2. 分页查询Page<User> p = lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).ge(minBalance != null, User::getBalance, minBalance).le(maxBalance != null, User::getBalance, maxBalance).page(page);// 3. 封装VO结果// return PageDTO.of(p, UserVO.class);// return PageDTO.of(p, user -> BeanUtil.copyProperties(user, UserVO.class));return PageDTO.of(p, user -> {// 1. 拷贝基础属性UserVO vo = BeanUtil.copyProperties(user, UserVO.class);// 2. 处理特殊逻辑vo.setUsername(vo.getUsername().substring(0, vo.getUsername().length() - 2) + "**");return vo;});}
}

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

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

相关文章

Vue3-Vite-ts 前端生成拓扑图vis,复制即用

完整代码&#xff0c;复制即可用&#xff0c;样式自调 试过 jointjs dagre-d3 vis&#xff0c;好用一点&#xff0c;可添加同层的双向箭头 方法1&#xff1a;Vis.js npm install vis-network <template><div id"mynetwork" class"myChart" :st…

通过DirectML和ONNXRuntime运行Phi-3模型

更多精彩内容&#xff0c;欢迎关注我的公众号“ONE生产力”&#xff01; 上篇我们讲到通过Intel Core Ultra系列处理器内置的NPU加速运行Phi-3模型&#xff0c;有朋友评论说他没有Intel处理器是否有什么办法加速Phi-3模型。通常&#xff0c;使用GPU特别是NVIDA的GPU加速AI模型…

混剪素材库有哪些?分享7个高质量混剪视频素材网站

作为自媒体创作者&#xff0c;我们经常需要高质量的混剪视频素材来吸引观众。今天&#xff0c;我将为大家介绍几个优质的视频素材网站&#xff0c;确保您的短视频制作既高效又充满创意。 蛙学府素材网 首推蛙学府素材网&#xff0c;这个平台真是创作者的福音。无论是短视频素材…

重磅消息! Stable Diffusion 3将于6月12日开源 2B 版本的模型,文中附候补注册链接。

在OpenAI发布Sora后&#xff0c;Stability AI也发布了其最新的模型Stabled Diffusion3, 之前的文章中已经和大家介绍过&#xff0c;感兴趣的小伙伴可以点击以下链接阅读。Sora是音视频方向&#xff0c;Stabled Diffusion3是图像生成方向&#xff0c;那么两者没有必然的联系&…

electron-Vue: Module parse failed: Unexpected character ‘ ‘

​ electron-Vue项目中&#xff0c;我自己写了一个node的C扩展&#xff08;xx.node&#xff09;&#xff0c;然后在.vue文件里import它&#xff0c;然后运行npm run electron:serve&#xff0c;报错如下: ​​ electron-Vue打包默认使用webpack&#xff0c;默认情况下webpack没…

盘点哪些企业容易被ddos攻击

DDoS&#xff08;分布式拒绝服务&#xff09;攻击已成为网络安全威胁中的重要一环。本文将探讨哪些类型的企业容易成为DDoS攻击的目标&#xff0c;并提出相应的防范策略&#xff0c;帮助企业更好地保护自身网络安全。 一、电子商务平台 电子商务平台作为线上交易和支付的重要场…

一文了解JVM(中)

HotSpot 虚拟机对象探秘 对象的创建 Header解释使用 new 关键字调用了构造函数使用 Class 的 newInstance 方法调用了构造函数使用 Constructor 类的newInstance 方法调用了构造函数使用 clone 方法没有调用构造函数使用反序列化没有调用构造函数说到对象的创建,首先让我们看…

6个迹象表明你的电脑电缆管理很糟糕,看下你有没有中招

​清理电脑内部的电缆可能看起来像是徒劳的忙碌。毕竟,如果一切都正常,为什么还要麻烦呢?好吧,我有六个很好的理由可以说服你打开你的机箱,修复你电脑里的混乱。 你很难打开侧板 如果你的电缆离侧板的边缘太近,你将无法毫不费力地将它们滑开。虽然这不是你每天都要做的…

数据结构——图论详细笔记

一 图论基本概念 Directed Acyclic Graph &#xff08;DAG&#xff09; 二 图的存储 ①邻接矩阵(适用于稠密图) ②邻接表(适用于稀疏图) 三、图的遍历 ①深度优先搜索 //(基于邻接表实现&#xff0c;以有向图为例) //DFS:Depth First Search 深度优先搜索 //1、访问起始顶点 …

为什么要开发盲盒小程序?商家企业的盈利方向?

近几年&#xff0c;盲盒已经成为了一种娱乐消费的流行趋势&#xff0c;受到了年轻人的喜欢&#xff0c;推动了盲盒经济的快速发展。在互联网的支持下&#xff0c;盲盒行业也获得了数字化发展&#xff0c;盲盒小程序为市场创新发展提供了重要动力。在当下小程序快速发展的时代&a…

【C++修行之道】类和对象(四)运算符重载

目录 一、 运算符重载 函数重载和运算符重载有什么关系&#xff1f; 二、.*运算符的作用 三、运算符重载的正常使用 四、重载成成员函数 五、赋值运算符重载 1.赋值运算符重载格式 传值返回和引用返回 有没有办法不生成拷贝&#xff1f; 2. 赋值运算符只能重载成类的…

MongoDB CRUD操作:可重试写入

MongoDB CRUD操作&#xff1a;可重试写入 文章目录 MongoDB CRUD操作&#xff1a;可重试写入使用的先决条件部署的限制支持的存储引擎3.6 MongoDB 驱动程序MongoDB 版本写确认 可重试写入和多文档事务启用可重试写入MongoDB驱动mongosh 可重试的写操作行为持续的网络错误故障切…

Linux如何远程连接服务器?

远程连接服务器是当代计算机技术中一个非常重要的功能&#xff0c;在各种领域都有广泛的应用。本文将重点介绍如何使用Linux系统进行远程连接服务器操作。 SSH协议 远程连接服务器最常用的方式是使用SSH&#xff08;Secure Shell&#xff09;协议。SSH是一种网络协议&#xff…

Java常规题技术分享

一、数组排序和添加成员 设计类Student和类StudentClass。 (1) 类Student有字符串属性name、double属性grade和int属性age 有带参数的构造方法&#xff0c;可设置三个属性的值 有各个属性的置取方法 (2)类StudentClass有Student数组属性stus存放班级成员&#xff0c;有int…

「不只是框架:Django REST framework的超能力大揭秘」

想要让你的API服务像五星级餐厅一样令人难忘吗&#xff1f;今天阿佑将为你揭晓&#xff01;从基础的RESTful原则到Django REST framework的高级特性&#xff0c;我们一步步带你走进API开发的后厨&#xff0c;展示如何准备食材&#xff08;数据模型&#xff09;、调制酱料&#…

揭秘GPU技术新趋势:从虚拟化到池化

从GPU虚拟化到池化 大模型兴起加剧GPU算力需求&#xff0c;企业面临GPU资源有限且利用率不高的挑战。为打破这一瓶颈&#xff0c;实现GPU算力资源均衡与国产化替代&#xff0c;GPU算力池化成为关键。本文深入探讨GPU设备虚拟化途径、共享方案及云原生实现&#xff0c;旨在优化资…

yolov5模型结构与构建原理

一.yolov5模型结构与构建原理 修改模型结构&#xff0c;全部在models文件夹下面 models/common.py &#xff08;加入新增网络细节&#xff09; models/yolo.py &#xff08;设定网络结构传参细节&#xff09; models/##.yaml &#xff08;修改模型结构配置文…

kill 不管用时,类型为C

当使用nvidia-smi时看到类型为C的进程时&#xff0c;使用 kill -9 PID&#xff0c;却不管用&#xff0c;这时需要先使用如下命令&#xff0c;找出运行的脚本对应的所有PID: ps -aux | grep train.py 接着就会把train.py对应运行的进程全部展示出来&#xff1a; 接着就是使用 …

景源畅信电商:抖音小店怎么做好运营?

在如今这个数字化时代&#xff0c;电商平台如雨后春笋般涌现&#xff0c;其中抖音小店以其独特的短视频营销模式迅速崛起。如何在这个竞争激烈的市场中占据一席之地&#xff0c;成为了许多商家和创业者思考的问题。下面&#xff0c;我们将深入探讨抖音小店的运营策略&#xff0…

Qt——控件

目录 概念 QWidget核心属性 enabled geometry WindowFrame的影响 windowTitle windowIcon qrc的使用 windowOpacity cursor font toolTip focusPolicy ​编辑 styleSheet 按钮类控件 PushButton RadioButton CheckBox 显示类控件 Label textFormat pixm…