【Springcloud微服务】MybatisPlus下篇
🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Springcloud微服务
🌠 首发时间:2024年6月4日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾
目录
- 扩展功能
- 代码生成
- 静态工具
- 逻辑删除
- 枚举处理器
- JSON处理器
- 插件功能
- 分页插件
- 通用分页实体
扩展功能
代码生成
在使用 MybatisPlus 以后,基础的 Mapper、Service、PO 代码相对固定,重复编写也比较麻烦。因此 MybatisPlus 官方提供了代码生成器根据数据库表结构生成 PO、Mapper、Service 等相关代码。只不过代码生成器同样要编码使用,也很麻烦。
所以这里推荐使用另外一款 MybatisPlus 的插件,它可以基于图形化界面完成 MybatisPlus 的代码生成,非常简单。
安装插件
在 Idea 的 plugins 市场中搜索并安装 MyBatisPlus 插件:

然后重启你的 Idea 即可使用。
使用
刚好数据库中还有一张 address 表尚未生成对应的实体和 mapper 等基础代码,我们可以利用插件生成一下。
首先需要配置数据库地址,在 Idea 顶部菜单中,找到 other,选择 Config Database,然后填写一些信息:

然后再次点击 Idea 顶部菜单中的 other,然后选择 Code Generator,填写表单信息:

示例:


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

下面,我们通过一些案例来学习使用静态工具。
需求:
1、改造根据 id 查询用户的接口,查询用户的同时,查询出用户对应的所有地址
由于现在需要额外返回收货地址,所以我们需要定义一个收货地址的 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 查询用户的业务接口,新建一个方法:
/*** 根据id查询用户** @param userId* @return*/
@GetMapping("/{id}")
@ApiOperation("根据id查询用户")
public UserVO queryUserById(@ApiParam("用户id") @PathVariable("id") Long userId) {return userService.queryUserAndAddressById(userId);
}
IUserService 接口中声明该方法:
/*** 根据id查询用户及其收货地址** @param userId* @return*/
UserVO queryUserAndAddressById(Long userId);
UserServiceImpl 中实现方法:
/*** 根据id查询用户及其收货地址** @param userId* @return*/
public UserVO queryUserAndAddressById(Long userId) {// 1.查询用户User user = getById(userId);if (user == null || user.getStatus() == 2) {throw new RuntimeException("用户状态异常!");}// 2.查询收货地址列表List<Address> addresses = Db.lambdaQuery(Address.class).eq(Address::getUserId, userId).list();// 3.封装数据UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);// 先判断收货地址列表是否为空if (CollUtil.isNotEmpty(addresses)) {userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));}// 4.返回数据return userVO;
}
测试:

2、改造根据 id 批量查询用户的接口,查询用户的同时,查询出用户对应的所有地址
UserController:
/*** 根据id集合查询用户** @param ids* @return*/
@GetMapping
@ApiOperation("根据id集合查询用户")
public List<UserVO> queryUserByIds(@RequestParam("ids") List<Long> ids) {return userService.queryUserAndAddressByIds(ids);
}
UserServiceImpl:
/*** 根据id集合查询用户及其收货地址** @param ids* @return*/
public List<UserVO> queryUserAndAddressByIds(List<Long> ids) {// 1.查询用户List<User> users = listByIds(ids);if (CollUtil.isEmpty(users)) {return Collections.emptyList(); // 返回空列表}// 2.查询收货地址List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, ids).list(); //根据用户id查询收货地址List<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class); //转化地址VO//将用户收货地址分组处理,相同用户的放入一个集合中Map<Long, List<AddressVO>> addressMap = new HashMap<>(0);if (CollUtil.isNotEmpty(addressVOList)) {addressMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));}// 3.封装数据List<UserVO> list = new ArrayList<>(users.size());for (User user : users) {UserVO vo = BeanUtil.copyProperties(user, UserVO.class); // 拷贝用户信息vo.setAddresses(addressMap.get(user.getId())); // 设置用户收货地址list.add(vo);}return list;
}
测试:

逻辑删除
逻辑删除就是基于代码逻辑模拟删除效果,但并不会真正删除数据库中的数据。思路如下:
- 在表中添加一个字段标记数据是否被删除
- 当删除数据时把标记置为 1
- 查询时只查询标记为 0 的数据
一旦采用了逻辑删除,所有的查询和删除逻辑都要跟着变化,非常麻烦。
为了解决这个问题,MybatisPlus 提供了逻辑删除功能,无需改变方法调用的方式,而是在底层帮我们自动修改 CRUD 的语句。我们要做的就是在 application.yaml 文件中配置逻辑删除的字段名称和值即可:
在 Address 实体类中已经存在一个字段 deleted 用于逻辑删除,所以我们不用再定义:

mybatis-plus:global-config:db-config:logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)logic-delete-value: 1 # 逻辑已删除值(默认为 1, 可以不配置)logic-not-delete-value: 0 # 逻辑未删除值(默认为 0, 可以不配置)
注意,只有 MybatisPlus 生成的 SQL 语句才支持自动的逻辑删除,自定义 SQL 需要自己手动处理逻辑删除。
写一个测试类测试一下:
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 java.util.List;@SpringBootTest
class IAddressServiceTest {@Autowiredprivate IAddressService addressService;@Testvoid testDeleteByLogic() {// 删除方法与以前没有区别addressService.removeById(59L);}@Testvoid testQuery() {List<Address> list = addressService.list();list.forEach(System.out::println);}
}
先执行第一个测试方法,进行删除,结果如下:

再执行第二个方法查询一下,结果如下:

综上, 开启了逻辑删除功能以后,我们就可以像普通删除一样做 CRUD,基本不用考虑代码逻辑问题。还是非常方便的。
但是,逻辑删除本身也有自己的问题,比如:
- 会导致数据库表垃圾数据越来越多,影响查询效率
- SQL 中全都需要对逻辑删除字段做判断,影响查询效率
因此,还是不太推荐采用逻辑删除功能,如果数据不能删除,可以采用把数据迁移到其它表的办法。
枚举处理器
User 类中有一个用户状态字段:

像这种字段我们一般会定义一个枚举,做业务判断的时候就可以直接基于枚举做比较。但是我们数据库采用的是 int 类型,对应的PO也是 Integer。因此业务操作时必须手动把枚举与 Integer 转换,非常麻烦。
因此,MybatisPlus 提供了一个处理枚举的类型转换器,可以帮我们把枚举类型与数据库类型自动转换。
定义枚举
我们定义一个用户状态的枚举:
package com.itheima.mp.enums;import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;@Getter
public enum UserStatus {NORMAL(1, "正常"),FREEZE(2, "冻结");private final int value;private final String desc;UserStatus(int value, String desc) {this.value = value;this.desc = desc;}
}
项目结构如下:

然后把 User 类中和 UserVO 中的 status 字段改为 UserStatus 类型。
将 UserServiceImpl 代码中的 2 替换为 UserStatus.FREEZE,显得更专业一点。
要让 MybatisPlus 处理枚举与数据库类型自动转换,我们必须告诉 MybatisPlus,枚举中的哪个字段的值作为数据库值。
MybatisPlus 中提供了 @EnumValue 注解来标记枚举属性:

配置枚举处理器
在 application.yaml 文件中添加配置:
mybatis-plus:configuration:default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
然后我们测试一下查询:

查询成功,不过查询出的 User 类的 status 字段是枚举类型,可能不是很好理解。
我们在 UserStatus 枚举中通过 @JsonValue 注解标记 JSON 序列化时要展示的字段,添加在 value 或者 desc 上都可以:
比如,我们添加在 desc 上,就会显示 “正常” 或者 “冻结”:


JSON处理器
数据库的 user 表中有一个 info 字段,是 JSON 类型:

而目前我们的 User 实体类中却是 String 类型的:

这样一来,我们要读取 info 中的属性时就非常不方便。如果要方便获取,info 的类型最好是一个 Map 或者实体类。
而一旦我们把 info 改为对象类型,就需要在写入数据库时手动转为 String,再读取数据库时,手动转换为对象,这将会非常麻烦。
因此,MybatisPlus 为我们提供了很多特殊类型字段的类型处理器,解决特殊字段类型与数据库类型转换的问题。例如处理 JSON 就可以使用 JacksonTypeHandler 处理器。
接下来,我们就来看看这个处理器该如何使用。
定义实体
首先,我们在 po 下定义一个单独实体类 UserInfo 来与 info 字段的属性匹配:
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 类的 info 字段修改为 UserInfo 类型,并声明类型处理器,同时开启结果自动映射;UserVO 中的 info 字段也需要修改为 UserInfo 类型:
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 = "user", autoResultMap = true) //开启结果自动映射
public class User {@TableId(type = IdType.AUTO) //不指定的话,默认为随机生成id,也就是第三种方式private Long id; //用户idprivate String username; //用户名private String password; //密码private String phone; //注册手机号@TableField(typeHandler = JacksonTypeHandler.class)private UserInfo info; //详细信息private UserStatus status; //使用状态(1正常 2冻结)private Integer balance; //账户余额private LocalDateTime createTime;//创建时间private LocalDateTime updateTime;//更新时间
}
将测试方法中关于设置详细信息的代码修改一下:

重启服务,测试一下查询接口,可以看到信息成功返回:

插件功能
MybatisPlus 提供了很多的插件功能,进一步拓展其功能。目前已有的插件有:
- PaginationInnerInterceptor:自动分页
- TenantLineInnerInterceptor:多租户
- DynamicTableNameInnerInterceptor:动态表名
- OptimisticLockerInnerInterceptor:乐观锁
- IllegalSQLInnerInterceptor:sql 性能规范
- BlockAttackInnerInterceptor:防止全表更新与删除
最常用的是自动分页插件。
分页插件
在未引入分页插件的情况下,MybatisPlus 是不支持分页功能的,IService 和 BaseMapper 中的分页方法都无法正常起效。所以,我们必须配置分页插件。
在项目中新建一个配置类,项目结构如下:

package com.itheima.mp.config;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() {// 1.初始化核心插件MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 2.创建分页插件PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();paginationInnerInterceptor.setMaxLimit(1000L); //设置最大分页限制// 3.添加分页插件interceptor.addInnerInterceptor(paginationInnerInterceptor);return interceptor;}
}
在 IUserServiceTest 中写一个分页查询的测试方法:
@Test
void testPageQuery() {// 1.准备分页条件int pageNum = 1, pageSize = 2; //页码、每页大小Page<User> page = Page.of(pageNum, pageSize);// 2.排序条件page.addOrder(new OrderItem("balance", true)); // 先按余额升序排序page.addOrder(new OrderItem("id", true)); // 再按id升序排序// 3.分页查询Page<User> p = userService.page(page);// 4.解析数据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);
}
结果:

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

定义实体
这里需要定义3个实体:
- UserQuery:分页查询条件的实体,包含分页、排序参数、过滤条件
- PageDTO:分页结果实体,包含总条数、总页数、当前页数据
- UserVO:用户页面视图实体(已存在)
虽然 UserQuery 之前已经定义过了,并且其中已经包含了过滤条件,其中缺少的仅仅是分页条件,但是分页条件不仅仅是用户分页查询需要,以后其它业务也都有分页查询的需求。因此建议将分页查询条件单独定义为一个 PageQuery 实体:
PageQuery 是前端提交的查询参数,一般包含四个属性:
- pageNo:页码
- pageSize:每页数据条数
- sortBy:排序字段
- isAsc:是否升序
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;
}
然后,让我们的 UserQuery 继承这个实体:
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;
}
最后,则是分页实体 PageDTO,由于在其它微服务项目中可能也会使用到这个分页实体,我们将其定为为 DTO:
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 中定义分页查询用户的接口:
/*** 根据条件分页查询用户** @param query* @return*/
@GetMapping("/page")
@ApiOperation("根据条件分页查询用户接口")
public PageDTO<UserVO> queryUsersPage(UserQuery query) {return userService.queryUsersPage(query);
}
在 IUserService 中创建 queryUsersPage 方法:
/*** 根据条件分页查询用户** @param query* @return*/
PageDTO<UserVO> queryUsersPage(UserQuery query);
在 UserServiceImpl 中实现该方法:
/*** 根据条件分页查询用户** @param query* @return*/
public PageDTO<UserVO> queryUsersPage(UserQuery query) {String name = query.getName();Integer status = query.getStatus();// 构建分页条件Page<User> page = Page.of(query.getPageNo(), query.getPageSize()); 3// 排序条件if (StrUtil.isNotBlank(query.getSortBy())) {// 不为空page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));} else {// 为空,默认按照更新时间排序page.addOrder(new OrderItem("update_time", query.getIsAsc()));}// 分页查询Page<User> p = lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).page(page);//封装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());} else {// 不为空dto.setList(BeanUtil.copyToList(records, UserVO.class));}return dto;
}
测试一下:

改造PageQuery实体
在上面的代码中,从 PageQuery 到 MybatisPlus 的 Page 之间转换的过程还是比较麻烦的。
我们完全可以在 PageQuery 这个实体中定义一个工具方法,来简化开发。
package com.itheima.mp.domain.query;import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {@ApiModelProperty("页码")private Long pageNo = 1L;@ApiModelProperty("每页数据条数")private Long pageSize = 2L;@ApiModelProperty("排序字段")private String sortBy;@ApiModelProperty("是否升序")private Boolean isAsc = true;// 多个参数public <T> Page<T> toMpPage(OrderItem... orders) {// 分页条件Page<T> page = Page.of(pageNo, pageSize);// 排序条件if (StrUtil.isNotBlank(sortBy)) {// 不为空page.addOrder(new OrderItem(sortBy, isAsc));} else if (orders != null) {// 为空page.addOrder(orders);}return page;}// 只有一个参数public <T> Page<T> toMpPage(String defaultSortBy, boolean isAsc) {return 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);}
}
这样我们在开发也时就可以省去对从 PageQuery 到 Page 的的转换:
// 构建分页条件
Page<User> page = query.toMpPageDefaultSortByUpdateTimeDesc();
改造PageDTO实体
同理,在查询出分页结果后,数据的非空校验,数据的 vo 转换都是模板代码,编写起来很麻烦。
我们完全可以将其封装到 PageDTO 的工具方法中,简化整个过程:
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<>();dto.setTotal(p.getTotal()); // 总条数dto.setPages(p.getPages()); // 总页数// 当前页数据List<PO> records = p.getRecords();if (CollUtil.isEmpty(records)) {// 为空,设置为空里欸博爱dto.setList(Collections.emptyList());} else {// 不为空dto.setList(BeanUtil.copyToList(records, clazz));}return dto;}// 需要传PO如何转换为VO的方法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());} else {// 不为空dto.setList(records.stream().map(convertor).collect(Collectors.toList()));}return dto;}
}
最终,业务层的代码可以简化为:
public PageDTO<UserVO> queryUsersPage(UserQuery query) {String name = query.getName();Integer status = query.getStatus();// 构建分页条件Page<User> page = query.toMpPageDefaultSortByUpdateTimeDesc();// 分页查询Page<User> p = lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).page(page);return PageDTO.of(p, UserVO.class);
}
如果是希望自定义 PO 到 VO 的转换过程,可以这样做:
return PageDTO.of(p, user -> {// 1.拷贝基础属性UserVO vo = BeanUtil.copyProperties(user, UserVO.class);// 2.处理特殊逻辑:比如用户名脱敏String username = vo.getUsername();vo.setUsername(username.substring(0, username.length() - 2) + "**");return vo;
});
测试一下:

在改进的过程中,我们都是在实体类中添加了对应的方法来简化我们的开发,这也意味着这些方法和实体类耦合了。因为我们使用的是 MP,所以可以这样写,如果使用的不是 MP,我们可以考虑定义一些工具类来实现这些类型之间的转换,以此让方法与实体类解耦合。
相关文章:
【Springcloud微服务】MybatisPlus下篇
🔥 本文由 程序喵正在路上 原创,CSDN首发! 💖 系列专栏:Springcloud微服务 🌠 首发时间:2024年6月4日 🦋 欢迎关注🖱点赞👍收藏🌟留言ὃ…...
i18n-demo
一、demo 1、资源文件准备 如我需要对menu、logMsg内容做国际化。 resources下放各个语言文件,直接放resources下都行。我是新建了一个myi18n文件夹, (1)然后在myi18n上点击New--Resource Bundle (2)在…...
[Leetcode] 0-1背包和完全背包
46. 携带研究材料 纯01背包(非应用):只能选择一次物品 dp[j]:容量为j的背包所能装的最大容量 容量需要倒序 416. 分割等和子集 能否装满 dp[j]:容量为j的背包所能装的最大容量 1049. 最后一块石头的重量 II 尽可…...
自定义类型:联合体和枚举
1. 联合体类型的声明 2. 联合体的特点 3. 联合体大小的计算 4. 枚举类型的声明 5. 枚举类型的优点 6. 枚举类型的使用 欢迎关注 熬夜学编程 创作不易,请多多支持 感谢大家的阅读、点赞、收藏和关注 如有问题,欢迎指正 1. 联合体 1.1 联合体类型的声…...
【Cityengine】Cityengine生产带纹理的建筑模型导入UE4/UE5(下)
【Cityengine】Cityengine生产带纹理的建筑模型导入UE4/UE5(下) 一、导出数据(2022中文版案例)二、安装datasmith插件三、导入数据四、检查导入材质是否正常五、编辑替换材质六、安装模型编辑插件七、编辑替换建筑规则 一、导出数…...
详解51种企业应用架构模式
导读:企业应用包括哪些?它们又分别有哪些架构模式?世界著名软件开发大师Martin Fowler给你答案 01、什么是企业应用 我的职业生涯专注于企业应用,因此,这里所谈及的模式也都是关于企业应用的。(企业应用还…...
【十年java搬砖路】Jumpserver docker版安装及配置Ldap登陆认证
Jumpserver docker 安装启动教程 拉取镜像 docker pull JumpServer启动进行前确保有Redis 和Mysql 创建jumperServer数据库 在MYSQL上执行 创建数据库 登陆MYSQL mysql -u root -p 创建Jumperserveri库 create database jumpserver default charset utf8mb4;可以为jumperSe…...
C\C++内存管理(未完结)
文章目录 一.C\C内存分布二.C语言中动态内存管理方式:malloc/calloc/realloc/free三.C内存管理方式3.1.new/delete操作内置类型3.2.new和delete操作自定义类型 四.operator new与operator delete函数(重要点进行讲解)4.1. operator new与oper…...
一个小时搞定JAVA面向对象(5)——抽象与接口
文章目录 抽象抽象的注意事项static\final\private是否可以修饰抽象方法继承和抽象知识点回顾 接口接口实现总结抽象方法默认方法静态方法成员变量接口的特点接口和抽象类的区别 抽象 关键字: abstract 抽象方法: 修饰符 abstract 返回值类型 方法名(参数); 抽象类: public a…...
图像关键特征描述方法-小目标
图像关键特征描述方法主要包括以下几种: SIFT(尺度不变特征变换): SIFT是一种广泛使用的特征描述方法,它通过尺度空间和梯度方向直方图来描述图像中的关键点。SIFT特征描述具有尺度不变性和旋转不变性,对于光照和视角变化也具有一定的鲁棒性。 SURF(加速稳健特征): SURF…...
【qt15】windeployqt 安装依赖
debug模式vs可以使用qt插件新建qt文件 D:\Qt15\5.15.2\msvc2019\bin\windeployqt.exe Warning: Cannot find Visual Studio installation directory, VCINSTALLDIR is not set.D:\Qt15\5.15.2\msvc2019\bin\windeployqt.exe .\filecopier.exeWindows PowerShell Copyright (C) …...
DETR论文重点
DETR就是 DEtection TRansformer 的缩写。 论文原名:End-to-End Object Detection with Transoformers。 重点有两个:端到端、Transformer结构 论文概述 注意:斜体的文字为论文原文,其他部分内容则是为增进理解而做的解释。 …...
slf4j等多个jar包冲突绑定的排查方法使用IDEA的maven help解决
1.安装 2.使用maven help解决,找到对应包存在的冲突 使用exclude直接解决即可...
MySQL主从的延迟怎么解决呢?
以下是一些减少或解决MySQL主从延迟的策略: 优化查询和索引: 确保所有的查询都经过优化,以减少主服务器上的负载。使用合适的索引来加速查询速度,减少锁的时间。 分散复制负载: 使用多个从服务器分散读取负载。使用并…...
【一百】【算法分析与设计】N皇后问题常规解法+位运算解法
N皇后问题 链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网 题目描述 给出一个nnn\times nnn的国际象棋棋盘,你需要在棋盘中摆放nnn个皇后,使得任意两个皇后之间不能互相攻击。具体来说,不能存在两个皇后位于同…...
GPT-4:人工智能领域的新里程碑
近期,OpenAI推出了备受瞩目的GPT-4。作为GPT系列的最新成员,GPT-4在自然语言处理(NLP)领域再次刷新了记录,引发了广泛的关注和讨论。在试用GPT-4之后,我深感其在技术能力、应用场景等方面都取得了显著的进步…...
mysql inset bug
在 SQL 中,日期值需要用单引号包围,这是因为 SQL 将日期值视为字符串格式。数据库引擎在处理这些值时会将它们解析为适当的日期类型。如果不使用单引号,数据库引擎会将它们视为数字或列名,从而导致语法错误。 日期格式 MySQL 支…...
oracle查看序列
在Oracle数据库中,查看序列的方式主要有以下几种: 查看当前用户下的所有序列名称: sql复制代码 SELECT sequence_name FROM user_sequences; 查看所有用户的序列: sql复制代码 SELECT sequence_name FROM all_sequences; 查看…...
flask-slqalchemy使用详解
目录 1、flask-sqlalchemy 1.1、flask_sqlalchemy 与sqlalchemy 的关系 1.1.1、 基本定义与用途 1.2、flask_sqlalchemy 的使用 1.2.1、安装相关的库 1.2.2、项目准备 1.2.3、创建ORM模型 1.2.3.1、使用db.create_all()创建表的示例 1.2.3.2、创建多表关联ORM模型 1.…...
Scala学习笔记8: 包
目录 第八章 包1- 包2- 包的作用域3- 串联式包语句4- 包对象5- 引入end 第八章 包 在Scala中, 包(Package) 用于组织和管理代码, 类似与 Java 中的包 ; 包可以包含类、对象、特质等Scala代码, 并通过层次结构来组织代码 ; 可以使用 package 关键字来定义包, 并使用 . 来表示…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...
