2024最新SpringCloud微服务开发与实战,java黑马商城项目微服务实战开发(涵盖MybatisPlus、Docker、MQ、ES、Redis高级等)---MybatisPlus笔记分享

目录​​​​​​​

​​​​​​​​编辑MybatisPlus

快速入门

常见注解

常见配置

核心功能

条件构造器

​编辑

基于QueryWrapper的查询

自定义SQL

Service接口

CRUD

基本用法

Lambda

批量新增

扩展功能

代码生成

静态工具

Iservice接口是非静态的,里面的方法都是非静态的所以需要自定义接口并且继承它,继承过程中还要指定泛型,泛型就是实体类的类型,为什么要指定实体类的类型呢?因为它要通过反射得到实体类的字节码才能知道表信息才能实现CRUD。静态工具由于他是静态的类上是不可能有泛型,或者说静态的方法是没有办法读取到类上的泛型的因此DB上是没有泛型的,这就导致没有办法知道实体类的类型,也就没有办法知道表信息,那怎么做CRUD,所以它的方法都需要传递一个额外的参数(class(class字节码)),只有传递这个才能利用反射拿到实体类的相关信息。

逻辑删除

枚举处理器

JSON处理器

插件功能

分页插件

通用分页实体


MybatisPlus

快速入门

1. 引入MybatisPlus的起步依赖
MyBatisPlus官方提供了starter,其中集成了Mybatis和MybatisPlus的所有功能,并且实现了自动装配效果。
因此我们可以用MybatisPlus的starter代替Mybatis的starter:
2. 定义Mapper
自定义的Mapper继承MybatisPlus提供的BaseMapper接口:

常见注解

常见配置

核心功能

条件构造器

除了新增以外,修改、删除、查询的SQL语句都需要指定where条件。因此BaseMapper中提供的相关方法除了以id作为where条件以外,还支持更加复杂的where条件。

基于QueryWrapper的查询

基于UpdateWrapper的更新

无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,会出现字符串魔法值。这在编程规范中显然是不推荐的。 那怎么样才能不写字段名,又能知道字段名呢?

其中一种办法是基于变量的gettter方法结合反射技术。因此我们只要将条件对应的字段的getter方法传递给MybatisPlus,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用Lambda表达式。

自定义SQL

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

@Param("ew") QueryWrapper<User> wrapper:将 QueryWrapper<User> 类型的参数命名为 ew,可以在 SQL 语句中通过 ${ew} 或 #{ew} 引用。
@Param("amount") int amount:将 int 类型的参数命名为 amount,可以在 SQL 语句中通过 ${amount} 或 #{amount} 引用。

Service接口

CRUD

基本用法

UserController
@Api("用户管理接口")
@RequestMapping("/users")
@RestController
@RequiredArgsConstructor //必备的构造函数,对一开始需要初始化的变量初始化
public class UserController {

    private final IUserService userService; //UserController初始化时必须对
                                            // userService变量初始化
    @ApiOperation("新增用户接口")
    @PostMapping
    public void saveUser(@RequestBody UserFormDTO userDTO) {
        // 用糊涂工具将UserFormDTO对象转换为User对象
        //1.把DTO拷贝到PO
        User user = BeanUtil.copyProperties(userDTO, User.class);
        //2.新增
        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.查询用户po
        User user =userService.getById(id);
        //2.把po拷贝到vo
        return BeanUtil.copyProperties(user, UserVO.class);
    }

    @ApiOperation("根据id批量查询用户接口")
    @GetMapping
    //@RequestParam 可以将请求中的参数绑定到方法的参数上
    public List<UserVO> queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids){
        //1.查询用户po
        List<User> users = userService.listByIds(ids);
        //2.把po拷贝到vo
        return BeanUtil.copyToList(users, UserVO.class);
    }
   @ApiOperation("扣减用户余额接口")
   @PutMapping("/{id}/deduction/{money}")
   public void deductMoneyById(@ApiParam("用户id") @PathVariable("id") long id,
                            @ApiParam("扣减的金额") @PathVariable("money") Integer money){
   userService.deductBalace(id ,money); 
   }
}
IUserService
public interface IUserService extends IService<User> {
    void deductBalace(long id , Integer money);
}
UserServiceImpl
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements IUserService {
    @Override
    public void deductBalace(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.deductMoneyById(id,money);
    }
}
UserMapper
void deductMoneyById(@Param("id") long id, @Param("money") Integer money);
UserMapper.xml
<update id="deductMoneyById">
    UPDATE user set balance = balance - #{money} WHERE id = #{id}
</update>
Lambda

复杂查询
@Override
public 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)
            .gt(minBalance != null, User::getBalance, minBalance)
            .le(maxBalance != null, User::getBalance, maxBalance)
            .list();
}

复杂更新
@Override
@Transactional // 面向事务
public void deductBalace(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.deductMoneyById(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();
}
批量新增

MySQL的客户端连接参数中有这样的一个参数:rewriteBatchedStatements。顾名思义,就是重写批处理的statement语句。这个参数的默认值是false,我们需要修改连接参数,将其配置为true修改项目中的application.yml文件,在jdbc的url后面添加参数

spring: datasource: url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true driver-class-name: com.mysql.cj.jdbc.Driver username: root password: MySQL123

扩展功能

代码生成

在使用MybatisPlus以后,基础的MapperServicePO代码相对固定,重复编写也比较麻烦。因此MybatisPlus官方提供了代码生成器根据数据库表结构生成POMapperService等相关代码。只不过代码生成器同样要编码使用,也很麻烦。这里推荐大家使用一款MybatisPlus的插件,它可以基于图形化界面完成MybatisPlus的代码生成,非常简单。

最新版idea好像不能在导航栏增加 "other" 选项卡了,生成代码的两个选项迁移到"tools"选项卡了,望周知

静态工具

Iservice接口是非静态的,里面的方法都是非静态的所以需要自定义接口并且继承它,继承过程中还要指定泛型,泛型就是实体类的类型,为什么要指定实体类的类型呢?因为它要通过反射得到实体类的字节码才能知道表信息才能实现CRUD。静态工具由于他是静态的类上是不可能有泛型,或者说静态的方法是没有办法读取到类上的泛型的因此DB上是没有泛型的,这就导致没有办法知道实体类的类型,也就没有办法知道表信息,那怎么做CRUD,所以它的方法都需要传递一个额外的参数(class<T>(class字节码)),只有传递这个才能利用反射拿到实体类的相关信息。

有的时候Service之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus提供一个静态工具类:Db,其中的一些静态方法与IService中方法签名基本一致,也可以帮助我们实现CRUD功能:

需求:改造根据id用户查询的接口,查询用户的同时返回用户收货地址列表

首先,我们要添加一个收货地址的VO对象:

package com.itheima.mp.domain.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
@ApiModel(description = "收货地址VO")
public class AddressVO{

    @ApiModelProperty("id")
    private Long id;

    @ApiModelProperty("用户ID")
    private Long userId;

    @ApiModelProperty("省")
    private String province;

    @ApiModelProperty("市")
    private String city;

    @ApiModelProperty("县/区")
    private String town;

    @ApiModelProperty("手机")
    private String mobile;

    @ApiModelProperty("详细地址")
    private String street;

    @ApiModelProperty("联系人")
    private String contact;

    @ApiModelProperty("是否是默认 1默认 0否")
    private Boolean isDefault;

    @ApiModelProperty("备注")
    private String notes;
}

然后,改造原来的UserVO,添加一个地址属性:

接下来,修改UserController中根据id查询用户的业务接口:

由于查询业务复杂,所以要在service层来实现。首先在IUserService中定义方法:

然后,在UserServiceImpl中实现该方法:

在查询地址时,我们采用了Db的静态方法,因此避免了注入AddressService,减少了循环依赖的风险。

逻辑删除

对于一些比较重要的数据,我们往往会采用逻辑删除的方案,即:

  • 在表中添加一个字段标记数据是否被删除

  • 当删除数据时把标记置为true

  • 查询时过滤掉标记为true的数据

一旦采用了逻辑删除,所有的查询和删除逻辑都要跟着变化,非常麻烦。

为了解决这个问题,MybatisPlus就添加了对逻辑删除的支持。

枚举处理器

要让MybatisPlus处理枚举与数据库类型自动转换,我们必须告诉MybatisPlus,枚举中的哪个字段的值作为数据库值。

MybatisPlus提供了@EnumValue注解来标记枚举属性

在UserStatus枚举中通过@JsonValue注解标记JSON序列化时展示的字段

@Getter
public enum UserStatus {
    NORMAL(1,"正常"),
    FROZEN(2,"冻结"),
    ;

    @EnumValue  //该注解的作用是value值对应数据库的status的值
    private final int value;
    @JsonValue  //选择返回的json值
    private final String desc;

    UserStatus(int value, String desc) {
        this.value = value;
        this.desc = desc;
    }
}

JSON处理器

在 MyBatis-Plus 中,`autoResultMap = true` 是一个用于简化复杂字段映射配置的属性。它主要用于解决像 `UserInfo info` 这样的嵌套对象的映射问题,即当实体类中包含复杂类型或嵌套对象时,`autoResultMap = true` 可以让 MyBatis 自动生成结果映射 (`resultMap`),避免手动配置映射关系。

下面将详细分析 `autoResultMap = true` 在该场景中是如何工作的。

### 场景分析

假设 `User` 类中包含了一个 `info` 字段:

```java
// User.java
@Data
public class User {
    private Long id;
    
    // 嵌套对象,JSON 存储的字段
    @TableField(typeHandler = JacksonTypeHandler.class)
    private UserInfo info;
}
```

`info` 是 `UserInfo` 类型的字段,而在数据库中,通常会将 `UserInfo` 对象序列化为 JSON 格式存储在单个字段中(例如 `info` 字段存储在数据库的一列中)。为了处理这种场景,`typeHandler` 配置了 `JacksonTypeHandler`,它负责在 Java 对象和 JSON 格式之间进行转换。

### `JacksonTypeHandler` 的作用

`JacksonTypeHandler` 是一个 MyBatis 类型处理器,负责在数据库和 Java 对象之间进行 JSON 转换。具体来说:
- **读操作**:从数据库中取出 `info` 字段的 JSON 字符串,并将其转换为 `UserInfo` 对象
- **写操作**:将 `UserInfo` 对象转换为 JSON 字符串,并存入数据库中的 `info` 字段

### `autoResultMap = true` 的作用

当 `autoResultMap = true` 时,MyBatis-Plus 会自动生成结果映射 `resultMap`,使 MyBatis 能够正确处理 `info` 字段的嵌套对象映射,而无需手动在 XML 中定义 `resultMap`。

具体分析 `autoResultMap = true` 的作用如下:

1. **自动生成 `resultMap`**:MyBatis-Plus 会扫描 `User` 类中的字段,并根据注解(如 `@TableField`、`@TableId` 等)自动生成一个 `resultMap`。在 `autoResultMap = true` 的情况下,它会自动生成能正确处理 `info` 的 `resultMap`。
   
2. **识别嵌套对象**:MyBatis-Plus 会识别到 `info` 字段是一个复杂对象(非基本类型),并根据 `@TableField(typeHandler = JacksonTypeHandler.class)` 注解确定该字段使用 `JacksonTypeHandler` 进行类型转换。这样就可以正确处理 `info` 字段的 JSON 序列化和反序列化过程。

3. **避免手动配置**:如果没有 `autoResultMap = true`,则需要手动在 XML 文件中定义 `resultMap` 来指定 `info` 字段的类型处理器,这样做不仅繁琐,而且不易维护。`autoResultMap = true` 则让这一过程自动化,简化了代码。

不使用autoResultMap=true 手动配置

<!-- UserMapper.xml -->
<mapper namespace="com.example.UserMapper">

    <!-- 定义 resultMap -->
    <resultMap id="userResultMap" type="com.example.User">
        <!-- 主键映射 -->
        <id property="id" column="id"/>
        
        <!-- 普通字段映射 -->
        <result property="name" column="name"/>
        
        <!-- 嵌套对象的 JSON 字段映射 -->
        <result property="info" column="info" 
                typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" 
                javaType="com.example.UserInfo"/>
    </resultMap>

    <!-- 查询方法,引用上面定义的 resultMap -->
    <select id="getUserById" resultMap="userResultMap">
        SELECT id, name, info
        FROM users
        WHERE id = #{id}
    </select>

</mapper>

### 总结

- **`autoResultMap = true`** 自动生成 `resultMap`,使 MyBatis 能够自动识别嵌套对象或复杂类型字段的映射需求。
- **`@TableField(typeHandler = JacksonTypeHandler.class)`** 告诉 MyBatis 使用 `JacksonTypeHandler` 将 `info` 字段从 JSON 转换为 Java 对象(和反向转换)。
  
在这种设置下,MyBatis 能够自动识别和处理 JSON 格式的复杂字段,从而避免手动定义复杂的 `resultMap` 配置。

插件功能

分页插件
首先,要在配置类中注册MyBatisPlus的核心插件,同时添加分页插件:
@Configuration
public class MyBatisConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //1.创建分页插件
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        paginationInnerInterceptor.setMaxLimit(1000L);
        //2.添加分页插件
        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        return interceptor;
    }
}
接着,就可以使用分页的API了:
@Test
    void testPageQuery() {
        int pageN0=1 , pageSize=5;
        //1.准备分页条件
        //1.1.分页条件
        Page<User> page = Page.of(pageN0, pageSize);
        //1.2.排序条件
        page.addOrder(new OrderItem("balance",true));

        //2.分页查询
        Page<User>  p = userService.page(page);

        //3.解析
        long total = p.getTotal();
        System.out.println("总记录数:"+total);
        long pages = p.getPages();
        System.out.println("总页数:"+pages);
        List<User> users = p.getRecords();
        users.forEach(System.out::println);
    }

通用分页实体

`@EqualsAndHashCode(callSuper = true)` 是 Lombok 提供的一个注解,用于在生成 `equals` 和 `hashCode` 方法时,包含父类中的字段。它用于继承结构中的子类,确保在判断相等性和生成哈希码时,将父类的字段也纳入计算。

### 作用和用法

在 Java 中,当一个类继承自另一个类时,通常需要在 `equals` 和 `hashCode` 方法中考虑父类的属性,否则可能导致相等性判断和集合操作出错。例如,如果子类只依赖自己的字段进行 `equals` 和 `hashCode` 操作,就可能忽略了继承自父类的属性,这样会造成不准确的结果。

#### `@EqualsAndHashCode(callSuper = true)` 的用法

- **`callSuper = true`**:表示在生成 `equals` 和 `hashCode` 方法时,将父类的字段包含在内。
- **继承结构中常用**:适用于子类需要同时考虑自身和父类属性的情况。

### 示例代码

假设有一个基类 `Person`,和一个子类 `Employee`:

```java
import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
public class Person {
    private String name;
    private int age;
}

@EqualsAndHashCode(callSuper = true) // 将父类的属性包含在 equals 和 hashCode 中
@Data
public class Employee extends Person {
    private String employeeId;
}
```

### `callSuper = true` 的作用

在上述代码中,`Employee` 类继承了 `Person` 类,`Employee` 的 `equals` 和 `hashCode` 方法会包含 `Person` 类的 `name` 和 `age` 字段,同时也包含 `Employee` 自己的 `employeeId` 字段。因此:

1. **equals 方法**:在判断两个 `Employee` 对象是否相等时,会比较 `name`、`age` 和 `employeeId` 三个属性。
2. **hashCode 方法**:在计算 `Employee` 的哈希码时,三个属性也会一起计算。

### 适用场景

- **继承结构**:当子类重写 `equals` 和 `hashCode` 方法,并希望包含父类字段时。
- **避免手动实现复杂的相等性判断**:当子类有多个属性,且继承自复杂的父类时,`@EqualsAndHashCode(callSuper = true)` 可以减少代码量,避免手动编写 `equals` 和 `hashCode` 方法。

package com.itheima.mp.domain.query;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {
    @ApiModelProperty("页码")
    private Long pageNo;
    @ApiModelProperty("页码")
    private Long pageSize;
    @ApiModelProperty("排序字段")
    private String sortBy;
    @ApiModelProperty("是否升序")
    private Boolean isAsc;
}
package com.itheima.mp.domain.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;
}
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;
}
 @ApiOperation("根据条件分页查询用户接口")
    @GetMapping("/page")
    //@RequestParam 可以将请求中的参数绑定到方法的参数上
    public PageDTO<UserVO> queryUserPage(UserQuery query){
        return userService.queryUsersPage(query);
    }
PageDTO<UserVO> queryUsersPage(UserQuery query);
@Override
    public PageDTO<UserVO> queryUsersPage(UserQuery query) {
        String name = query.getName();
        Integer status = query.getStatus();
        //1.构建查询条件
        Page<User> page = Page.of(query.getPageNo(), query.getPageSize());
        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)
                .page(page);
        //3.封装VO结果
        PageDTO<UserVO> dto = new PageDTO<>();
        dto.setTotal(p.getTotal());
        dto.setPages(p.getPages());
        List<User> records = p.getRecords();
        if(CollUtil.isEmpty(records)) {
            dto.setList(Collections.emptyList());
            return dto;
        }
        List<UserVO> VOS = BeanUtil.copyToList(records, UserVO.class);
        dto.setList(VOS);

        //4.返回
        return dto;
    }

  • 第一个 <T>:是类型参数的声明,表示当前方法是泛型方法,T 是一种占位符,代表某种具体的类型。
  • Page<T>:表示返回类型是 Page<T>其中的 T 是使用了前面声明的类型参数 T。具体的类型会在方法调用时由编译器推断出来。
@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {
    @ApiModelProperty("页码")
    private Long pageNo = 1L;
    @ApiModelProperty("页码")
    private Long pageSize = 5L;
    @ApiModelProperty("排序字段")
    private String sortBy;
    @ApiModelProperty("是否升序")
    private Boolean isAsc = true;

    public <T> Page<T> toMpPage(OrderItem ... items) {
        //1.构建查询条件
        Page<T> page = Page.of(pageNo, pageSize);
        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 isAsc){
        return this.toMpPage(new OrderItem(defaultSortBy, isAsc));
    }

    public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() {
        return toMpPage("create_time", false);
    }

    public <T> Page<T> toMpPageDefaultSortByUpdateTimeDesc() {
        return toMpPage("update_time", false);
    }
}
@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<>();
        dto.setTotal(p.getTotal());
        dto.setPages(p.getPages());
        List<PO> records = p.getRecords();
        if(CollUtil.isEmpty(records)) {
            dto.setList(Collections.emptyList());
            return dto;
        }
        dto.setList(BeanUtil.copyToList(records, clazz));
        //4.返回
        return dto;
    }

}

User实体和UserVO实体字段名需要相同

将MybatisPlus分页结果转为 VO分页结果,允许用户自定义PO到VO的转换方式

@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, Function<PO,VO> convertor) {
        PageDTO<VO> dto = new PageDTO<>();
        dto.setTotal(p.getTotal());
        dto.setPages(p.getPages());
        List<PO> records = p.getRecords();
        if(CollUtil.isEmpty(records)) {
            dto.setList(Collections.emptyList());
            return dto;
        }
        dto.setList( records.stream().map(convertor).collect(Collectors.toList()));
        //4.返回
        return dto;
    }

}

records.stream().map(convertor).collect(Collectors.toList()) 是 Java 8 Stream API 中的一个常见操作,它执行以下步骤:

  1. records.stream():将 records 集合转换为一个流(Stream)。records 通常是一个集合或数组(如 List, Set 等),stream() 方法将其转换为流对象,允许我们使用 Stream API 进行操作。

  2. map(convertor)对流中的每个元素应用 convertor 转换函数convertor 是一个 Function<PO, VO> 类型的对象,表示一个从 PO 类型转换到 VO 类型的函数。map 方法会将流中的每个元素(类型 PO)转换为类型 VO,并返回一个新的流,这个新流中的每个元素都是转换后的 VO 类型。

  3. collect(Collectors.toList()):将流中的元素收集到一个新的 List 中。collect 是一个终结操作,它会将流中的所有元素处理并以指定的方式收集。Collectors.toList() 是一个常用的收集器,它会将流中的元素收集到一个 List 中并返回。

Function<PO, VO> 是一个函数式接口,它表示一个接受类型 PO 作为输入并返回类型 VO 的函数。Function 接口定义了一个单一的抽象方法 apply,用于执行从 POVO 的转换操作。

@Override
    public PageDTO<UserVO> queryUsersPage(UserQuery query) {
        String name = query.getName();
        Integer status = query.getStatus();
        //1.构建查询条件
        Page<User> page = query.toMpPageDefaultSortByUpdateTimeDesc();
        //2.分页查询
        Page<User> p =lambdaQuery()
                .like(name != null ,User::getUsername, name)
                .eq(status != null, User::getStatus, status)
                .page(page);
        //3.封装VO结果
        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;
        });
    }

### 黑马商城微服务架构笔记 #### Spring Cloud 和 Dubbo 技术选型 国内知名的微服务框架有SpringCloud和阿里巴巴的Dubbo[^1]。对于黑马商城项目而言,选择的技术栈很大程度上取决于具体需求和技术团队熟悉度。 #### Spring Cloud 功能概述 基于Spring Boot构建的Spring Cloud提供了丰富的工具集来简化分布式系统的建设。这些工具涵盖了配置管理、服务发现、智能路由、断路器等多个方面,使得开发者能够更加便捷地实现复杂的业务逻辑和服务治理[^2]。 #### API模块设计原则 为了确保各个微服务之间的交互一致性,在所有微服务都会引用的地方定义API是非常重要的。考虑到某些功能较为单一的情况,可以采用匿名内部类的形式编写代码;而当涉及到跨服务的数据传递时,则可能需要额外引入common模块以便更好地处理数据交换问题[^3]。 ```java // 示例:在pom文件中引入common模块 <dependency> <groupId>com.example</groupId> <artifactId>common</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> // 使用匿名内部类的方式创建简单组件 @Bean public FilterRegistrationBean someFilter() { final Filter filter = new Filter() { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest)request; HttpServletResponse httpResponse = (HttpServletResponse)response; String userInfo = httpRequest.getHeader("User-Info"); // 处理用户信息... chain.doFilter(request,response); } @Override public void init(FilterConfig arg0) {} @Override public void destroy() {} }; FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(filter); return registration; } ``` #### Zuul网关Eureka注册中心集成 Zuul作为Netflix开源的服务网关组件之一,通常会配合Eureka一起工作以完成服务间的通信任务。无论是系统默认提供的自动映射机制还是自定义配置下的手动映射模式,最终目的都是要让客户端可以通过Zuul访问到部署在Eureka上的目标服务实例[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值