一、MybatisPlus
简单介绍:MyBatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息。
不过要符合以下约定:
1.类名驼峰转下划线作为表名
2.名为id的字段作为主键
3.变量名驼峰转下划线作为表的字段名
不符合约定也可以,有以下方法补救
常见注解

具体使用
1.先在项目pom文件下导入以下坐标
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
2.定义Mapper接口并继承BaseMapper
public interface XXMapper extends BaseMapper<XXX> {
}
常用配置
基本无需太大改动,要改动的的话看官方文档即可
核心功能
基于QueryWrapper的查询

Lambda写法(其实就是Lambda表达式,没有像上面的例子硬编码,不容易出错)

基于UpdateWrapper的更新

总结 • 条件构造器的用法:
• QueryWrapper和LambdaQueryWrapper通常用来构建select、delete、update的where条件部分
• UpdateWrapper和LambdaUpdateWrapper通常只有在set语句比较特殊才使用
• 尽量使用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码
自定义SQL
指可以利用MyBatisPlus的Wrapper来构建复杂的Where条件,然后自己定义SQL语句中剩下的部分。
使用例子:

实战例子(下面这个例子用的是硬编码):



IService 接口

使用例子


看,用这个接口,一句实现类以及service类的代码都没写完整往数据库添加了数据

总结流程

实战例子:
基于Lambda的查询

//Controller层
@GetMapping ("/list")
@ApiOperation("根据复杂条件查询用户信息")
public List<UserVO> queryUsers(UserQuery userQuery){
List<User> users = userService.queryUsers(userQuery.getName(), userQuery.getStatus(), userQuery.getMinBalance(), userQuery.getMaxBalance());
return BeanUtil.copyToList(users, UserVO.class)
//Service
List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance);
//ServiceImpl
@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)
.ge(minBalance != null,User::getBalance,minBalance)
.le(maxBalance != null,User::getBalance,maxBalance)
.list();
}
基于Lambda的更新

//Controller
@ApiOperation("扣减用户余额接口")
@PutMapping("/{id}/deduction/{money}")
public void deductMoneyById(
@ApiParam("用户id") @PathVariable("id") Long id,
@ApiParam("扣减的金额") @PathVariable("money") Integer money){
userService.deductBalance(id, money);
}
//Service
void deductBalance(Long id, Integer money);
//ServiceImpl
@Override
@Transactional// 开启事务
public void deductBalance(Long id, Integer money) {
User user = userMapper.queryUserById(id);
//User user = getById(id);
if (user == null || user.getStatus() == 2){
throw new RuntimeException("用户不存在或已被冻结");
}
if (user.getBalance()<money){
throw new RuntimeException("余额不足");
}
// 更新余额
//baseMapper.deductBalance(id,money);
//更新余额,如果余额为0 则冻结用户
int remainBalance = user.getBalance() - money;
lambdaUpdate()
.set(User::getBalance,remainBalance)
.set(User::getStatus,remainBalance==0?2:1)
.eq(User::getId,id)
.eq(User::getBalance,user.getBalance())//添加乐观锁条件,如果余额为0,则更新为冻结用户
.update();//上面的句子相当于:update user set balance=balance-#{money} where id=#{id},最后要执行update
}
扩展功能
代码生成器

具体操作 MybatisPlus-12.扩展功能-代码生成器_哔哩哔哩_bilibili
DB静态工具
有时候在一个工程中,出现Service一直相互调用(或是Service中含有其他Service业务的时候),那就选择DB静态工具,因为他里面的方法跟Service里的方法几乎一样,不用再写过多繁杂的代码

来个例子

Impl层代码:

结果

逻辑删除

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

但逻辑删除本身也有自己的问题,比如:
1.会导致数据库表垃圾数据越来越多,影响查询效率
2.SQL中全都需要对逻辑删除字段做判断,影响查询效率
使用逻辑删除后,Delete 语句变成Update 语句,查询的时候会多一个where语句只查询默认值为0 的(未被逻辑删除)
![]()
![]()
枚举处理器
作用:用于实现PO类中的枚举类型变量与数据库字段的转换
使用步骤:
1.定义一个枚举(举例)
2.给枚举中的与数据库对应value值添加@EnumValue注解
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
@Getter//方便取值
public enum UserStatus {
NORMAL(1,"正常"),
FROZEN(2,"冻结"),
;
@EnumValue//用了这个后mp就知道把枚举值存到数据库了
private final int value;// 数据库存储的枚举值
@JsonValue// 返回json时返回的枚举值,加在谁上就返回谁的值,就像这样返回的是"正常""冻结"
private final String desc;// 描述
UserStatus(int value, String desc) {
this.value = value;
this.desc = desc;
}
}
3.在配置文件中配置统一的枚举处理器,实现类型转换

4.其他使用就类似下面,原本Integer类型改成枚举类型

JSON处理器
情景案例:
在一个User实体内还有一个详细信息,详细信息里面又含有三个字段,而且又不想多弄一张数据库表的情况下,可以把详细信息内的字段写成JSON语句,就像下面这样:

那么我们在输入信息的时候,该给这个字段的值给予一个什么样的类型呢,用string类型的话,输入如下面这么麻烦
user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
读取出来的数据也比较难读
![]()
那么有没有什么方法可以让我们输入方便输出也方便呢,那就是JSON处理器!!!!

1.首先定一个Info的实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor// 无参构造器
@AllArgsConstructor(staticName = "of")// 有参构造器,同时提供静态方法of,就可以类名.方法名创建对象
public class UserInfo {
private Integer age;
private String intro;
private String gender;
}
2.把原本定义成String类型的info字段全部改成用 UserInfo类接收,并且用上@TableField注释说明使用了json处理器

user类上指定结果集,写下面第二行的句子

3.那么输入数据时候就不用写那么麻烦,读数据的时候可读性也提高了,存入数据库的依然是json格式,变得更规范
user.setInfo(UserInfo.of(24,"英文老师","female"));

MP插件

主要使用分页插件为多
配置分页插件
1.新建配置类
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 {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 1.创建分页插件
PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
paginationInterceptor.setMaxLimit(100L);// 设置单页最大条数,随便自定义什么功能
//2.添加分页插件
interceptor.addInnerInterceptor(paginationInterceptor);
return interceptor;
}
}
2.用一个测试类来验证分页是否成功
@Test
void 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);
}
3.结果

案例

1.定义分页实体(同时哪里要使用哪里就继承)
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;
}
2.定义分页结果实体
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import java.util.List;
@Data
@ApiOperation("分页查询结果")
public class PageDTO<T> {
@ApiModelProperty("总条数")
private Long total;
@ApiModelProperty("总页数")
private Long pages;
@ApiModelProperty("集合")
private List<T> list;
}
3.controller层和service层
//controller层
@ApiOperation("根据条件分页查询用户接口")
@GetMapping("/page")
public PageDTO<UserVO> queryUsersPage(UserQuery query) {
return userService.queryUsersPage(query);
}
//service层
PageDTO<UserVO> queryUsersPage(UserQuery query);
4.impl
@Override
public PageDTO<UserVO> queryUsersPage(UserQuery query) {
String name = query.getName();
Integer status = query.getStatus();
// 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. 分页查询,相当于SQL语句
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<>();
// 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的VO
List<UserVO> vos = BeanUtil.copyToList(records, UserVO.class);
dto.setList(vos);
// 4. 返回
return dto;
}
通用分页实体与MP转换(上述案例的简化)
1..定义分页实体
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;// 默认每页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));
}
}
2.定义分页结果实体
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import java.util.Collections;
import java.util.List;
@Data
@ApiOperation("分页查询结果")
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的VO
dto.setList(BeanUtil.copyToList(records, Clazz));
// 5. 返回
return dto;
}
}
3.impl
@Override
public PageDTO<UserVO> queryUsersPage(UserQuery query) {
String name = query.getName();
Integer status = query.getStatus();
// 1. 构建分页条件
Page<User> page = query.toMpPageDefaultSortByUpdateTime();
// 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, UserVO.class);
}
二、Docker
快速构建、运行、管理应用的工具
安装
Docker 安装:小白 Ubuntu 安装Docker全教程(已成功!)_ubuntu安装docker菜鸟教程-CSDN博客
可能出现的问题 :
Docker 连接MYSQL数据库(Linux系统)
1.确保Docker服务已经启动:
sudo service docker start
2.手动拉取MySQL镜像(如果需要的话)(要等待一定时间):
docker pull mysql
3.运行MySQL容器(端口密码自己设置,不过一般端口都用3306)
<your password>替换成你设置的密码
docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD= <your password> \
mysql
4.重启mysql
sudo docker restart mysql
5.设置mysql自启动
sudo docker update mysql --restart=always
6.确保MySQL Docker容器正在运行
sudo docker ps
效果如下,能看到包含 mysql 的一行,这表示MySQL容器正在运行。

7.用IDEA来连接虚拟机数据库
查询虚拟机IP地址
hostname -I

基础认识与总结


常用命令

使用:1.Docker常见命令
数据卷挂载
数据卷(volume)是一个虚拟目录,是容器内目录与宿主机目录之间映射的桥梁,它将宿主机目录映射到容器内目录,方便我们操作容器内文件,或者方便迁移容器产生的数据

命令

如何挂载数据卷
1.在创建容器时,利用 -v 数据卷名:容器内目录完成挂载
2.容器创建时,如果发现挂载的数据卷不存在时,会自动创建
例子:创建Nginx容器并且挂载数据卷
-v html:/usr/.../html

容器网络互联
1.首先自定义一个网桥(<>内的是自定义)
docker network create <name>
2.把所需要接入同一个网桥的镜像连起来(要是部署后端一般会后面接上mysql)
docker network connect <name> <mysql...>
3.同一网桥内不同的容器之间可以互相ping通。
实战:项目部署(仅仅为个人记录)
1.把后端项目成功打包成jar包后,把jar包和配置镜像的文件拉进虚拟机,然后使用以下指令
docker build -t hmall .
2.然后新建并运行一个新的容器,同时连入自定义网桥
docker run -d --name hm -p 8080:8080 --network keke hmall
3.配置静态资源前端,前端资料里有html文件和conf文件,运行以下命令
root@kokoa-virtual-machine:~# docker run -d \
> --name nginx \
> -p 18080:18080 \
> -p 18081:18081 \
> -v /root/nginx/html:/usr/share/nginx/html \ nginx的容器内目录
> -v /root/nginx/nginx.conf:/etc/nginx/nginx.conf \ 配置conf文件的容器内目录
> --network keke \ 加入网桥
> nginx
4.输入以下命令可以观察日志
root@kokoa-virtual-machine:~# docker logs -f hm
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.12)
看到熟悉的界面比什么都好。。。。。
碎碎念:
总的来说,就是首先建一个新网桥,这点很重要,运行mysql镜像,前提是加入网桥,运行后端jar包,当然要先记得build,才能创建容器加入网桥并运行,前端也同理。。主要是自定义镜像的三大模块 入口,层,基础镜像.....
940

被折叠的 条评论
为什么被折叠?



