MybatisPlus 与 Docker 学习笔记

一、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> {

   
}

常用配置

基本无需太大改动,要改动的的话看官方文档即可

MyBatis-Plus 🚀 为简化开发而生

核心功能

基于QueryWrapper的查询

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

基于UpdateWrapper的更新

总结  •  条件构造器的用法:

•  QueryWrapperLambdaQueryWrapper通常用来构建selectdeleteupdatewhere条件部分

•  UpdateWrapperLambdaUpdateWrapper通常只有在set语句比较特殊才使用

•  尽量使用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码

自定义SQL

指可以利用MyBatisPlusWrapper来构建复杂的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运行hello-world镜像失败或超时:Unable to find image ‘hello-world:latest‘ locally Trying to pull reposi_unable to find image 'hello-world:latest' locally-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常见命令

           2.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,才能创建容器加入网桥并运行,前端也同理。。主要是自定义镜像的三大模块 入口,层,基础镜像.....

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值