MybatisPlus(简单CURD,MP的实体类注解,MP条件查询,MP分页查询,MP批量操作,乐观锁,代码生成器)

目录

一、MP入门

1. MP是什么

2. MP使用入门

1 说明

2 准备MP项目环境

1) 添加依赖

2) 创建配置文件

3) 创建引导类

3 MP使用入门

1 创建实体类

2 创建Mapper

3 使用测试

3. 小结

二、MP简单CURD【重点】

1. 说明

2. 示例

3. 小结

三、MP的实体类注解[重点]

1. @TableName表名映射

问题说明

解决方案

2. @TableField字段映射

1 属性名与字段名不匹配的情况

问题说明

解决方案

2 自定义属性没有对应字段的情况

问题说明

解决方案

3 某字段不需要查询的情况

问题说明

解决方案

3. @TableId主键策略

说明

示例

4. 小结

四、MP条件查询【重点】

1.介绍

1 条件查询介绍

2 开发环境准备

2. QueryWrapper的使用

1 使用入门

2 设置查询条件

3.or连接条件

4 动态条件拼接

5 查询投影[了解]

3. LambdaQueryWrapper的使用

1 使用入门

2 设置查询条件

3 动态条件拼接

4 查询投影[了解]

4. 小结

五、MP分页查询【重点】

1. 介绍

1 使用步骤

2 分页API

2. 示例

1 配置分页插件

2 执行分页查询

3. 小结

六、MP的DML操作

1. MP的批量操作

2. MP的逻辑删除

3. MP的乐观锁

4. 小结

七、MP代码生成器

1. 说明

2. 示例

1 新建一个project

2 编写代码生成器


一、MP入门

1. MP是什么

MybatisPlus,简称MP,是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变。MP为简化开发、提高效率而生。它已经封装好了单表curd方法,我们直接调用这些方法就能实现单表CURD。

注意:MP仅仅提供了单表的CURD。如果要实现多表联查,就只能自己动手实现了,按Mybatis的方式写

官网地址:MyBatis-Plus

2. MP使用入门

1 说明

1 使用步骤说明

MP是dao层操作数据库的框架,它的使用步骤和Mybatis类似但要简单的多。

MP的使用步骤:

  1. 准备数据库表

  2. 创建一个SpringBoot工程,准备MybatisPlus环境:

    添加依赖:MybatisPlus的依赖坐标、数据库驱动包

    修改配置:配置数据库连接地址

    准备引导类

  3. 使用MybatisPlus

    准备实体类,对应数据库表

    创建一个空的Mapper接口,继承BaseMapper<实体类>,并添加注解@Mapper

  4. 测试MybatisPlus

2 准备数据库表

连接本机的MySQL,执行《资料/mp_db.sql》,或者执行以下脚本代码:

create database if not exists mp_db character set utf8mb4;
use mp_db;
drop table if exists user;
CREATE TABLE user
(
    id       bigint(20) primary key auto_increment,
    user_name     varchar(32) not null,
    password varchar(32) not null,
    age      int(3)      not null,
    tel      varchar(32) not null,
    sex      char(1)     not null
);
insert into user values (null, 'tom', '123456', 12, '12345678910', '男');
insert into user values (null, 'jack', '123456', 8, '12345678910', '男');
insert into user values (null, 'jerry', '123456', 15, '12345678910', '女');
insert into user values (null, 'rose', '123456', 9, '12345678910', '男');
insert into user values (null, 'snake', '123456', 28, '12345678910', '女');
insert into user values (null, '张益达', '123456', 22, '12345678910', '男');
insert into user values (null, '张大炮', '123456', 16, '12345678910', '男');

alter table user auto_increment = 8;

2 准备MP项目环境

这里准备了两种方式,在实际开发中都可能会使用。大家习惯哪一种就用哪一种

建议使用手动创建方式

使用idea创建一个maven工程,不用选择任何Artifact骨架,设置好工程坐标即可。然后按照如下步骤准备MP的开发环境:

1) 添加依赖

在工程的pom.xml里添加依赖:

<parent><!--SpringBoot父工程坐标--><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.3</version>
</parent><dependencies><!--MybatisPlus的起步依赖--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version></dependency><!--MySQL数据库驱动包--><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.0.33</version></dependency><!--SpringBoot测试起步依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>
</dependencies>
2) 创建配置文件

在工程的resources文件夹里创建配置文件application.yaml,内容如下:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mp_db
    username: root
    password: root

3) 创建引导类

在工程的java文件夹里创建引导类 com.itheima.MpApplication,代码如下:

@SpringBootApplication
public class MpApplication {
    public static void main(String[] args) {
        SpringApplication.run(MpApplication.class, args);
    }
}

3 MP使用入门

1 创建实体类

在工程的java文件夹里创建实体类com.itheima.pojo.User,对应数据库里的user表:

package com.itheima.pojo;

import lombok.Data;

@Data
public class User {
    private Long id;
    private String userName;
    private String password;
    private Integer age;
    private String tel;
    private String sex;
}

2 创建Mapper

com.itheima.mapper包里创建一个接口UserMapper,要求:

  • 接口要继承BaseMapper<实体类>。我们的实体类是User,所以要继承的是BaseMapper<User>

  • 接口上添加注解@Mapper

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

3 使用测试

创建一个单元测试类,测试MP的功能,代码如下:

package com.itheima;import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class Demo01MpTest {@Autowiredprivate UserMapper userMapper;@Testpublic void test1(){User user = userMapper.selectById(1);System.out.println(user);}
}

3. 小结

  1. 准备基本环境:添加坐标依赖,修改配置,引导类,准备实体类

  2. 创建空的Mapper接口,继承BaseMapper<实体类>,接口上加@Mapper

二、MP简单CURD【重点】

  • 掌握MP的简单CURD操作

1. 说明

MP内置了强大的BaseMapper,它已经提供好了单表CURD功能:只要我们的Mapper接口继承了BaseMapper,就可以直接使用整套的单表CURD功能了。

常用的基本curd方法有:

2. 示例

package com.itheima;import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
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
public class Demo02MpCurdTest {@Autowiredprivate UserMapper userMapper;/*** 查询数据的方法示例*/@Testpublic void testSelect(){//1. 根据id查询一个User user = userMapper.selectById(1);System.out.println(user);//2. 查询列表(查询全部)List<User> userList = userMapper.selectList(null);for (User u : userList) {System.out.println(u);}//3. 查询数量Integer count = userMapper.selectCount(null);System.out.println("总数量:" + count);}/*** 插入数据的示例*/@Testpublic void testInsert(){User user = new User();user.setUserName("老王");user.setAge(40);user.setPassword("heihei");user.setTel("13800138000");user.setSex("男");System.out.println("插入之前的user没有id:" + user);int i = userMapper.insert(user);System.out.println("影响的行数是:" + i);System.out.println("插入之后的user有id了:" + user);}/*** 修改数据的示例*/@Testpublic void testUpdate(){User user = new User();user.setId(1L);user.setPassword("666666");//注意:方法是根据id修改的,所以传入的对象里必须有主键id的值int i = userMapper.updateById(user);System.out.println("影响的行数是:" + i);}/*** 删除数据的示例*/@Testpublic void testDelete(){int i = userMapper.deleteById(1);System.out.println("影响的行数是:" + i);}
}

3. 小结

MP本身提供了一批单表CURD的方法

查询方法:都以select开头

  • selectById:根据id查询一个

  • selectCount:根据条件查询数量

  • selectList:根据条件查询列表

新增方法:都以insert开头

  • insert:新增数据,并且MP会自动帮我们生成主键值。方法返回值是影响行数

修改方法:都以update开头

  • updateById:根据id修改。方法返回值是影响行数

删除方法:都以delete开头

  • deleteById:根据id删除。方法返回值是影响行数

三、MP的实体类注解[重点]

MP对实体类的写法是有一些要求的:

  • 实体类名:和表名要符合 下划线与驼峰命名的映射规则

  • 实体类里的属性:

    • 属性:与字段必须一一对应。每个属性都必须有一个对应的字段

    • 命名:属性名与字段名,要符合 下划线与驼峰命名的映射规则

如果实体类确实不符合以上规则,MP提供了一些注解用于特殊设置:

  • 表名与实体类名的映射:

    @TableName:用于设置 实体类名 与 数据库表名 的对应关系。当表名和实体类名不对应时需要设置

  • 字段与属性的映射:

    @TableField:用于非主键字段。设置非主键字段与属性对应关系。当表字段名与属性名不对应时设置

    @TableId:用于主键字段。设置主键字段与属性对应关系,还可以设置主键生成策略

1. @TableName表名映射

问题说明

我们在使用MP查询的过程中,并没有指定要查询的表。例如:我们调用了UserMapperselectList方法,MP就从user表里查询数据了。那么MP是怎么知道要查询哪张表呢?

实际上MP默认会把实体类名与表名做映射,不过是按照下划线命名与大驼峰命名的映射方式:

  • 表名称:以下划线式命名。例如:tb_order, tb_order_detail

  • 实体类名称:以大驼峰式命名。例如:TbOrderTbOrderDetail

所以我们调用UserMapper时继承的BaseMapper<User>,MP就知道要查询 实体类User对应的表user

但是,如果实体类名与表名不匹配的时候,MP就会报错 “表不存在”。例如实体类名为AppUser,表名为user时,报错如下图示例:

解决方案

  1. 有实体类AppUser如下图。在类上添加注解@TableName("表名称")

再次查询用户列表,可以正常查询

2. @TableField字段映射

@TableField用在JavaBean里的属性上,用于设置属性与数据库字段的映射关系。

这个注解提供了很多参数,我们这里介绍3个常用的:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableField {
    //=====常用参数这里只列出以下几个,其它都省略掉了====
    
    //该属性对应的字段名称,用于解决 JavaBean属性名 与 字段名不匹配的情况
    String value() default "";

    //该属性是否存在对应的字段,如果不存在,就要设置为false,否则操作数据库会报错
    boolean exist() default true;

    //查询时是否查询此属性对应的字段
    boolean select() default true;
}

1 属性名与字段名不匹配的情况

问题说明

默认情况下,MP会把数据库表的字段名 自动与 JavaBean属性名进行映射,而映射的规则是:

  • 表的字段名:要求采用下划线式命名,单词之间使用_连接。例如:user_name, order_id

  • JavaBean的属性名:采用小驼峰式命名,首字母小字,后续每个单词首字母大写。例如:userName, orderId

MP在操作数据库时,如果有任何属性找不到对应的字段,就会报错“找不到字段”,如下图示例:

这时候,我们就必须使用@TableField(value="字段名")来手动设置属性与字段的映射关系。

解决方案
  1. 有实体类如下图。

    其中一个属性名称为uname与字段名user_name不匹配。就在属性上加注解@TableField(value="字段名")

    2.查询用户列表,发现user_name字段的值也查询出来了

@SpringBootTest
public class Demo03MpTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void test(){
        List<AppUser> users = userMapper.selectList(null);
        for (AppUser user : users) {
            System.out.println(user);
        }
    }
}

2 自定义属性没有对应字段的情况

问题说明

有时候,我们会在JavaBean中添加一些自定义的属性,而这些属性 是没有任何字段与之对应的。

MP在操作数据库时,如果有任何属性找不到对应的字段,就会报错“找不到字段”。如下图示例:

这时候我们可以通过添加@TableField(exist=false),告诉MP,这个属性没有对应的字段,MP就不会报错了

解决方案
  1. 有实体类User如下图。

    其中有属性address,数据库里是没有对应的字段的。所以在属性上加注解@TableField(exist=false)

     2.再次执行查询用户列表,发现不报错了

3 某字段不需要查询的情况

问题说明

默认情况,MP会查询表里所有字段的值,封装到JavaBean对象中。但是实际开发中,可能某些字段并不需要查询,这时候我们可以使用@TableField(select=false)

解决方案
  1. 有实体类User如下图。

    其中的password属性我们不要查询,就在属性上添加注解@TableField(select=false)

3. @TableId主键策略

说明

当我们调用了Mapper的insert方法,插入数据时,并不需要指定id值,MP会按照既定的主键生成策略帮我们生成好主键值。

使用方法:修改实体类,在主键对应的属性上添加注解@TableId(type=主键策略)

其中的“主键策略”,MP提供了多种策略供我们使用:

  • IdType.NONE:不设置,跟随全局

  • IdType.AUTO:数据库主键自增

  • IdType.INPUT:MP不生成主键值,在插入数据时由我们提供一个主键值

  • IdType.ASSIGN_ID:由MP使用雪花算法生成一个id值,可兼容数值型(long类型)和字符串型

  • IdType.ASSIGN_UUID:由MP使用UUID算法生成一个id值

示例

  1. 有实体类AppUser,其主键属性id是Long类型,要设置主键策略为自增。就在id属性上添加注解@TableId(type=IdType.AUTO)

   2.重新执行一下今天资料里的SQL脚本,恢复一下数据库表和其中的数据。

   3.在测试类里增加下测试代码,执行后发现数据库里新的数据id是自增的

@Test
public void test2(){
    AppUser user = new AppUser();
    user.setUname("小张");
    user.setPassword("12345");
    user.setAge(20);
    user.setSex("男");
    user.setTel("13800138000");
    userMapper.insert(user);
}

4. 小结

实体类与表名规则要求:

  • MP默认情况下,要求:实体类和表名 要符合 下划线与驼峰的映射规则;

  • 如果不符合:就需要在实体类上加@TableName("表名")

实体类里属性与字段的规则要求:

  • 默认情况下,要求每个属性都必须有一个对应的字段,属性名和字段名要符合 驼峰与下划线映射规则

  • 非主键字段:

    • 如果一个属性名,和字段名不对应,怎么办?加@TableField(value="字段名")

    • 如果一个属性,没有对应的字段存在,怎么办?加@TableField(exists=false)

    • 如果一个属性,不想查询对应字段的值,怎么办?加@TableField(select=false)

  • 主键字段:

    • 要使用@TableId(value="字段名", type=主键生成策略)

    • 主键生成策略,设置的方式:实体类里主键属性上加 @TableId(type=IdType.策略名)

      NONE:不设置,跟随全局

      AUTO:主键值自增,前提是主键支持自增

      INPUT:由我们的代码设置主键值,不让MP生成主键值

      ASSIGN_ID:雪花算法。根据时间戳+机器码+序列号生成最终的Long类型的数字,特点是单调递增的

      ASSIGN_UUID:UUID算法。不推荐,因为UUID值是乱序的,会影响主键字段上的索引

练习:

  1. 把实体类重命名成AppUser (重命名快捷键 Shift + F6),实体类名与表名不对应了,怎么办?

  2. 把实体类里userName重命名成uname( Shift + F6),属性名和字段名不对应了,怎么办?

  3. 在实体类里增加address属性,没有对应的字段,怎么办?

  4. 设置主键生成策略为雪花算法,再测试新增。

四、MP条件查询【重点】

  • 能够使用QueryWrapper或者LambdaQueryWrapper实现条件查询

1.介绍

1 条件查询介绍

以前使用Mybatis开发时,遇到动态条件的查询,就需要我们在xml文件里使用各种标签实现SQL语句的动态拼接。而MP把这些封装成了Java的API,我们可以以编程的形式完成SQL语句的构建并实现复杂的多条件查询。

而复杂的多条件查询的关键在于查询条件的构造,为此MP提供了查询条件抽象类:Wrapper,用于封装查询相关的所有条件和其它查询信息。

MP提供的所有条件式的操作,都需要传入这个类型的参数。比如:

  • selectList(Wrapper w):根据条件查询列表

  • selectOne(Wrapper w):根据条件查询一条数据

  • selectCount(Wrapper w):根据条件查询数量

  • selectPage(Page p, Wrapper w):根据条件分页查询

  • ……

Wrapper有两个常用的子类可以封装where条件

  • QueryWrapper:使用时容易写错,但使用更灵活

  • LambdaQueryWrapper:没有QueryWrapper灵活,但是不易写错。使用的更多一些

在实际开发中,有时候会把QueryWrapper转换成LambdaQueryWrapper,两者混合使用。

2 开发环境准备

为了避免前边代码的干扰,我们准备一个新的工程,步骤略

实体类:User

@Data
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String userName;
    private String password;
    private Integer age;
    private String tel;
    private String sex;
}

UserMapper

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

2. QueryWrapper的使用

1 使用入门

需求

查询年龄大于20岁的用户,只查询用户的id、姓名、年龄,结果按年龄降序排列

示例

package com.itheima;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.mapper.UserMapper;
import com.itheima.pojo.AppUser;
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
public class Demo04QueryTest {@Autowiredprivate UserMapper userMapper;/*** 查询所有年龄大于20岁的用户* 只查询id、姓名、年龄* 结果按年龄降序排列*/@Testpublic void test1(){//创建一个QueryWrapper对象,泛型是实体类QueryWrapper<User> wrapper = new QueryWrapper<>();//	设置查询条件:age > 20wrapper.select("id", "user_name", "age").gt("age", 20).orderByDesc("age");//调用selectList方法,根据条件查询列表List<User> users = userMapper.selectList(wrapper);for (User user : users) {System.out.println(user);}}
}

2 设置查询条件

说明

如果要使用QueryWrapper进行条件查询,常用的查询条件方法有:

示例

@Test
public void test2(){
    //QueryWrapper<AppUser> wrapper = new QueryWrapper<>();
    //wrapper.gt("age", 15);
    //wrapper.lt("age", 25);
    
    QueryWrapper<AppUser> wrapper = new QueryWrapper<>();
    wrapper.gt("age", 15).lt("age", 25);

    List<AppUser> users = userMapper.selectList(wrapper);
    for (AppUser user : users) {
        System.out.println(user);
    }
}

3.or连接条件

在QueryWrapper的条件连接中,所有查询条件默认都是使用and连接的。如果想要多个条件使用or,可以使用它的or()方法

注意:SQL语句中or的优先级低于and

 @Test
 public void test3(){
     QueryWrapper<AppUser> wrapper = new QueryWrapper<>();
     // user_name = ? AND password = ? OR age > ?
     wrapper.eq("user_name", "jack").eq("password", "123456")
             .or()
             .gt("age", 15);

     List<AppUser> users = userMapper.selectList(wrapper);
     for (AppUser user : users) {
         System.out.println(user);
     }
 }

4 动态条件拼接

说明

在实际的数据查询中,通常可能有多个条件组合查询,并且条件的数量是动态变化的。如下图所示,用户可以选择任意1个、2个、多个条件进行组合查询

如果用户没有选择某个条件,那么SQL语句中就不能添加这个查询条件,这就需要使用SQL语句的动态拼接了

MP的所有查询条件的方法都有重载:

示例

@Test
public void test4(){//模拟:客户端提交过来的两个查询条件,最小年龄minAge,最大年龄maxAgeInteger minAge = 15;Integer maxAge = null;QueryWrapper<AppUser> wrapper = new QueryWrapper<>();//动态拼接查询条件: 如果minAge不为空,才会拼接  age > minAge值wrapper.gt(minAge!=null, "age", minAge)//        如果maxAge不为空,才会拼接  age < maxAge值.lt(maxAge!=null, "age", maxAge);List<AppUser> users = userMapper.selectList(wrapper);for (AppUser user : users) {System.out.println(user);}
}

5 查询投影[了解]

说明

所谓查询投影,指的就是SQL语句select .... from ...中,select的内容。MP允许我们自定义要select的字段,只要直接调用QueryWrapper对象的select()方法即可

示例

@Test
public void test5(){QueryWrapper<User> wrapper = new QueryWrapper<>();//select id, user_name, tel from user where age > 15 order by age descwrapper.select("id, user_name, tel").gt("age", 15).orderByDesc("age");List<User> users = userMapper.selectList(wrapper);for (User user : users) {System.out.println(user);}
}@Test
public void test6(){QueryWrapper<User> wrapper = new QueryWrapper<>();// select sex, count(*) as cnt from user group by sex;wrapper.select("sex, count(*) as cnt").groupBy("sex");List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);for (Map<String, Object> map : maps) {System.out.println(map);}
}

3. LambdaQueryWrapper的使用

1 使用入门

在刚刚使用QueryWrapper进行条件操作时,所有的条件都写的是数据库的字段名,这种方式很容易写错,而且不方便。所以MP提供了LambdaQueryWrapper解决这个问题。

使用LambdaQueryWrapper封装条件时,所有的条件不再写数据库字段,而是写字段对应的属性

需求

查询年龄大于20岁的用户,只查询用户的id、姓名、年龄,结果按年龄降序排列

示例

package com.itheima;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
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
public class Demo05LambdaQueryTest {@Autowiredprivate UserMapper userMapper;@Testpublic void test01(){LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.select(User::getId, User::getUserName, User::getAge).gt(User::getAge, 20).orderByDesc(User::getAge);List<User> users = userMapper.selectList(wrapper);for (User user : users) {System.out.println(user);}}
}

2 设置查询条件

说明

如果要使用LambdaQueryWrapper进行条件查询,常用的查询条件方法有:

示例

@Test
public void test02(){LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.gt(User::getAge, 15).lt(User::getAge, 25);List<User> users = userMapper.selectList(wrapper);for (User user : users) {System.out.println(user);}
}

拓展了解:or连接条件

说明

在LambdaQueryWrapper的条件连接中,所有查询条件默认都是使用and连接的。如果想要多个条件使用or,可以使用它的or()方法

注意:SQL语句中or的优先级低于and

示例

@Test
public void test03(){LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();// user_name = ? AND password = ? OR age > ?wrapper.eq(User::getUserName, "jack").eq(User::getPassword, "123456").or().gt(User::getAge, 15);List<User> users = userMapper.selectList(wrapper);for (User user : users) {System.out.println(user);}
}

3 动态条件拼接

说明

MP的所有查询条件的方法都有重载:

示例

@Test
public void test04(){//模拟:客户端提交过来的两个查询条件,最小年龄minAge,最大年龄maxAgeInteger minAge = 15;Integer maxAge = null;LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();//动态拼接查询条件: 如果minAge不为空,才会拼接  age > minAge值wrapper.gt(minAge!=null, User::getAge, minAge)//        如果maxAge不为空,才会拼接  age < maxAge值.lt(maxAge!=null, User::getAge, maxAge);List<User> users = userMapper.selectList(wrapper);for (User user : users) {System.out.println(user);}
}

4 查询投影[了解]

说明

LambdaQueryWrapper有它的局限:它只能将表里的字段替换成Lambda方法引用的形式,但是非表字段的内容,LambdaQueryWrapper就无能为力了。

这时候,只能先使用QueryWrapper设置好非表字段的内容,再将QueryWrapper转换成LambdaQueryWrapper

示例

@Test
public void test05(){LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();//select id, user_name, tel from user where age > 15 order by age descwrapper.select(User::getId, User::getUserName, User::getTel).gt(User::getAge, 15).orderByDesc(User::getAge);List<User> users = userMapper.selectList(wrapper);for (User user : users) {System.out.println(user);}
}@Test
public void test06(){QueryWrapper<User> wrapper = new QueryWrapper<>();// 要查询的内容不是表里的字段,就不能使用LambdaQueryWrapper的方式了//  先使用QueryWrapper,它更灵活,可以设置非表字段的内容wrapper.select("sex, count(*) as cnt")//然后把QueryWrapper转换成LambdaQueryWrapper.lambda().groupBy(User::getSex);List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);for (Map<String, Object> map : maps) {System.out.println(map);}
}

4. 小结

使用MP做条件性操作时,需要构造条件对象。Wrapper有两个子类:QueryWrapper, LambdaQueryWrapper

QueryWrapper的用法:

//直接new的方式。
QueryWrapper<实体类> wrapper = new QueryWrapper<实体类>().select("字段1,字段2,字段3 as 别名, ifnull(...) as xx")//条件方法:eq, ne, gt, ge, lt, le, like, notLike, in, notIn, isNull, isNotNull.....条件方法(字段名,值) .条件方法(是否要拼接此条件, 字段名,值).orderByAsc("字段名...").orderByDesc("字段名...")//直接Wrappers的静态方法方式。
QueryWrapper<实体类> wrapper = Wrappers.<实体类>query().select("字段1,字段2,字段3 as 别名, ifnull(...) as xx")//条件方法:eq, ne, gt, ge, lt, le, like, notLike, in, notIn, isNull, isNotNull.....条件方法(字段名,值) .条件方法(是否要拼接此条件, 字段名,值).orderByAsc("字段名...").orderByDesc("字段名...")    List list = xxxMapper.selectList(wrapper);

LambdaQueryWrapper的用法:

//直接new
LambdaQueryWrapper<实体类> wrapper = new LambdaQueryWrapper<实体类>().select(实体类::get属性, ....).条件方法(实体类::get属性,值).条件方法(是否要拼接此条件, 实体类::get属性,值).orderByAsc(实体类::get属性,..).orderByDesc(实体类::get属性,...)//使用Wrappers的静态方法
LambdaQueryWrapper<实体类> wrapper = Wrappers.<实体类>lambdaQuery().select(实体类::get属性, ....).条件方法(实体类::get属性,值).条件方法(是否要拼接此条件, 实体类::get属性,值).orderByAsc(实体类::get属性,..).orderByDesc(实体类::get属性,...)

五、MP分页查询【重点】

  • 掌握MP的分页查询步骤和查询方法

1. 介绍

之前我们要实现分页查询,要么在mybatis中引入pageHelper插件,要么完全手动实现分页查询。

而MybatisPlus本身就内置分页插件,直接配置即可,不需要再额外导入任何插件,也不需要我们再手动实现了。

参考:分页插件 | MyBatis-Plus

1 使用步骤

  1. 配置分页插件:创建一个配置类,在配置类中增加MP的分页插件

  2. 实现分页查询:调用Mapper的selectPage方法实现分页查询功能

2 分页API

MP的Mapper提供的分页查询方法是:IPage selectPage(IPage page, Wrapper wrapper)

  • 参数page:用于封装分页条件,包括页码和查询数量

  • 参数wrapper:用于封装查询条件,实现条件查询并分页

  • 返回值Page:分页查询的结果

IPage:是一个接口;Page是它的实现类;是分页信息对象

  • 在执行分页查询前,把分页参数封装成Page对象

  • 当执行分页查询后,MP会把查询结果封装到这个Page对象中

  • 常用方法有

2. 示例

1 配置分页插件

package com.itheima.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 MpConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {//创建拦截器对象MybatisPlusInterceptorMybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//添加分页插件PaginationInnerInterceptor,注意数据库的类型。如果数据库是MySQL,就设置DbType.MYSQLinterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}
}

2 执行分页查询

package com.itheima;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
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
public class Demo06PageTest {@Autowiredprivate UserMapper userMapper;@Testpublic void testPage(){//准备查询条件:性别为男的LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.eq(User::getSex, "男");//执行分页查询Page<User> page = userMapper.selectPage(new Page<>(1, 3), wrapper);//得到分页查询结果System.out.println("总数量:" + page.getTotal());System.out.println("总页数:" + page.getPages());System.out.println("每页几条:" + page.getSize());System.out.println("当前页码:" + page.getCurrent());List<User> list = page.getRecords();list.forEach(System.out::println);}
}

3. 小结

MP的分页查询:

  1. 先配置分页插件。在配置类或者在引导类里添加

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//如果配置多个插件,切记分页最后添加
    return interceptor;
}

    2.调用mapper接口的selectPage方法

Page<实体类> page = xxxMapper.selectPage( new Page(页码,每页几条), wrapper对象 );

page.getTotal();  //获取总数量
page.getPages();  //获取总页数
page.getRecords();//获取数据列表

六、MP的DML操作

  • 掌握MP的批量操作
  • 了解MP的逻辑删除
  • 了解MP的乐观锁

1. MP的批量操作

1 说明

在实际开发中,通常需要批量操作。例如:批量删除、批量下单等等,如果下图所示

MP提供了批量操作的一些方法,常用的有:

  • deleteBatchIds(Collection idList):根据id集合,批量删除

  • selectBatchIds(Collection idList):根据id集合,批量查询

2 示例

package com.itheima;import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.ArrayList;
import java.util.List;@SpringBootTest
public class Demo07BatchTest {@Autowiredprivate UserMapper userMapper;@Testpublic void testBatchDelete(){List<Long> ids = new ArrayList<>();ids.add(1L);ids.add(2L);ids.add(3L);userMapper.deleteBatchIds(ids);}@Testpublic void testBatchSelect(){List<Long> ids = new ArrayList<>();ids.add(1L);ids.add(2L);ids.add(3L);List<User> users = userMapper.selectBatchIds(ids);for (User user : users) {System.out.println(user);}}
}

2. MP的逻辑删除

1 说明

什么是逻辑删除

如果需要删除一条数据,开发中往往有两种方案可以实现:

  • 物理删除:真正的从数据库中把数据删除掉

  • 逻辑删除:有一个字段用于标识数据是否删除的状态。删除时仅仅是把字段设置为“已删除”状态,数据还在数据库里,并非真正的删除

在实际开发中,逻辑删除使用的更多一些。所以MP也提供了逻辑删除的支持,帮助我们更方便的实现逻辑删除

MP的逻辑删除用法

使用步骤:

  1. 修改数据库表,增加一个字段deleted。字段名称可以随意,仅仅是用于存储数据的状态的

  2. 修改实体类,增加对应的属性,并在属性上添加注解@TableLogic

  3. 修改配置文件application.yaml,声明删除与未删除的字面值

MP逻辑删除的本质是:

  • 当执行删除时,MP实际上执行的是update操作,把状态字段修改为“已删除状态”

  • 当执行查询时,MP会帮我们加上一个条件 状态字段 = 未删除,从而只查询未删除的数据

2 示例

1 增加状态字段

执行SQL语句:

use mp_db;
alter table user add deleted int default 0;

2 增加属性并加@TableLogic注解

@Data
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String userName;
    private String password;
    private Integer age;
    private String tel;
    private String sex;
    //增加@TableLogic注解,这个字段被声明为 逻辑删除状态字段
    @TableLogic
    private int deleted;
}

3 修改配置文件

mybatis-plus:
  global-config:
    db-config:
      #logic-delete-field: deleted #全局的默认逻辑删除字段名,即 状态字段名。
      logic-delete-value: 1 #已删除状态的值
      logic-not-delete-value: 0 #未删除状态的值

4 测试

@Test
public void testLogicDelete(){
    //删除id为5的数据
    userMapper.deleteById(5L);

    //查询所有数据,查询结果中没有id为5的数据。但是数据库里id为5的数据还在,只是deleted为1(已删除状态)
    List<User> users = userMapper.selectList(null);
    for (User user : users) {
        System.out.println(user);
    }
}

3. MP的乐观锁

1 说明

随着互联网的发展,特别是国内互联网人数的增加,数据量、并发量也随之增加,并发访问控制的问题也越发凸显,例如:秒杀。如果一条数据被多个线程并发修改数据,就需要对数据加锁,避免其它线程修改这条数据。

MP已经帮我们实现了乐观锁,它的使用步骤非常简单:

  1. 修改数据库表,增加一个字段version。字段名称随意,仅仅是用于记录数据版本号

  2. 修改实体类,增加对应的属性,并在属性上增加注解@Version

  3. 创建配置类,在配置类里设置好乐观锁插件

  4. 修改数据时乐观锁就生效了:但是要先查询数据,再修改数据,乐观锁才会生效;直接修改数据是不生效的

MP的乐观锁实现逻辑也非常简单:

  1. 先查询数据,得到这条数据的version版本号。假如是0

  2. 在修改数据时,带上这个version版本号

    MP执行update时,会加上 set version = version + 1 where version = 这个version版本号

    如果数据库里的数据的version,和带上的这个version不一致,就放弃修改

2 示例

1 增加版本号字段

执行以下SQL语句

use mp_db;
alter table user add version int default 0;

2 修改实体类

package com.itheima.pojo;import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;@Data
@TableName("user")
public class User {@TableId(type = IdType.AUTO)private Long id;private String userName;private String password;private Integer age;private String tel;private String sex;@TableLogicprivate int deleted;//增加对应的属性,并添加@Version注解@Versionprivate int version;  
}

3 配置乐观锁插件

在配置类(MpConfig)里,并在配置类里配置好乐观锁插件

@Configuration
public class MpConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {//创建拦截器对象MybatisPlusInterceptorMybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//添加分页插件PaginationInnerInterceptor,注意数据库的类型。如果数据库是MySQL,就设置DbType.MYSQL//interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//添加乐观锁插件OptimisticLockerInnerInterceptorinterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}
}

4 测试

    @Testpublic void testLock(){//先查询数据。假如查询到的version版本号是0User user = userMapper.selectById(6L);System.out.println(user);user.setAge(19);//再更新数据。MP会带上版本号0,//  如果数据库里的id为6的数据版本号也是0,就能修改成功,然后版本号+1变成1//  如果数据库里的id为6的数据版本号不是0,就放弃修改(MP会认为数据被其它线程修改了)userMapper.updateById(user);}

4. 小结

MP的逻辑删除:

  • 什么是逻辑删除?并不是真正的删除数据,而是把数据的状态字段值设置为“已删除”状态;查询数据时,不查询“已删除”状态的数据

  • 如何实现逻辑删除?

    1. 修改表,增加一个状态字段,用于存储数据是否删除的状态

    2. 修改实体类,增加对应的属性,并给属性上加@TableLogic

    3. 修改配置文件,告诉MP,状态字段值为几的时候是已删除,状态是几的时候是未删除

  • 逻辑删除的效果是?

    • 调用delete方法,底层实际执行的是update语句,把状态设置为“已删除”状态

    • 调用select、update等等方法时,底层会自动给SQL增加where条件 状态=未删除

悲观锁:

  • 在自己操作资源数据期间,认为总有其它线程来干扰,所以要添加排他性的、独占性的锁。

    在我加锁操作期间,其它所有线程都要阻塞排队。直到我释放锁,其他线程才可以再抢锁操作

  • 特点:

    • 安全性高,因为把多线程并发变成了串行

    • 性能较低,因为同一时间只有一个线程能操作,其它线程都是阻塞排队状态

  • 适用:写多读少的情况

  • 技术:

    • synchronized关键字

    • Lock对象

乐观锁

  • 在自己操作资源数据期间,其它线程很少来干扰,所以并不需要真正添加锁,可以有更好的性能

    自己在每次操作数据时,都先校验感知一下,数据是否被其它线程修改过了

    如果被其它线程修改过了:就放弃操作或者报错

    如果没有被其它线程修改:就直接执行操作

  • 特点:

    • 安全性足够

    • 性能比较好。因为没有真正加锁,在当前线程操作期间,可以有其它线程执行读操作,不会有任何影响

  • 适合:读多写少的情况

  • 思想:

    • CAS思想:Compare And Swap 对比并交换设置。比如:Java里的AtomicInteger底层使用了CAS思想,并不需要真正加锁,也能实现多线程操作时的线程安全性

    • 版本号:给数据设置版本号,每次变动数据时都要把版本号+1。修改数据就可以感知,数据是否被其它线程修改了

MP的乐观锁:采用的是版本号方式

  1. 给表里增加一个版本号字段

  2. 给实体类里增加对应的属性,并添加@Version

  3. 给配置类或引导类里,配置 乐观锁插件

七、MP代码生成器

  • 了解MP代码生成器的用法

1. 说明

MP可以帮我们有效的减轻开发中的工作量:所有的单表CURD完全不用自己编写了,直接使用MP就行。但是这还不够,因为我们还需要编写实体类、Mapper接口、Service、Controller代码。

为了更进一步的减轻我们的工作量,MP提供了代码生成器,可以根据数据库表,直接生成好实体类、Mapper接口,甚至Service和Controller。

​可以给idea安装 MybatisPlus 插件,它可以直接生成代码。不需要我们自己编写代码生成器代码了

1.安装MybatisPlus插件

2.使用MybatisPlus插件生成代码:配置数据库信息

3.使用MybatisPlus插件生成代码:选择表,给指定表生成代码

按下图配置,实体类会生成到 com.itheima.pojo包里;Mapper接口会生成到 com.itheima.mapper包里

最后点击按钮 “Code Generatro” 生成代码

2. 示例

1 新建一个project

创建一个新的project:

  • pom.xml内容如下,注意:必须有MybatisPlus相关的起步依赖

    <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.3</version></parent><dependencies><!--MybatisPlus的起步依赖--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version></dependency><!-- MySQL驱动包 --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.0.33</version></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.16</version></dependency></dependencies>

 

2 编写代码生成器

1 添加依赖

<!-- MP生成器 -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.4.1</version>
</dependency>
<!-- MP生成代码需要的模板引擎 -->
<dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.3</version>
</dependency>

2 编写生成器代码

精简版

package com.itheima;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;public class MpCodeGenerator {public static void main(String[] args) {// 代码生成器AutoGenerator mpg = new AutoGenerator();// 数据源配置DataSourceConfig dsc = new DataSourceConfig();dsc.setUrl("jdbc:mysql://localhost:3306/mp_db?useUnicode=true&useSSL=false&characterEncoding=utf8");dsc.setDriverName("com.mysql.cj.jdbc.Driver");dsc.setUsername("root");dsc.setPassword("root");mpg.setDataSource(dsc);//开始执行mpg.execute();}
}

详细版两个

package com.itheima;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;public class MpCodeGenerator {public static void main(String[] args) {// 代码生成器AutoGenerator mpg = new AutoGenerator();// 全局配置GlobalConfig gc = new GlobalConfig();//  代码文件生成到哪:当前工程的src/main/java里gc.setOutputDir("src/main/java");//  代码作者gc.setAuthor("liuyp");//  生成后是否直接打开文件夹gc.setOpen(false);//  是否覆盖原代码文件gc.setFileOverride(true);//  主键生成策略gc.setIdType(IdType.AUTO);mpg.setGlobalConfig(gc);// 数据源配置DataSourceConfig dsc = new DataSourceConfig();dsc.setUrl("jdbc:mysql://localhost:3306/mp_db?useUnicode=true&useSSL=false&characterEncoding=utf8");dsc.setDriverName("com.mysql.cj.jdbc.Driver");dsc.setUsername("root");dsc.setPassword("root");mpg.setDataSource(dsc);// 包配置PackageConfig pc = new PackageConfig();//  设置父包名。与代码所在文件夹不冲突pc.setParent("com.itheima");//  设置实体类所在包名。父包名+此包名,即是实体类所在位置pc.setEntity("pojo");//  设置Mapper所在包名。父包名+此包名,即是Mapper接口所在位置pc.setMapper("mapper");mpg.setPackageInfo(pc);// 策略配置StrategyConfig strategy = new StrategyConfig();//  实体类是否使用Lombokstrategy.setEntityLombokModel(true);//  是否生成Rest风格的Controller类strategy.setRestControllerStyle(true);mpg.setStrategy(strategy);mpg.execute();}
}
public class CodeGenerator {public static void main(String[] args) {//1.获取代码生成器的对象AutoGenerator autoGenerator = new AutoGenerator();//设置数据库相关配置DataSourceConfig dataSource = new DataSourceConfig();dataSource.setDriverName("com.mysql.cj.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC");dataSource.setUsername("root");dataSource.setPassword("root");autoGenerator.setDataSource(dataSource);//设置全局配置GlobalConfig globalConfig = new GlobalConfig();globalConfig.setOutputDir(System.getProperty("user.dir")+"/mybatisplus_04_generator/src/main/java");    //设置代码生成位置globalConfig.setOpen(false);    //设置生成完毕后是否打开生成代码所在的目录globalConfig.setAuthor("黑马程序员");    //设置作者globalConfig.setFileOverride(true);     //设置是否覆盖原始生成的文件globalConfig.setMapperName("%sDao");    //设置数据层接口名,%s为占位符,指代模块名称globalConfig.setIdType(IdType.ASSIGN_ID);   //设置Id生成策略autoGenerator.setGlobalConfig(globalConfig);//设置包名相关配置PackageConfig packageInfo = new PackageConfig();packageInfo.setParent("com.aaa");   //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径packageInfo.setEntity("domain");    //设置实体类包名packageInfo.setMapper("dao");   //设置数据层包名autoGenerator.setPackageInfo(packageInfo);//策略设置StrategyConfig strategyConfig = new StrategyConfig();strategyConfig.setInclude("tbl_user");  //设置当前参与生成的表名,参数为可变参数strategyConfig.setTablePrefix("tbl_");  //设置数据库表的前缀名称,模块名 = 数据库表名 - 前缀名  例如: User = tbl_user - tbl_strategyConfig.setRestControllerStyle(true);    //设置是否启用Rest风格strategyConfig.setVersionFieldName("version");  //设置乐观锁字段名strategyConfig.setLogicDeleteFieldName("deleted");  //设置逻辑删除字段名strategyConfig.setEntityLombokModel(true);  //设置是否启用lombokautoGenerator.setStrategy(strategyConfig);//2.执行生成操作autoGenerator.execute();}
}

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

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

相关文章

字符串漏洞注入深入学习

字符串型漏洞注入&#xff0c;特别是针对Web应用程序的SQL注入&#xff0c;是一种常见的网络安全威胁。它涉及攻击者在不受控制的情况下&#xff0c;通过构造特定的字符串输入&#xff0c;干扰或改变应用程序中原有的SQL查询语句&#xff0c;从而执行恶意的SQL代码。 要深入学…

微软Phi-3,3.8亿参数能与Mixtral 8x7B和GPT-3.5相媲美,量化后还可直接在IPhone中运行

Phi-3系列 Phi-3是一系列先进的语言模型&#xff0c;专注于在保持足够紧凑以便在移动设备上部署的同时&#xff0c;实现高性能。Phi-3系列包括不同大小的模型&#xff1a; Phi-3-mini&#xff08;38亿参数&#xff09; - 该模型在3.3万亿个令牌上进行训练&#xff0c;设计得足…

【Stable Diffusion系列】(一):AI绘画本地部署教程

目录 一、总览 二、本地部署 1、安装cuda 2、安装python 3、安装git 4、方法一 1&#xff09;获取安装包 2&#xff09;update 3&#xff09;run 5、方法二 1&#xff09;git clone 2&#xff09;双击webui-user.bat 3&#xff09;更新 6、设置启动参数 7、…

指针(5)

前言 本节是有关指针内容的最后一节&#xff0c;本节的内容以讲解指针习题为主&#xff0c;那么就让我们一起来开启本节的学习吧&#xff01; sizeof和strlen的对比 1.sizeof 我们在学习操作符的时候&#xff0c;学习了sizeof。sizeof存在的意义是用来计算变量所占用的内存空…

AI大模型日报#0424:全球首个AI基因编辑器、出门问问上市、微软开源Phi-3 Mini、昆仑万维年收49亿

导读&#xff1a; 欢迎阅读《AI大模型日报》&#xff0c;内容基于Python爬虫和LLM自动生成。目前采用“文心一言”生成了每条资讯的摘要。标题: 爱诗科技完成A2轮超亿元融资&#xff0c;蚂蚁集团领投摘要: 爱诗科技完成A2轮超亿元融资&#xff0c;成为视频大模型领域融资规模最…

STM32学习和实践笔记(20):定时器

1.定时器介绍 STM32F1的定时器一共有8个&#xff0c;由2个基本定时器&#xff08;TIM6、TIM7&#xff09;、4个通用定时器&#xff08;TIM2-TIM5&#xff09;和2个高级定时器&#xff08;TIM1、TIM8&#xff09;组成。 基本定时器的功能最为简单&#xff0c;类似于51单片机内定…

【行为型模式】中介者模式

一、中介者模式概述 中介者模式定义&#xff1a;用一个中介对象来封装一系列的对象交互&#xff0c;中介者使各对象不需要显式地相互引用&#xff0c;从而使其耦合松散&#xff0c;而且可以独立地改变它们之间的交互。中介者模式又称为调停者模式。(对象行为型模式) 中介者模式…

python+django校园社交高校交友网站2x7r5.

本课题使用Python语言进行开发。代码层面的操作主要在PyCharm中进行&#xff0c;将系统所使用到的表以及数据存储到MySQL数据库中&#xff0c;方便对数据进行操作本课题基于WEB的开发平台&#xff0c;设计的基本思路是&#xff1a; 前端&#xff1a;vue.jselementui 框架&#…

Node.JS安装及配置教程(Windows)【安装】

文章目录 一、 Node.JS 下载1. 官网下载&#xff08;1&#xff09;国内地址&#xff08;2&#xff09;国外地址 2. 其它渠道 二、 Node.JS 安装三、 Node.JS验证四、 Node.JS 配置&#xff08;可选&#xff09;1. 配置全局模块安装路径方法一方法二2. 配置国内镜像 五、 yarn 安…

企业数智化:为什么选择梧桐数据库?

个人介绍&#xff1a;艺名司镜233&#xff0c;是中国移动梧桐数据库研发团队成员&#xff0c;从事相关的技术开发近5年了。最让我觉得自豪的不是在研发这款数据库&#xff0c;而是我们用代码&#xff0c;切实地帮助企业解决数据的困扰&#xff0c;切实地解决社会的问题。 本篇文…

【炼金术士】BatchSize对网络训练的影响

文章目录 1 BatchSize对于网络训练的影响2 调整学习率可以提高大BatchSize的性能3 实际训练时的建议3.1 设置初始学习率的方法3.2 多卡训练时学习率的设置 参考资料&#xff1a; 【深度学习】Batch Size对神经网络训练的影响【AI不惑境】学习率和batchsize如何影响模型的性能&…

AXI4---低功耗接口

在电子系统设计中&#xff0c;"low-power interface"&#xff08;低功耗接口&#xff09;是指专为减少能耗而设计的硬件接口。这类接口在不需要牺牲性能的情况下&#xff0c;通过各种技术降低功耗&#xff0c;对于移动设备、嵌入式系统和其他电池供电的应用来说尤其重…

如何更好的管理个人财务?使用极空间部署私有记账系统Firefly III

如何更好的管理个人财务&#xff1f;使用极空间部署私有记账系统Firefly III 哈喽小伙伴们好&#xff0c;我是Stark-C~ 不知道屏幕前的各位“富哥”日常生活中是怎么管理自己巨额财富的&#xff0c;反正对于像我这样年薪过千的摸鱼族来说&#xff0c;请一个专业的理财顾问多多…

Laravel 6 - 第十三章 请求

​ 文章目录 Laravel 6 - 第一章 简介 Laravel 6 - 第二章 项目搭建 Laravel 6 - 第三章 文件夹结构 Laravel 6 - 第四章 生命周期 Laravel 6 - 第五章 控制反转和依赖注入 Laravel 6 - 第六章 服务容器 Laravel 6 - 第七章 服务提供者 Laravel 6 - 第八章 门面 Laravel 6 - …

记录第一次云服务器redis被黑

redis里莫名奇妙被写入四个键值对&#xff0c;backup1,backup2,backup3,backup4&#xff0c;内容是奇奇怪怪的sh脚本&#xff1a;*/5 * * * * root wd1 -q -O- http://45.83.123.29/cleanfda/init.sh | sh http://en2an.top/cleanfda/init.sh */2 * * * * root cd1 -fsSL http…

The_Maya_Society

突然发现自己做了一些逆向题都没有写笔记 今天&#xff0c;发现这道题有意思 1.解压文件 三个文件The Maya Society.html&#xff0c;maim.cc,maya.png 当时我看到这个题的时候&#xff0c;我以为是不是会是js逆向 看来是我蠢了 这三个文件&#xff0c;main.css和maya.png这两…

PyQt5的安装和配置

1.准备工作 首先&#xff0c;下载Pycharm及python-3.7.5-amd64.exe并安装两个软件。 Pycharm链接&#xff1a; python-3.7.5-amd64.exe链接&#xff1a; 2.1.在线安装 pip安装PyQt5&#xff1a; pip install PyQt5 pip安装pyqt5-tools pip install pyqt5-tools 遇到下载…

编译原理 LR(0)

讲解视频&#xff1a;编译原理LR&#xff08;0&#xff09;分析表&#xff08;上&#xff09;_哔哩哔哩_bilibili 【编译原理】LR(0)分析表分析输入串_哔哩哔哩_bilibili 拓广文法 已知G&#xff1a;S->(S)S | ε 拓广文法&#xff1a; S -> S S -> (S)S S -> ε…

基于springboot的公交线路查询系统设计与实现

第1章 绪论 1.1 研究背景 互联网时代不仅仅是通过各种各样的电脑进行网络连接的时代&#xff0c;也包含了移动终端连接互联网进行复杂处理的一些事情。传统的互联网时代一般泛指就是PC端&#xff0c;也就是电脑互联网时代&#xff0c;但是最近几十年&#xff0c;是移动互联网…

中台架构下的性能测试实践方法

有同学私信问我&#xff1a;中台服务建设过程中&#xff0c;性能测试如何开展&#xff1f;问题背景如下&#xff1a; 业务背景&#xff1a;银行业务&#xff1b; 技术架构&#xff1a;业务应用和中台之间请求统一走ESB&#xff1b; 当前阶段&#xff1a;中台建设中&#xff0c;…