当前位置: 首页 > news >正文

长沙视频制作公司/seo排名培训公司

长沙视频制作公司,seo排名培训公司,做网站方面的问题,网站双链接怎么做MyBatis-Plus 1、mybatis-plus介绍 官网:https://baomidou.com/ MyBatis-Plus (简称 MP)是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。MyBatis-Plus提供了通用的mapper…

MyBatis-Plus

1、mybatis-plus介绍

官网:https://baomidou.com/

MyBatis-Plus (简称 MP)是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。MyBatis-Plus提供了通用的mapper和service,可以在不编写任何SQL语句的情况下,快速的实现对单表的CRUD批量、逻辑删除、分页等操作。

1.1、特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

1.2、支持数据库

任何能使用 MyBatis 进行 CRUD, 并且支持标准 SQL 的数据库,具体支持情况如下,如果不在下列表查看分页部分教程 PR 您的支持。

  • MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb,informix,TDengine,redshift
  • 达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库,优炫数据库

1.3、代码托管

  • gitee:https://gitee.com/baomidou/mybatis-plus
  • GitHub:https://github.com/baomidou/mybatis-plus

2、入门案例

2.1、开发环境

IDE: IDEA 2023.1

JDK:JDK8+

构建工具:maven3.8.3

MySQL版本:MySQL5.5.27

SpringBoot:2.7.6

mybatis-plus:3.5.3.1

2.2、创建数据库及表

-- 创建数据库
CREATE DATABASE mybatisplus;
-- 使用数据库
USE mybatisplus;
-- 创建表
CREATE TABLE USER
(id BIGINT(20) NOT NULL COMMENT '主键ID',NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',age INT(11) NULL DEFAULT NULL COMMENT '年龄',email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',PRIMARY KEY (id)
);

2.3、表中添加数据

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

2.4、创建SpringBoot项目

使用Spring Initializr 快速构建SpringBoot项目

请添加图片描述
项目中没有用途的几个文件可以删除

请添加图片描述

2.5、添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.6</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.qbzaixian</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>demo</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- mybatis-plus的启动器--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version></dependency><!-- lombok简化实体类开发,需要idea安装lombok的插件哦~~~--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.8</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.34</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

2.6、配置文件

spring:datasource:# 配置数据源类型type: com.zaxxer.hikari.HikariDataSource# 配置连接数据库的驱动driver-class-name: com.mysql.jdbc.Driver# 配置连接数据库的urlurl: jdbc:mysql://127.0.0.1:3306/mybatisplus?characterEncoding=utf-8&useSSL=false# 配置连接数据库的账号username: root# 配置连接数据库的密码password: 123

2.7、配置实体类

@Data
public class User {private Long id;private String name;private Integer age;private String email;
}

2.8、编写mapper接口

mybatis-plus提供了BaseMapper接口,其中提供大量的CRUD的方法,我们的接口只需要去继承这个接口,基本就可以实现对单表的CRUD操作。接口的泛型对应编写的实体类

public interface UserMapper extends BaseMapper<User> {
}

2.9、编写启动类

在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹:

@SpringBootApplication
@MapperScan("com.qbzaixian.mapper")
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}

2.10、编写测试类

UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper,所以不填写就是无任何条件

@SpringBootTest
public class UserTest {@Autowiredprivate UserMapper userMapper;@Testpublic void testSelect(){// 接口中提供的selectList参数为条件,如果没有设置为nullList<User> users = this.userMapper.selectList(null);users.forEach(System.out::println);}
}

请添加图片描述

这里我选择降低SpringBoot的版本为2.7.6来解决这个问题。

 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.6</version><relativePath/> <!-- lookup parent from repository --></parent

运行测试类,看到如下测试结果

请添加图片描述

通过以上几个简单的步骤,我们就实现了 User 表的 CRUD 功能,甚至连 XML 文件都不用编写!

从以上步骤中,我们可以看到集成MyBatis-Plus非常的简单,只需要引入 starter 工程,并配置 mapper 扫描路径即可。

2.11、添加日志

希望看到mybatis-plus执行的具体过程和对应的sql语句,需要简单的配置mybatis-plus的日志即可

# 配置日志
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3、BaseMapper接口

mybatis-plus提供了BaseMapper接口,其中提供大量的CRUD的方法,我们的接口只需要去继承这个接口,基本就可以实现对单表的CRUD操作。接口的泛型对应编写的实体类

3.1、insert插入

参数说明

类型参数名描述
Tentity实体对象
// 插入一条记录
int insert(T entity);
    // BaseMapper的新增功能// INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )@Testpublic void testInset(){User user = new User();user.setAge(20);user.setName("赵四");user.setEmail("zhaosi@qq.com");int result = this.userMapper.insert(user);System.out.println(result);}

温馨提示:添加完成之后,添加的新对象也会进行ID回显的,但ID值默认采用的是雪花算法计算出来的数据,不是一个自增长的值。

3.2、delete删除

参数说明

类型参数名描述
Wrapperwrapper实体对象封装操作类(可以为 null)
Collection<? extends Serializable>idList主键 ID 列表(不能为 null 以及 empty)
Serializableid主键 ID
Map<String, Object>columnMap表字段 map 对象
// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 删除(根据ID 批量删除)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 ID 删除
int deleteById(Serializable id);
// 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
    // BaseMapper的删除功能@Testpublic void testDel(){// 根据id删除 DELETE FROM user WHERE id=?int i = this.userMapper.deleteById(1L);// 根据实体对象的id删除 DELETE FROM user WHERE id=?User user= new User();user.setId(2L);int i2 = this.userMapper.deleteById(user);// 根据Collection集合删除(id列表)// DELETE FROM user WHERE id IN ( ? , ? , ? )List<Long> list = Arrays.asList(2L, 3L, 4L);this.userMapper.deleteBatchIds(list);// 根据指定的列属性删除// DELETE FROM user WHERE name = ? AND age = ?Map<String,Object> map = new HashMap<>();map.put("age",20);map.put("name","赵四");int i3 = this.userMapper.deleteByMap(map);}

3.3、update修改

在调用updateById方法前,需要在T entity(对应的实体类)中的主键属性上加上@TableId注解。

参数说明

类型参数名描述
Tentity实体对象 (set 条件值,可为 null)
WrapperupdateWrapper实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
// 根据 whereWrapper 条件,更新记录
int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);
// 根据 ID 修改
int updateById(@Param(Constants.ENTITY) T entity);
    @Testpublic void testUpdate(){// 根据id修改数据 UPDATE user SET name=?, email=? WHERE id=?User user = new User();user.setId(5L);user.setName("乔治");user.setEmail("qiaozhi@.qq.com");int i = this.userMapper.updateById(user);}

3.4、select查询

参数说明

类型参数名描述
Serializableid主键 ID
WrapperqueryWrapper实体对象封装操作类(可以为 null)
Collection<? extends Serializable>idList主键 ID 列表(不能为 null 以及 empty)
Map<String, Object>columnMap表字段 map 对象
IPagepage分页查询条件(可以为 RowBounds.DEFAULT)
// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 查询(根据ID 批量查询)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据 columnMap 条件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
-- 由于前面演示删除,修改等操作,数据库中的数据已经不多了,现在给数据库中重新插入部分数据
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com');
    @Testpublic void testSelectAll(){// selectList:根据条件查询,如果没有条件书写为null// SELECT id,name,age,email FROM userList<User> users = this.userMapper.selectList(null);users.forEach(System.out::println);// selectById:根据id查询// SELECT id,name,age,email FROM user WHERE id=?User user = this.userMapper.selectById(5L);System.out.println(user);// selectCount:查询满足条件的记录数,如果没有条件书写为null// SELECT COUNT( * ) AS total FROM userLong count = this.userMapper.selectCount(null);System.out.println(count);// selectBatchIds:通过多个id列表查询// SELECT id,name,age,email FROM user WHERE id IN ( ? , ? , ? )List<User> userList = this.userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));userList.forEach(System.out::println);// selectByMap:根据指定map中的属性作为查询条件// SELECT id,name,age,email FROM user WHERE name = ? AND age = ?Map<String,Object> map = new HashMap<>();map.put("name","乔治");map.put("age","20");List<User> list = this.userMapper.selectByMap(map);list.forEach(System.out::println);}

3.5、自定义接口方法

mybatis-plus主要是单表的操作,如果我们需要执行多表,或者执行自己书写的sql脚本与接口,mybatis-plus也支持指定的方式。

在mybaits-plus的MybatisPlusProperties 类中配置相关的属性配置,其中

private String[] mapperLocations = new String[]{"classpath*:/mapper/**/*.xml"};

用来加载指定路径下的xml映射文件的,当然如果你不喜欢这个路径,可以在SpringBoot的配置文件中进行修改

mybatis-plus:
config-location: 书写自己的路径

在项目resoureces 目录下,创建 mapper 文件夹,在其中创建UserMapper.xml 文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qbzaixian.mapper.UserMapper"><select id="selectUserById" resultType="map">select * from user where id = #{id}</select>
</mapper>

在接口中编写对应的查询方法

public interface UserMapper extends BaseMapper<User> {/*** 根据指定的id,查询user数据,返回map集合* @param id* @return*/public Map<String,Object> selectUserById(Long id);
}

执行查询操作:

    @Testpublic void testMyQuery(){// 测试自定义的接口方法Map<String, Object> map = this.userMapper.selectUserById(1L);System.out.println(map);}

4、通用Service接口

mybatis-plus不仅提供通用的mapper接口,还提供通用的Service接口。

  • 通用 Service CRUD 封装IService (opens new window)接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,
  • 泛型 T 为任意实体对象
  • 建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类
  • 对象 Wrapper 为 条件构造器

4.1、IService接口

Mybatis-Plus中提供的IService接口和实现类ServiceImpl,封装了常见业务逻辑,可以简单查阅源码

public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {// 提供了大量的CRUD相关的方法
}

4.2、测试Service接口

通过前面的mapper接口的CRUD演示,针对Service接口中的大部分方法使用基本一致,这里就简单演示批量插入操作

虽然有ServiceImpl实现类和IService接口,但是大部分情况下还是需要根据对应的业务书写相关接口和实现类

// 在service包下创建UserService接口
public interface UserService extends IService<User> {
}
// 在service.impl包下创建UserServiceImpl实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

准备进行测试

@SpringBootTest
public class UserServiceTest {@Autowiredprivate UserServiceImpl userService;@Testpublic void testSaveBatch(){List<User> list = new ArrayList<>();for (int i = 1 ; i < 5 ; i++){User user = new User();user.setName("测试"+i);user.setAge(20+i);user.setEmail("test@163.com");list.add(user);}// 批量给数据库中插入数据,在mapper接口中是没有的boolean b = this.userService.saveBatch(list);System.out.println(b);}
}

5、注解介绍

mybatis-plus提供部分的注解,方便快速的进行表、属性、主键、主键生成策略等进行标注

5.1、@TableName注解

  • 描述:表名注解,标识实体类对应的表
  • 使用位置:实体类
@Data
@TableName("user")
public class User {private Long id;private String name;private Integer age;private String email;
}

一般用在实体类名与对应的表名不一致的情况下,当然如果项目中整个表名都有对应的前缀,也可以在SpringBoot的核心配置文件进行前缀的配置,省去注解的配置

mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 全局配置表名的前缀  global-config:db-config:table-prefix: tb_

5.2、@TableId注解

用于标注当前的实体类的id属性对应表的主键,mybatis-plus默认采用id属性作为主键。

  • 描述:主键注解
  • 使用位置:实体类主键字段
@Data
@TableName("user")
public class User {@TableIdprivate Long id;private String name;private Integer age;private String email;
}

TableId注解的两个属性作用:

属性类型必须指定默认值描述
valueString“”主键字段名
typeEnumIdType.NONE指定主键类型

mybatis-plus在设置逐渐的时候ltype属性用来设置主键的生成策略:

@Data
@TableName("user")
public class User {@TableId(value='对应的表主键列名' , type=主键策略)private Long id;private String name;private Integer age;private String email;
}

关于type属性(主键策略)的枚举值IdType的详细介绍:

常用的有:

@TableId(value='对应的表主键列名' , type=idType.AUTO)
@TableId(value='对应的表主键列名' , type=idType.ASSIGN_ID)
描述
AUTO数据库 ID 自增
NONE无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
INPUTinsert 前自行 set 主键值
ASSIGN_ID分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
ASSIGN_UUID分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认 default 方法)
ID_WORKER分布式全局唯一 ID 长整型类型(please use ASSIGN_ID)
UUID32 位 UUID 字符串(please use ASSIGN_UUID)
ID_WORKER_STR分布式全局唯一 ID 字符串类型(please use ASSIGN_ID)

关于主键生成策略,可以在SpringBoot的全局配置文件中进行配置

# 配置日志
mybatis-plus:
configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 全局配置
global-config:db-config:# 配置表的名的前缀table-prefix: tb_# 配置主键生成策略id-type: auto

5.3、雪花算法

SnowFlake 中文意思为雪花,故称为雪花算法。最早是 Twitter 公司在其内部用于分布式环境下生成唯一 ID。在2014年开源 scala 语言版本。

请添加图片描述

雪花算法的原理就是生成一个的 64 位比特位的 long 类型的唯一 id。

  • 最高 1 位固定值 0,因为生成的 id 是正整数,如果是 1 就是负数了。
  • 接下来 41 位存储毫秒级时间戳,2^41/(1000606024365)=69,大概可以使用 69 年。
  • 再接下 10 位存储机器码,包括 5 位 datacenterId 和 5 位 workerId。最多可以部署 2^10=1024 台机器。
  • 最后 12 位存储序列号。同一毫秒时间戳时,通过这个递增的序列号来区分。即对于同一台机器而言,同一毫秒时间戳下,可以生成 2^12=4096 个不重复 id。
  • 可以将雪花算法作为一个单独的服务进行部署,然后需要全局唯一 id 的系统,请求雪花算法服务获取 id 即可。

对于每一个雪花算法服务,需要先指定 10 位的机器码,这个根据自身业务进行设定即可。例如机房号+机器号,机器号+服务号,或者是其他可区别标识的 10 位比特位的整数值都行。

简单说雪花算法解决的问题:

  • 需要选择合适的方案应对数据规模化的增长,以应对逐渐增长的访问压力与数据量。
  • 数据库扩展方式主要包括:业务分库、主从复制、数据库分表等。

不同的编程语言,都有雪花算法的实现,可以直接调用对应的程序即可得到一个雪花算法值。

5.4、@TableField注解

作用:字段注解(非主键),解决表的属性名与实体中的属性名不一致问题。

@Data
@TableName("user")
public class User {@TableIdprivate Long id;@TableField("nickname")private String name;private Integer age;private String email;
}

@TableField注解的属性比较多,常用的就是标注列名(即value属性,默认可以不写)

其他的属性如果需要,可以参考官方文档

5.5、@TableLogic注解

作用:表字段逻辑处理注解(逻辑删除)

物理删除:真实删除,将对应的数据从数据库中删除,之后查询不到此条被删除的数据

逻辑删除:假删除,将对应的数据中代表是否删除的字段状态修改被删除,在数据库可以查到这条数据记录,只是标记为被删除

使用场景:可以进行数据恢复

@Data
@TableName("user")
public class User {@TableIdprivate Long id;@TableField("nickname")private String name;private Integer age;private String email;@TableLogicprivate Integer isDeleted;
}

注意:

  • 上面使用isDeleted 属性来标准逻辑删除,就需要在表中添加这么一列,可以将未删除的状态设置为0,删除就对应的为1,
  • 在进行查询时候,默认会添加 where is_deleted = 0 表示只查询未被删除的,
  • 在逻辑删除的时候,默认会将 is_deleted 的值设置为1

6、条件构造器

6.1、条件构造器介绍

在使用mybatis-plus的时候,发现修改、删除、查询的都对应有Wrapper,他就是用于构建各种条件。是构造条件的顶级父类。

  • Wrapper:条件构造器
    • AbstractWrapper:用于条件封装,生成sql的where条件
      • QueryWrapper:查询条件封装
      • UpdateWrapper:修改条件封装
      • AbstractLambdaWrapper:使用Lambda语法
        • LambdaQueryWrapper:用于Lambda语法使用的查询Wrapper
        • LambdaUpdateWrapper:Lambda更新封装Wrapper

6.2、QueryWrapper查询条件

    @Testpublic void testQueryWrapper(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询姓名中包含字母o,邮箱不为null,年龄大于等于20的wrapper.like("name","o").isNotNull("email").ge("age",20);List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}

最终生成的sql:SELECT id,name,age,email FROM user WHERE (name LIKE ? AND email IS NOT NULL AND age >= ?)

6.3、QueryWrapper排序条件

    @Testpublic void testQueryWrapper2(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询年龄在20到30之间的,先按照年龄降序,年龄相同在按照姓名升序wrapper.between("age",20,30).orderByDesc("age").orderByAsc("name");List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}

最终生成的sql:SELECT id,name,age,email FROM user WHERE (age BETWEEN ? AND ?) ORDER BY age DESC,name ASC

6.4、QueryWrapper删除条件

    @Testpublic void testQueryWrapper3(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 删除邮箱为空的数据wrapper.isNull("email");int i = this.userMapper.delete(wrapper);System.out.println(i);}

最终生成的sql:DELETE FROM user WHERE (email IS NULL)

6.5、QueryWrapper实现修改

    @Testpublic void testQueryWrapper4(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 修改name中包含字母a的数据wrapper.like("name","a");User user = new User();user.setAge(26);user.setEmail("bbb@qq.com");int i = this.userMapper.update(user, wrapper);System.out.println(i);}

最终生成的sql:UPDATE user SET age=?, email=? WHERE (name LIKE ?)

6.6、QueryWrapper查询指定的列

    @Testpublic void testQueryWrapper5(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询name、age两列数据wrapper.select("name","age");List<Map<String, Object>> maps = this.userMapper.selectMaps(wrapper);maps.forEach(System.out::println);}

最终生成的sql:SELECT name,age FROM user

6.7、QueryWrapper更改查询的条件优先级

默认情况下,使用QueryWrapper进行条件组装的时候,多个条件之间使用的and进行连接,QueryWrapper中提供and和or方法,可以提供指定的查询条件的优先级。

    @Testpublic void testQueryWrapper6(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询年龄在20到30之间并且(姓名包含字母a或者邮箱不为null的数据)wrapper.between("age",20,30).and(i->i.like("name","a").or().isNotNull("email"));List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}

最终生成的sql:

SELECT id,name,age,email FROM user WHERE (age BETWEEN ? AND ? AND (name LIKE ? OR email IS NOT NULL))

6.8、QueryWrapper实现子查询

    @Testpublic void testQueryWrapper7(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询年龄在20到30之间的所有数据,这里故意采用子查询的方式完成wrapper.inSql("age","select age from user where age >= 20 and age <= 30");List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}

最终生成的sql:

SELECT id,name,age,email FROM user WHERE (age IN (select age from user where age >= 20 and age <= 30))

6.9、使用UpdateWrapper实现修改

虽然QueryWrapper可以完成修改操作的,但是需要传递实体对象,还是有点小麻烦。提供的UpdateWrapper是专门用于完成修改条件和数据封装

    @Testpublic void testUpdateWrapper(){// 创建UpdateWrapper对象,用于构建查询条件UpdateWrapper<User> wrapper = new UpdateWrapper<>();// 修改name中包含字母a的数据// 设置修改的的条件wrapper.like("name","a");// 设置需要修改的数据wrapper.set("age",22).set("email","abc@qq.com");int i = this.userMapper.update(null, wrapper);System.out.println(i);}

最终生成的sql:

UPDATE user SET age=?,email=? WHERE (name LIKE ?)

6.10、使用condition组装条件

condition用于在进行条件封装的时候,判断某个值,当这个值不为null的时候,会自动添加对应的条件,如果为null,就不会添加条件

简单说:condition可以动态根据条件组装条件

    @Testpublic void testQueryWrapper9(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 根据指定的数据,添加查询条件,模拟多条件查询的场景String name = "a";Integer age = 20;String email = null;wrapper.like(StringUtils.isNotBlank(name),"name",name).gt(age!=null , "age",age).eq(StringUtils.isNotBlank(email),"email",email);List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}

最终生成的sql:生成的sql中并不包含email,因为email为null

SELECT id,name,age,email FROM user WHERE (name LIKE ? AND age > ?)

6.11、LambdaQueryWrapper

Lambda相关的查询与修改构造器,针对QueryWrapper和UpdateWrapper在构造添加的时候,需要书写表的列名进行优化,通过Lambda相关的条件构造器,可以将对应的条件采用实体类的实型进行编写。

    @Testpublic void testQueryWrapper10(){// 创建QueryWrapper对象,用于构建查询条件LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();// 查询姓名中包含字母o,邮箱不为null,年龄大于等于20的wrapper.like(User::getName,"o").isNotNull(User::getEmail).ge(User::getAge,20);List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}

最终生成的sql:生成的sql中并不包含email,因为email为null

SELECT id,name,age,email FROM user WHERE (name LIKE ? AND email IS NOT NULL AND age >= ?)

6.12、LambdaUpdateWrapper

    @Testpublic void testUpdateWrapper(){// 创建UpdateWrapper对象,用于构建查询条件LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();// 修改name中包含字母a的数据// 设置修改的的条件wrapper.like(User::getName,"a");// 设置需要修改的数据wrapper.set(User::getAge,22).set(User::getEmail,"abc@qq.com");int i = this.userMapper.update(null, wrapper);System.out.println(i);}

最终生成的sql:生成的sql中并不包含email,因为email为null

UPDATE user SET age=?,email=? WHERE (name LIKE ?)

7、MyBatis-Plus分页

7.1、分页插件的配置和使用

MyBatis-Plus的分页非常简单,只需要添加一个配置类即可,而MyBatis-Plus提供的配置是以插件的方式提供。

@Configuration
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){// 创建拦截器对象MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 设置分页的拦截器,并设置数据库类型为mysqlinterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));// 返回拦截器对象return interceptor;}
}

通过上面代码的配置,就可以使用分页功能

    @Testpublic void testPagination(){LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.ge(User::getAge,20);Page<User> page = new Page<>(1,3);// 如果没有条件,第二个参数可以设置为nullthis.userMapper.selectPage(page , wrapper);// 查询的结果就放在Page对象中// 获取当前分页总页数System.out.println(page.getPages());// 每页显示条数,默认 10System.out.println(page.getSize());// 当前页System.out.println(page.getCurrent());// 当前满足条件的总数System.out.println(page.getTotal());// 分页查询的数据System.out.println(page.getRecords());}

7.2、自定义分页功能

在实际使用中,难免会出现需要自己书写sql语句,同时还需要使用分页功能,这时就需要自定义分页功能

需要注意:

  • 自定义Mapper接口中的方法的第一个参数必须是MybatisPlus提供的分页Page对象。
  • 自定义Mapper接口中的方法返回值必须是Page对象
public interface UserMapper extends BaseMapper<User> {@Select("select * from user where age > #{age}")public Page<User> selectUserAndPage(@Param("page") Page<User> page , @Param("age") Integer age);
}
    @Testpublic void testPagination2(){Page<User> page = new Page<>(1,3);// 如果没有条件,第二个参数可以设置为nullthis.userMapper.selectUserAndPage(page , 20);// 查询的结果就放在Page对象中// 获取当前分页总页数System.out.println(page.getPages());// 每页显示条数,默认 10System.out.println(page.getSize());// 当前页System.out.println(page.getCurrent());// 当前满足条件的总数System.out.println(page.getTotal());// 分页查询的数据System.out.println(page.getRecords());}

7.3、乐观锁与悲观锁

MySQL中的乐观锁和悲观锁主要区别如下:

  1. 悲观锁:
  • 悲观锁会在更新数据时对数据加锁,避免其他事务对此数据进行更新。
  • 通常使用锁定的SELECT…FOR UPDATE语句来实现。
  • 在整个更新操作过程中,数据被锁定,无法进行其他更新操作,保证数据consistency。
  • 效率低,锁定时间长,发生锁争用的概率大。
  1. 乐观锁:
  • 乐观锁不会对数据加锁,只在更新时检查版本号,如果版本号不同,则说明数据已经被更新。
  • 通常使用数据版本号version来实现。
  • 在更新时,通过where 条件检查version号是否与最新的相同。如果不同就代表已经更新过了。
  • 效率高,不会发生死锁。

InnoDB存储引擎从MySQL 5.5开始支持行锁,可以实现行级别的悲观锁。

总体来说,乐观锁适用于写比较少的场景,可以提高吞吐量。悲观锁适用于写比较多的场景,可以保证数据的完整性。

7.4、mybatis-plus的乐观锁插件

为了测试,重新创建一张表

CREATE TABLE goods(id INT PRIMARY KEY AUTO_INCREMENT,NAME VARCHAR(100),price DOUBLE ,num INT,VERSION INT DEFAULT 0
);
INSERT INTO goods(id,NAME,price,num,VERSION) VALUES(NULL,"华为手机",100,3,0);

需要在数据库表中添加一列version,用于乐观锁的版本号确认,需要在实体类中使用@Version注解。

@Data
@TableName("goods")
public class Goods {@TableIdprivate Long id;private String name;private Integer price;private Integer num;@Versionprivate Integer version;
}

需要在拦截器中添加乐观锁的插件

@Configuration
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){// 创建拦截器对象MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 设置分页的拦截器,并设置数据库类型为mysqlinterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));// 添加乐观锁的拦截器interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());// 返回拦截器对象return interceptor;}
}
@SpringBootTest
public class GoodsTest {@Autowiredprivate GoodsMapper goodsMapper;@Testpublic void test(){// 架设一个操作:需要对商品的价格先增加50%,然后在优惠20%// 架设另一个操作:需要对商品的价格在前一个修改后的基础上在优惠10%// 但是可能会出现两个操作同时进行的情况,就需要通过乐观锁进行控制// 架设两个操作同时进行,操作之前需要先查询到当前商品的数据信息Goods goods = this.goodsMapper.selectById(1);Goods goods2 = this.goodsMapper.selectById(1);// 开始执行 价格先增加50%goods.setPrice(goods.getPrice()*1.5);// 然后在优惠20%goods.setPrice(goods.getPrice()*0.8);// 将数据跟新到数据库int i = this.goodsMapper.updateById(goods);System.out.println(i);// goods2进行更新goods2.setPrice(goods2.getPrice()*0.9);// goods2更新不会成功this.goodsMapper.updateById(goods2);// 若要更新成功,就必须在更新失败后,重新获取数据,对最新的数据进行更新}
}

通过执行的过程中产生的sql语句,会发现,version作为where的条件存在

UPDATE goods SET name=?, price=?, num=?, version=? WHERE id=? AND version=?

8、代码生成器

MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。

安装:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。安装成功会提示重启idea,我们重庆idea之后,就可以使用MybatisX插件了

使用前提:需要配置数据库源

请添加图片描述

选择 + 按钮,然后选择Data Source 菜单,找到 MySql 选项
请添加图片描述

在弹出窗口中,填写对应的信息,最后点击OK ,即可完成数据源的配置

请添加图片描述

打开数据源,找到对应的表,右击选择MyBatisX-Generator

请添加图片描述

需要根据提示,填写对应的内容

请添加图片描述

继续完成对应的配置

请添加图片描述

最后选择Finish ,即可生成最基础的模版,然后就可以开始愉快的编程了。

在mybatis-plus默认的功能不够的时候,就可以借助MyBaitsX快速给Mapper接口添加对应的方法,同时也会在mapper文件中生成对应的sql 语句。

相关文章:

MyBatis-Plus的使用

MyBatis-Plus 1、mybatis-plus介绍 官网&#xff1a;https://baomidou.com/ MyBatis-Plus &#xff08;简称 MP&#xff09;是一个MyBatis的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。MyBatis-Plus提供了通用的mapper…...

板卡设计+硬件每日学习十个知识点(44)23.8.24 (检测单元设计,接口部分设计,板卡电源输入设计,电源检测电路)

文章目录 1.检测单元介绍&#xff08;使用GD32单片机&#xff09;2.GD32的最小系统板3.GD32的温度监测4.GD32的电压监测和电流监测5.GD32的布线6.接口部分设计7.板卡电源输入设计8.电源检测电路 1.检测单元介绍&#xff08;使用GD32单片机&#xff09; 答&#xff1a; 首先要为…...

jmeter HTTP信息头管理器

首先&#xff0c;打开JMeter并创建一个新的测试计划。右键单击测试计划&#xff0c;选择"添加" > “线程组”&#xff0c;然后在线程组上右键单击&#xff0c;选择"添加" > “Sampler” > “HTTP请求”。 在HTTP请求中填写服务器的URL和其他必要…...

各种中间件的默认端口

面试时会忘记个别中间件端口 docker&#xff1a;2375 nacos&#xff1a;8848 redis&#xff1a;6379 rabbitMq: 5672(后台配置的端口)15672&#xff08;web管理界面&#xff09;账号&#xff1a;guest15674&#xff08;web STOMP插件&#xff09;&#xff1a;通过WebSocket…...

leetcode303. 区域和检索 - 数组不可变(java)

前缀和数组的应用 区域和检索 - 数组不可变题目描述前缀和数组代码演示 区域和检索 - 数组不可变 难度 - 简单 原题链接 - 区域和检索 - 数组不可变 题目描述 给定一个整数数组 nums&#xff0c;处理以下类型的多个查询: 计算索引 left 和 right &#xff08;包含 left 和 righ…...

PHP 安装Composer,vue前端依赖包

电脑安装Composer 官网下载&#xff1a;https://getcomposer.org/Composer-Setup.exe 后端安装&#xff1a; 检查是否安装依赖&#xff1a; 安装Composer install 或 Composer i 前端安装&#xff1a; yarn install 安装依赖...

OpenCV项目开发实战--基于Python/C++实现鼠标注释图像和轨迹栏来控制图像大小

鼠标指针是图形用户界面 (GUI) 中的关键组件。没有它,您就无法真正考虑与 GUI 进行交互。那么,让我们深入了解 OpenCV 中鼠标和轨迹栏的内置函数。我们将演示如何使用鼠标来注释图像,以及如何使用轨迹栏来控制图像的大小 我们将使用下图来演示 OpenCV 中鼠标指针和轨迹栏功能…...

❤ Vue使用Eslint检测报错问题和解决

❤ Vue使用Eslint检测报错问题和解决 1、 关闭Eslint检测 关闭ESLint语法检测即可&#xff1a;具体步骤如下&#xff1a; 第一步我们打开setting设置&#xff1a; 打开左上角的file&#xff0c;然后点击setting 接下来进入setting以后我们来看下面操作&#xff1a; eol-l…...

解决运行在微信小程序中报[ app.json 文件内容错误] app.json: app.json 未找到(env: Windows,mp,1.05.2204

找到project.config.json文件夹 添加 "miniprogramRoot": "unpackage/dist/dev/mp-weixin/", 即可...

python 基础 -- 安装Python模块

作为一个流行的开源开发项目&#xff0c;Python拥有一个由贡献者和用户组成的活跃支持社区&#xff0c;他们还根据开源许可条款向其他Python开发人员提供他们的软件。 这允许Python用户有效地共享和协作&#xff0c;从其他人已经创建的常见(有时甚至是罕见的!)问题的解决方案中…...

C语言实现状态机

关于状态机&#xff0c;基础的知识点可以自行理解&#xff0c;讲解的很多&#xff0c;这里主要是想写一个有限状态机FSM通用的写法&#xff0c;目的在于更好理解&#xff0c;移植&#xff0c;节省代码阅读与调试时间&#xff0c;体现出编程之美。 传统的实现方案 if...else : …...

交叉编译工具链arm-linux-gnueabihf的安装-ubuntu 20.04

前面下载安装步骤参考该博主的文章 http://t.csdn.cn/ZbjFX 另&#xff1a;本人对所遇到的环境变量的配置问题作补充 1.修改环境变量 建议直接在.bashrc文件作修改 &#xff0c;修改方式相同 ( vi :视自己的编辑器而定) sudo vi ~/.bashrc 2.修改环境变量后&#xff0c;可…...

Java的类加载器

类加载 1、ClassLoader 用来加载 Class 文件 2、 系统内置的ClassLoader 通过双亲委托加载指定目录下的class和资源 3、 可以自定义ClassLoader 一般覆盖findClass() 4、ContextClassLoader 与线程相关&#xff0c;可以获取和设置&#xff0c;可以绕过双亲委托的机制。 三个类…...

Stable Diffusion web UI 部署详细教程

前言 本文使用 AutoDL 平台进行 Stable Diffusion web UI 云端部署 AutoDL 官网&#xff1a;AutoDL算力云 | 弹性、好用、省钱。租GPU就上AutoDL Stable Diffusion web UI 官网&#xff1a;AUTOMATIC1111/stable-diffusion-webui: Stable Diffusion web UI (github.com) 步…...

《深度学习计算机视觉 》书籍分享(包邮送书三本)

深度学习计算机视觉介绍 随着计算机技术的发展和进步&#xff0c;计算机视觉领域得到了广泛的关注和研究。而深度学习作为一种强大的机器学习方法&#xff0c;已经成为计算机视觉领域的重要工具之一。本文将介绍深度学习在计算机视觉中的应用和取得的成果。 深度学习是一种模…...

【使用 k 折叠交叉验证的卷积神经网络(CNN)】基于卷积神经网络的无特征EMG模式识别研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

微服务 Nacos配置热部署

在nacos中添加配置文件 在配置列表中添加配置&#xff0c; 注意&#xff1a;项目的核心配置&#xff0c;需要热更新的配置才有放到nacos管理的必要。基本不会变更的一些配置还是保存在微服务本地比较好。 从微服务拉取配置 微服务要拉取nacos中管理的配置&#xff0c;并且与…...

国产调度器之光——Fsched到底有多能打?

这是一篇推荐我们速石自研调度器——Fsched的文章。 看起来在专门写调度器&#xff0c;但又不完全在写。 往下看&#xff0c;你就懂了。 本篇一共五个章节&#xff1a; 一、介绍一下主角——速石自研调度器Fsched 二、只要有个调度器&#xff0c;就够了吗&#xff1f; 三…...

LeetCode:53. 最大子数组和 - Python

53. 最大子数组和 问题描述&#xff1a; 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。 示例 1&#xff1a; 输入&#xff1a;nums [-…...

网站建设 之 react usestate

react着重在于“不可变动” 如果变动了怎么办呢&#xff1f;那就整个新的 局部变量/函数/jsx-》state/props-〉ref&#xff0c;依次越来越难变 每次state/props&#xff0c;局部变量/函数/jsx都是新的 既然函数是新的&#xff0c;那么就会有一个问题&#xff0c;回调函数用…...

第一讲使用IDEA创建Java工程——HelloWorld

一、前言导读 为了能够让初学者更快上手Java,不会像其他书籍或者视频一样,介绍一大堆历史背景,默认大家已经知道Java这么编程语言了。本专栏只会讲解干货,直接从HelloWord入手,慢慢由浅入深,讲个各个知识点,这些知识点也是目前工作中项目使用的,而不是讲一些老的知识点…...

BootstrapBlazor组件使用:数据注解

文章目录 前言BB数据注解数据注解源码数据注解简介注解简单实例[BB 编辑弹窗](https://www.blazor.zone/edit-dialog)[ValidateForm 表单组件](https://www.blazor.zone/validate-form)使用简介 前言 BootstrapBlazor(一下简称BB)是个特别好用的组件&#xff0c;基本上满足了大…...

MySQL 触发器

文章目录 1.简介2.行级与语句级触发器3.触发时机4.触发器优缺点5.创建触发器语法示例 6.查看触发器7.删除触发器参考文献 1.简介 触发器&#xff08;Trigger&#xff09;是与表关联的命名数据库对象&#xff0c;当表发生特定事件时激活。 触发器的一些用途是对要插入表中的值执…...

DPDK主从进程模式 rte_mempool_put失败

版本&#xff1a;19.11.6 情景&#xff1a;主进程应用rte_mempool_create创建mempool&#xff0c;rte_mempool_get获取数据&#xff1b;从进程应用rte_mempool_put归还数据 问题&#xff1a;从进程rte_mempool_put无法归还数据 原因&#xff1a;DPDK通过rte_mempool_ops_tab…...

ZooKeeper 的工作原理

ZooKeeper 的工作原理可以概括为以下几个方面: 1. 数据模型 ZooKeeper 使用树形目录节点(znode)来建模关键的数据,每个 znode 可以存储数据内容,也可以作为目录包括子节点。客户端可以在节点上设置监听器。 2. 一致性算法 ZooKeeper 使用 ZAB(ZooKeeper Atomic Broadcast)协议…...

【业务功能篇73】分布式ID解决方案

业界实现方案 1. 基于UUID2. 基于DB数据库多种模式(自增主键、segment)3. 基于Redis4. 基于ZK、ETCD5. 基于SnowFlake6. 美团Leaf(DB-Segment、zkSnowFlake)7. 百度uid-generator() 1.基于UUID生成唯一ID UUID:UUID长度128bit&#xff0c;32个16进制字符&#xff0c;占用存储空…...

Qt安卓开发经验技巧总结V202308

01&#xff1a;01-05 pro中引入安卓拓展模块 QT androidextras 。pro中指定安卓打包目录 ANDROID_PACKAGE_SOURCE_DIR $$PWD/android 指定引入安卓特定目录比如程序图标、变量、颜色、java代码文件、jar库文件等。 AndroidManifest.xml 每个程序唯一的一个全局配置文件&…...

【vue2】前端实现下载后端返回的application/octet-stream文件流

1、下载csv/txt时 此时无须修改接口的响应格式 let filenameRegex /filename[^;\n]*((["]).*?\2|[^;\n]*)/; let matches filenameRegex.exec(data.headers[content-disposition]); let blob new Blob([\uFEFF data.data], {//目前只有csv格式type: text/csv;charse…...

【Java】SM2Utils(国密 SM2 工具类)

基于 bouncycastle 实现 国密 SM2 <!-- 引入 bouncycastle --> <dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.70</version> </dependency>import lombok.Sneak…...

『C语言入门』初识C语言

文章目录 前言C语言简介一、Hello World&#xff01;1.1 编写代码1.2 代码解释1.3 编译和运行1.4 结果 二、数据类型2.1 基本数据类型2.2 复合数据类型2.3 指针类型2.4 枚举类型 三、C语言基础3.1 变量和常量3.2 运算符3.3 控制流语句3.4 注释单行注释多行注释注释的作用 四、 …...