MyBatisPlus快速入门

MyBatisPlus概述和快速入门

概述

基础框架

  • MyBatis
  • Spring
  • SpringMVC

为什么需要学习?

MyBatisPlus可以节省我们大量的工作时间,所有的CRUD都可以自动化完成。还有其他框架JPAtk-mapper

简介

是什么?

Mybatis本来就是简化JDBC操作的!MyBatis-Plus是一个Mybatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

愿景

成为 MyBatis 最好的搭档,就像 魂斗罗中的 1P2P,基友搭配,效率翻倍。

特性

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

快速入门

使用第三方库,学习流程

  1. 导入对应的依赖
  2. 研究依赖如何配置
  3. 代码如何编写
  4. 提高扩展技术能力

步骤

  1. 创建数据库test
  2. 创建user
sql复制代码DROP TABLE IF EXISTS user;

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)
);


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');


-- 真实开发中,version(乐观锁)、deleted(逻辑删除)、gmt_create、gmt_modified 这些字段也是需要的
  1. 编写项目,初始化项目,使用Spirngboot初始化项目。
  2. 导入依赖
xml复制代码<!--数据库驱动-->  
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->  
<dependency>  
 <groupId>mysql</groupId>  
 <artifactId>mysql-connector-java</artifactId>  
</dependency>  
  
<!--Mybatis-plus-->  
<!--Mybatis-plus 是自己开发的,并非官方的!-->  
<dependency>  
 <groupId>com.baomidou</groupId>  
 <artifactId>mybatis-plus-boot-starter</artifactId>  
 <version>3.4.3.4</version>  
</dependency>  
  
<!--lombok-->  
<dependency>  
 <groupId>org.projectlombok</groupId>  
 <artifactId>lombok</artifactId>  
</dependency>

说明:我们使用Mybatis-plus可以节省我们大量的代码,尽量不要同时导入MyBatisMybatis-plus,有版本的差异。 5. 连接数据库,这一步和Mybatis相同!

properties复制代码# 根据自己的数据库配置

# mysql 5  
spring.datasource.username=root  
spring.datasource.password=123456  
# useSSL 安全连接;useUnicode 是否使用Unicode字符集;characterEncoding 字符集;  
# mysql 8 驱动不同,需要增加时区 serverTimezone=GMT%2B8spring.datasource.url=jdbc:mysql://localhost/test?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8  
# 使用 8 的驱动,高版本兼容低版本  
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  1. 传统方式:pojo-dao(连接mybatis,配置mapper.xml文件)-service-controller ; 使用mybatis-plus之后:
  • pojo
java复制代码@Data  
@NoArgsConstructor  
@AllArgsConstructor  
public class User {  
 private Long id;  
    private String name;  
    private Integer age;  
    private String email;  
}
  • mapper接口
java复制代码/**  
 * 在对应的 mapper 上面实现继承基本的类 BaseMapper  
 */
@Repository  
public interface UserMapper extends BaseMapper<User> {  
  
 /*  
 * 所有的CRUD操作都已经编写完成了  
 * 不需要配置一大推文件了  
 */
 }

注意点:需要在主启动类上去扫描我们的mapper包下的所有接口。@MapperScan("com.zbc.mybatis_plus_demo.mapper")

  • 测试类中测试
java复制代码@SpringBootTest  
class MybatisPlusDemoApplicationTests {  
  
 /**  
 * 继承了 BaseMapper,所有的方法都来自己的父类  
 * 我们也可以编写自己的扩展方法  
 */  
 @Autowired  
 private UserMapper userMapper;  
  
 @Test  
 void contextLoads() {  
  
 //参数是一个 wrapper,条件构造器  
 //查询所有用户  
 List<User> users = userMapper.selectList(null);  
        users.forEach(System.out::println);  
    }  
}

Pasted image 20211105233655.png

思考问题

  • SQL谁帮我们写的? Mybatis-Plus
  • 方法从哪里来的?Mybatis-Plus

配置日志

我们所有的sql现在是不可见的,我们需要知道是怎么执行的,还有在开发过程中调试以及查找定位问题的时候需要查看。在开发和测试环境可以配置,生产环境不需要配置(因为产生大量不必要的日志,并且对性能有一点影响)。 在application.properties中假如以下配置

properties复制代码# 配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

重新执行刚才的测试代码

log复制代码Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5d717f19] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1180105925 wrapping com.mysql.cj.jdbc.ConnectionImpl@2c16677c] will not be managed by Spring
==>  Preparing: SELECT id,name,age,email FROM user 
==> Parameters: 
<==    Columns: id, name, age, email
<==        Row: 1, Jone, 18, test1@baomidou.com
<==        Row: 2, Jack, 20, test2@baomidou.com
<==        Row: 3, Tom, 28, test3@baomidou.com
<==        Row: 4, Sandy, 21, test4@baomidou.com
<==        Row: 5, Billie, 24, test5@baomidou.com
<==      Total: 5
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5d717f19]
User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com)

Insert插入

java复制代码//测试插入  
@Test  
public void testInsert() {  
    User user = new User();  
    user.setName("zbc");  
    user.setAge(20);  
    user.setEmail("test@163.com");  
  
    //帮我们自动生成id  
    int result = userMapper.insert(user);  
    //受影响的行数  
    System.out.println(result);  
    //发现,id会自动回填  
    System.out.println(user);  
}
log复制代码Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@18715bb] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1180105925 wrapping com.mysql.cj.jdbc.ConnectionImpl@2c16677c] will not be managed by Spring
==>  Preparing: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? ) 
==> Parameters: 1456795944390905857(Long), zbc(String), 20(Integer), test@163.com(String)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@18715bb]
1
User(id=1456795944390905857, name=zbc, age=20, email=test@163.com)

数据库插入的id是全局唯一id1456795944390905857是基于雪花算法生成。

主键生成策略

MybatisPlus中关于主键生成策略有一个专门的注解(@TableId)

java复制代码@Documented  
@Retention(RetentionPolicy.RUNTIME)  
@Target({ElementType.FIELD})  
public @interface TableId {  
    String value() default "";  
  
    IdType type() default IdType.NONE;  
}

IdType是策略的类型

java复制代码public enum IdType {  
    AUTO(0),  
    NONE(1),  
    INPUT(2),  
    ASSIGN_ID(3),  
    ASSIGN_UUID(4);  
  
    private final int key;    
}
  • AUTO
    

    :数据库

    ID
    

    自增

    • 需要在实体类字段上添加@TableId(type = IdType.AUTO)
    • 数据库字段也必须是自增。
    • 再次测试,我们发现id自增1
  • NONE: 默认方案,未设置主键

  • INPUT:插入前手动设置主键,一旦设置之后就需要配置id

  • ASSIGN_ID:分配ID(主键类型为Number(LongInteger)或String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)

  • ASSIGN_UUID:分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认default方法)

  • ID_WORKER:分布式全局唯一ID,主键类型Long,后续建议使用ASSIGN_ID

  • UUID32UUID字符串,主键类型String,后续建议使用ASSIGN_UUID

  • ID_WORKER_STR:分布式全局唯一ID,主键类型String,后续建议使用ASSIGN_ID

雪花算法:snowflakeTwitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096ID),最后还有一个符号位,永远是0。可以保证几乎全球唯一。

UUID:国际标准化组织ISO提出的一个概念,是一个128bit的数字,也可以表现为3216进制的字符,中间用-分割。

  • 时间戳+UUID版本号,分三段占16个字符(60bit+4bit),
  • Clock Sequence号与保留字段,占4个字符(13bit3bit),
  • 节点标识占12个字符(48bit),
  • 示例:3F2504E0-4F89-11D3-9A0C-0305E82C3301

update更新

java复制代码//测试更新  
@Test  
public void testUpdate() {  
    User user = new User();  
    //通过条件自动拼接动态sql  
    user.setId(1L);  
    user.setName("zbc");  
    user.setAge(14);  
    //注意:updateById 实际传参是一个 对象,并不是id  
    int result = userMapper.updateById(user);  
    System.out.println(result);  
    System.out.println(user);  
}

所有的sql都是自动帮你配置的

自动填充

创建时间、修改时间,这些字段都是自动化完成的,我们不希望手动更新。(阿里巴巴开发手册:所有的数据库表:gmt_creategmt_modified这两个字段几乎所有的表都需要配置上,而且需要自动化)

数据库级别(不建议使用)

  • 在表中新增字段create_timeupdate_time
sql复制代码-- 添加 create_time 设置默认时间 CURRENT_TIMESTAMP
ALTER TABLE `table_name`  
ADD COLUMN `create_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间';

-- 修改 create_time 设置默认时间 CURRENT_TIMESTAMP
ALTER TABLE `table_name`  
MODIFY COLUMN `create_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间';

-- 添加 update_time 设置默认时间 CURRENT_TIMESTAMP 设置更新时间为 ON UPDATE CURRENT_TIMESTAMP
ALTER TABLE `table_name`  
ADD COLUMN `update_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间';

-- 修改 update_time 设置默认时间 CURRENT_TIMESTAMP 设置更新时间为 ON UPDATE CURRENT_TIMESTAMP
ALTER TABLE `table_name`  
MODIFY COLUMN `update_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间';
  • 测试插入方法,需要同步实体类字段
java复制代码private Date createTime;  
private Date updateTime;
idnameageemailcreate_timeupdate_time
1456795944390905861zbc20test@163.com2021-11-07 17:29:332021-11-07 17:29:33
  • 测试更新方法
idnameageemailcreate_timeupdate_time
1456795944390905861zbc14test@163.com2021-11-07 17:29:332021-11-07 17:34:39

代码级别

  • 删除数据库级别
sql复制代码-- 去掉 create_time 默认值
ALTER TABLE `table_name`  
MODIFY COLUMN `create_time` TIMESTAMP NULL COMMENT '创建时间';

-- 去掉 update_time 默认值和默认更新
ALTER TABLE `table_name`  
MODIFY COLUMN `update_time` TIMESTAMP NULL COMMENT '修改时间';
  • 实体类字段数据增加注解
java复制代码@TableField(fill = FieldFill.INSERT)  
private Date createTime;  
@TableField(fill = FieldFill.INSERT_UPDATE)  
private Date updateTime;
java复制代码//填充类型说明:
public enum FieldFill {  
	 /**  
	 * 默认不处理  
	 */  
	 DEFAULT,  
	/**  
	 * 插入时填充字段  
	 */  
	 INSERT,  
	/**  
	 * 更新时填充字段  
	 */  
	 UPDATE,  
	/**  
	 * 插入和更新时填充字段  
	 */  
	 INSERT_UPDATE  
}
  • 编写处理器来处理这个注解
java复制代码package com.zbc.mybatis_plus_demo.handler;  
  
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;  
import lombok.extern.slf4j.Slf4j;  
import org.apache.ibatis.reflection.MetaObject;  
import org.springframework.stereotype.Component;  
  
import java.util.Date;  
  
/**  
 * 不要忘记把处理器加到IOC容器中  
 *  
 * @author zbc * @date 2021/11/7 
 */  
@Slf4j  
@Component  
public class MyMetaObjectHandler implements MetaObjectHandler {  
  
 /**  
 * 插入时的填充策略  
 *  
 * @param metaObject 元数据  
 */  
 @Override  
 public void insertFill(MetaObject metaObject) {  
 	//setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject)  
 	this.setFieldValByName("createTime", new Date(), metaObject);  
	this.setFieldValByName("updateTime", new Date(), metaObject);  
 }  
  
 /**  
 * 更新时的填充策略  
 *  
 * @param metaObject 元数据  
 */  
 @Override  
 public void updateFill(MetaObject metaObject) {  
 	this.setFieldValByName("updateTime", new Date(), metaObject);  
 }  
}

乐观锁

  • 悲观锁:认为数据是不安全的,随时可能都有变动,无论什么时候都会加锁。再操作。
  • 乐观锁:相对悲观锁而言,乐观锁任务数据是安全的,一般情况下不会出现问题,无论干什么都不去上锁。如果出现冲突,则返回错误信息,交给用户决定。

乐观锁实现方式

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

使用乐观锁

  • 数据库增加version字段:
sql复制代码ALTER TABLE `table_name`  
ADD COLUMN `version` INT DEFAULT 1 COMMENT '乐观锁';
  • 给实体类增加字段
java复制代码@Version  
private Integer version;
  • 注册乐观锁组件
java复制代码@Configuration  
@EnableTransactionManagement  
public class MyBatisPlusConfig {  
  
 /**  
 * 用来配置mybatisPlus 插件 
 *  
 * @return 拦截器  
 */  
 @Bean  
 public MybatisPlusInterceptor mybatisPlusInterceptor() {  
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();  
        //添加乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());  
        return interceptor;  
    }  
}
  • 测试成功
java复制代码//测试乐观锁成功  
@Test  
public void testOptimisticLocker() {  
	 //1.先获取用户信息  
	 User user = userMapper.selectById(1L);  
	 //2.修改用户信息  
	 user.setName("test");  
	 user.setAge(25);  
	 //3.执行更新操作  
	 userMapper.updateById(user);  
}
log复制代码Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@74431b9c] was not registered for synchronization because synchronization is not active
2021-11-07 18:41:52.417  INFO 21884 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-11-07 18:41:52.792  INFO 21884 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
JDBC Connection [HikariProxyConnection@1729992636 wrapping com.mysql.cj.jdbc.ConnectionImpl@774f2992] will not be managed by Spring
==>  Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id=?
==> Parameters: 1(Long)
<==    Columns: id, name, age, email, create_time, update_time, version
<==        Row: 1, zbc, 14, test1@baomidou.com, null, 2021-11-06 22:28:53, 1
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@74431b9c]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@74ce7fdf] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@33779587 wrapping com.mysql.cj.jdbc.ConnectionImpl@774f2992] will not be managed by Spring
==>  Preparing: UPDATE user SET name=?, age=?, email=?, update_time=?, version=? WHERE id=? AND version=?
==> Parameters: test(String), 25(Integer), test1@baomidou.com(String), 2021-11-07 18:41:52.93(Timestamp), 2(Integer), 1(Long), 1(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@74ce7fdf]
  • 测试失败
java复制代码//测试乐观锁失败  
@Test  
public void testOptimisticLocker2() {  
  
	 //线程1  
	 User user = userMapper.selectById(1L);  
	 user.setName("test1");  

	 //模拟线程2,先一步执行修改操作  
	 User user2 = userMapper.selectById(1L);  
	 user2.setName("test2");  
	 userMapper.updateById(user2);  

	 //如果没有乐观锁,就会覆盖线程2的值   
	 userMapper.updateById(user);  
}
log复制代码Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@f2fb225] was not registered for synchronization because synchronization is not active
2021-11-07 18:46:35.982  INFO 13340 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-11-07 18:46:36.457  INFO 13340 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
JDBC Connection [HikariProxyConnection@2001676690 wrapping com.mysql.cj.jdbc.ConnectionImpl@602298b] will not be managed by Spring
==>  Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id=?
==> Parameters: 1(Long)
<==    Columns: id, name, age, email, create_time, update_time, version
<==        Row: 1, test, 25, test1@baomidou.com, null, 2021-11-07 18:41:53, 2
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@f2fb225]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@13908f9c] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@849031967 wrapping com.mysql.cj.jdbc.ConnectionImpl@602298b] will not be managed by Spring
==>  Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id=?
==> Parameters: 1(Long)
<==    Columns: id, name, age, email, create_time, update_time, version
<==        Row: 1, test, 25, test1@baomidou.com, null, 2021-11-07 18:41:53, 2
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@13908f9c]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@642c72cf] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@966457052 wrapping com.mysql.cj.jdbc.ConnectionImpl@602298b] will not be managed by Spring
==>  Preparing: UPDATE user SET name=?, age=?, email=?, update_time=?, version=? WHERE id=? AND version=?
==> Parameters: test2(String), 25(Integer), test1@baomidou.com(String), 2021-11-07 18:46:36.63(Timestamp), 3(Integer), 1(Long), 2(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@642c72cf]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6aa6c17] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1196877260 wrapping com.mysql.cj.jdbc.ConnectionImpl@602298b] will not be managed by Spring
==>  Preparing: UPDATE user SET name=?, age=?, email=?, update_time=?, version=? WHERE id=? AND version=?
==> Parameters: test1(String), 25(Integer), test1@baomidou.com(String), 2021-11-07 18:46:36.68(Timestamp), 3(Integer), 1(Long), 2(Integer)
<==    Updates: 0
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6aa6c17]

Select查询

java复制代码//测试查询
@Test
public void testSelect() {
    //根据id查询
    User user = userMapper.selectById(1L);
    System.out.println(user);

    //批量查询
    List<User> users = userMapper.selectBatchIds(Arrays.asList(2L, 3L));
    users.forEach(System.out::println);

    //条件查询 map
    Map<String, Object> map = new HashMap<>();
    map.put("name", "zbc");
    List<User> users1 = userMapper.selectByMap(map);
    users1.forEach(System.out::println);
}
log复制代码//根据id查询
==>  Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id=?
==> Parameters: 1(Long)

//批量查询
==>  Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id IN ( ? , ? )
==> Parameters: 2(Long), 3(Long)

//条件查询 map
==>  Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE name = ?
==> Parameters: zbc(String)

分页查询

实现方式

  • 原始分页limit
  • 第三方插件pageHelper
  • MyBatisPlus自带分页功能

使用分页查询

  • 增加分页插件,分页插件需要在乐观锁插件前面添加
java复制代码@Configuration
@EnableTransactionManagement
public class MyBatisPlusConfig {

    /**
     * 用来配置mybatisPlus 插件
     *
     * @return 拦截器
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //分页插件,注意数据库的类型
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        //乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}
  • 直接使用Page对象即可
java复制代码//测试分页查询  
@Test  
public void testPage() {  
    //参数1:页码,参数2:每页记录数  
    Page<User> page = new Page<>(1, 5);  
    userMapper.selectPage(page, null);  
    page.getRecords().forEach(System.out::println);  
}
log复制代码//先求总记录数
==>  Preparing: SELECT COUNT(*) AS total FROM user
==> Parameters: 
<==    Columns: total
<==        Row: 12
<==      Total: 1
//然后分页查询
==>  Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user LIMIT ?
==> Parameters: 5(Long)
//输出结果
User(id=1, name=test2, age=25, email=test1@baomidou.com, createTime=null, updateTime=Sun Nov 07 18:46:37 CST 2021, version=3)
User(id=2, name=Jack, age=20, email=test2@baomidou.com, createTime=null, updateTime=Sat Nov 06 22:28:53 CST 2021, version=1)
User(id=3, name=Tom, age=28, email=test3@baomidou.com, createTime=null, updateTime=Sat Nov 06 22:28:53 CST 2021, version=1)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com, createTime=null, updateTime=Sat Nov 06 22:28:53 CST 2021, version=1)
User(id=5, name=Billie, age=24, email=test5@baomidou.com, createTime=null, updateTime=Sat Nov 06 22:28:53 CST 2021, version=1)
12

delete删除

java复制代码//测试删除
@Test
public void testDelete() {
    //单个id删除
    userMapper.deleteById(1456795944390905863L);

    //批量id删除
    userMapper.deleteBatchIds(Arrays.asList(1456795944390905861L, 1456795944390905862L));

    //条件删除 map
    Map<String, Object> map = new HashMap<>();
    map.put("name", "123");
    userMapper.deleteByMap(map);
}
log复制代码//单个id删除
==>  Preparing: DELETE FROM user WHERE id=?
==> Parameters: 1456795944390905863(Long)
<==    Updates: 1
//批量id删除
==>  Preparing: DELETE FROM user WHERE id IN ( ? , ? )
==> Parameters: 1456795944390905861(Long), 1456795944390905862(Long)
<==    Updates: 2
//条件删除 map、
==>  Preparing: DELETE FROM user WHERE name = ?
==> Parameters: 123(String)
<==    Updates: 1

逻辑删除

  • 物理删除:从数据库中直接删除
  • 逻辑删除:从数据库中没有删除,而是通过一个变量来让它失效

应用场景:管理员可以查看被删除的记录,防止数据丢失,类似回收站。

使用

  • 增加deleted字段
sql复制代码ALTER TABLE `table_name`  
ADD COLUMN `deleted` INT(1) DEFAULT 1 COMMENT '逻辑删除';
  • 实体类增加属性
java复制代码@TableLogic  
private Integer deleted;
  • 增加逻辑删除配置
properties复制代码# 逻辑删除
# 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置@TableLogic)
mybatis-plus.global-config.db-config.logic-delete-field=flag
# 逻辑已删除值(默认为 1)
mybatis-plus.global-config.db-config.logic-delete-value=1
# 逻辑未删除值(默认为 0)
mybatis-plus.global-config.db-config.logic-not-delete-value=0
  • 测试
java复制代码//测试逻辑删除
@Test
public void testLogicDelete(){
    userMapper.deleteById(1456795944390905864L);
    User user = userMapper.selectById(1456795944390905864L);
    System.out.println(user);
}
log复制代码==>  Preparing: UPDATE user SET deleted=1 WHERE id=? AND deleted=0
==> Parameters: 1456795944390905864(Long)
<==    Updates: 1


==>  Preparing: SELECT id,name,age,email,create_time,update_time,version,deleted FROM user WHERE id=? AND deleted=0
==> Parameters: 1456795944390905864(Long)
<==      Total: 0

null

本质上是更新操作并不是删除,而是修改操作。 查询的时候我们发现已经不能查找到了

条件构造器(重点)

条件构造器主要用来写一些复杂的sql

注意事项

不支持以及不赞成在 RPC 调用中把 Wrapper 进行传输

  1. wrapper 很重
  2. 传输 wrapper 可以类比为你的 controllermap 接收值(开发一时爽,维护火葬场)
  3. 正确的 RPC 调用姿势是写一个 DTO 进行传输,被调用方再根据 DTO 执行相应的操作

拼接方法

查询方法说明示例
eq等于 =eq("username", "admin") —> username = 'admin'
ne等于 =ne("username", "admin") —> username <> 'admin'
gt大于 >gt("age", "18") —> age > 18
ge大于等于 >=ge("age", "18") —> age >= 18
lt小于 <lt("age", 18) —> age < 18
le小于等于 <=le("age", 18) —> age <= 18
between在值1和值2之间between("age", 18, 24) —> age BETWEEN 18 AND 24
notBetween不在值1和值2之间notBetween("age", 18, 24) —> age NOT BETWEEN 18 AND 24
like模糊查询like("name", "张") —> name LIKE '%张%'
notLike不模糊查询内notLike("name", "张") —> name NOT LIKE '%张%'
likeLeft左模糊查询likeLeft("name", "三") —> name LIKE '%三'
likeRight右模糊查询内likeRight("name", "张") —> name LIKE '张%'
isNull字段为空isNull("name") —> name IS NULL
isNotNull字段不为空isNotNull("name") —> name IS NOT NULL
in在集合内in("age", 16,17,18) —> age IN (16,17,18)
notIn在集合内notIn("age", 16,17,18) —> age NOT IN (16,17,18)
inSql子查询in("id", "SELECT id FROM table WHERE id < 3") —> id IN (SELECT id FROM table WHERE id < 3)
notInSql子查询notInSql("id", "SELECT id FROM table WHERE id < 3") —> id ONT IN (SELECT id FROM table WHERE id < 3)
groupBy分组groupBy("age", "id") —> GROUP BY id, name
orderByAsc字段正序orderByAsc("age") —> ORDER BY age ASC
orderByDesc字段倒序orderByDesc("age") —> ORDER BY age DESC
having分组筛选having("sum(age) > 10")—>HAVING SUM(age) > 10
oreq("id", 1).or().eq("name", "张三")—>id = 1 OR name = '张三'
andeq("id", 1).and().eq("name", "张三")—>id = 1 AND name = '张三'
exists存在exists("SELECT id FROM table WHERE age = 1")—>EXISTS (SELECT id FROM table WHERE age = 1)
notExists存在notExists("SELECT id FROM table WHERE age = 1")—>NOT EXISTS (SELECT id FROM table WHERE age = 1)

isNotnullge演示

java复制代码@Test  
void test1() {  
 //查询name不为空并且邮箱不为空,年龄大于15  
 QueryWrapper<User> wrapper = new QueryWrapper<>();  
    wrapper.  
            isNotNull("name").  
            isNotNull("email").  
            ge("age", 15);  
    userMapper.selectList(wrapper).forEach(System.out::println);  
}

eqselectOne演示

java复制代码@Test  
void test2() {  
 //查询name=test2  
 QueryWrapper<User> wrapper = new QueryWrapper<>();  
    wrapper.eq("name", "test2");  
    //selectOne 只能查询一个,假如出现多个结果会报错  
 User user = userMapper.selectOne(wrapper);  
    System.out.println(user);  
}

betweenselectCount演示

java复制代码@Test  
void test3() {  
 //查询年龄在[20,30]之间的用户  
 QueryWrapper<User> wrapper = new QueryWrapper<>();  
    wrapper.between("age", 20, 30);  
    long count = userMapper.selectCount(wrapper);  
    System.out.println(count);  
}

notLikelikeRightselectMaps演示

java复制代码@Test  
void test4() {  
 //name不包含bc,并且email以t开头(t%)  
 QueryWrapper<User> wrapper = new QueryWrapper<>();  
    wrapper.  
            notLike("name", "bc").  
            likeRight("email", "t");  
    List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);  
    maps.forEach(System.out::println);  
}

inSqlselectObjs和子查询演示

java复制代码@Test  
void test5() {  
 QueryWrapper<User> wrapper = new QueryWrapper<>();  
    wrapper.inSql("id", "SELECT id FROM user WHERE id < 10");  
    List<Object> objects = userMapper.selectObjs(wrapper);  
    objects.forEach(System.out::println);  
}

orderByDesc演示

java复制代码@Test  
void test6() {  
 QueryWrapper<User> wrapper = new QueryWrapper<>();  
    wrapper.orderByDesc("id");  
    List<User> users = userMapper.selectList(wrapper);  
    users.forEach(System.out::println);  
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

redis原理 3:未雨绸缪 —— 持久化

redis原理 3&#xff1a;未雨绸缪 —— 持久化 Redis 的数据全部在内存里&#xff0c;如果突然宕机&#xff0c;数据就会全部丢失&#xff0c;因此必须有一种机制来保证 Redis 的数据不会因为故障而丢失&#xff0c;这种机制就是 Redis 的持久化机制。 Redis 的持久化机制有两种…

(三)Node.js - 模块化

1. Node.js中的模块化 Node.js中根据模块来源不同&#xff0c;将模块分为了3大类&#xff0c;分别是&#xff1a; 内置模块&#xff1a;内置模块由Node.js官方提供的&#xff0c;例如fs、path、http等自定义模块&#xff1a;用户创建的每个.js文件&#xff0c;都是自定义模块…

企升编辑器word编写插件

面向用户群体招投标人员&#xff0c;用统一的模板来编写标书&#xff0c;并最终合并标书。项目经理&#xff0c;编写项目开发计划书&#xff0c;项目验收文档等。开发人员&#xff0c;编写项目需求规格说明书、设计说明书、技术总结等文档。其他文档编写工作量较多的岗位人员。…

flowable-ui部署(6.80)

前置条件&#xff1a;Apache Tomcat/9.0.78版本及以下 https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.78/bin/apache-tomcat-9.0.78-windows-x64.zip 一、下载资源 https://github.com/flowable/flowable-engine/releases/download/flowable-6.8.0/flowable-6.8.0.zip 二…

Apoll 多项式规划求解

一、纵向规划 void QuarticPolynomialCurve1d::ComputeCoefficients(const float x0, const float dx0, const float ddx0, const float dx1,const float ddx1, const float p) {if (p < 0.0) {std::cout << "p should be greater than 0 at line 140." &…

伦敦金费用有哪几方面?

通常在网上开设伦敦金投资账户是没有成本的&#xff0c;而它交易的费用&#xff0c;主要是由点差和过夜利息&#xff08;仓息&#xff09;构成。如果伦敦金投资者只是做短线的日内交易&#xff0c;做一手完整的100盎司的标准合约&#xff0c;需要支付大约50美元点差费用&#x…

分支和循环语句(1)(C语言)

目录 什么是语句&#xff1f; 分支语句&#xff08;选择结构&#xff09; if语句 悬空else if书写形式的对比 switch语句 在switch语句中的 break default子句 循环语句 while循环 while语句中的break和continue for循环 语法 break和continue在for循环中 for语句和…

fastadmin、vue、react图标库适用于多种框架

在二开fastadmin中&#xff0c;在写vue以及react时&#xff0c;侧边导航栏以及按钮中常常需要很多图标&#xff0c;那么这些图标应该去哪里得到呢&#xff0c;在这里给大家一个链接&#xff0c;这里有丰富的图标库&#xff0c;可以找到自己想要的进行使用。 点击下方链接&…

AI lightning学习

真的是没有mmlab的框架好理解&#xff0c;hook调用没问题&#xff0c;就是代码写的不整洁&#xff0c;hook放的到处都是&#xff0c;而且hook的名字和run的名字也不好对应。 又是捧mmengine的一天 &#x1f603;

Leetcode-每日一题【剑指 Offer 14- I. 剪绳子】

题目 给你一根长度为 n 的绳子&#xff0c;请把绳子剪成整数长度的 m 段&#xff08;m、n都是整数&#xff0c;n>1并且m>1&#xff09;&#xff0c;每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]*k[1]*...*k[m-1] 可能的最大乘积是多少&#xff1f;例如&#xff0…

Electron 开发,报handshake failed; returned -1, SSL error code 1,错误

代码说明 在preload.js代码中&#xff0c;暴露参数给渲染线程renderer.js访问&#xff0c; renderer.js 报&#xff1a;ERROR:ssl_client_socket_impl.cc(978)] failed; returned -1, SSL error code 1,错误 问题原因 如题所说&#xff0c;跨进程传递消息&#xff0c;这意味…

实验笔记之——Android项目的适配

android有一个很烦人的点就是版本之间差距较大&#xff0c;且不兼容&#xff0c;导致不同版本之间代码兼容很容易出问题&#xff0c;一个常见的例子就是几年前自己开发的app&#xff0c;几年后再用竟然配置不了。。。为此&#xff0c;写下本博客记录一下配置旧项目的过程。 …

vue3报错

这是因为eslint对代码的要求严格导致的&#xff0c;可以在package.json里面删掉"eslint:recommended"&#xff0c;然后重启就可以正常运行了

三步免费接入 Claude 2.0,支持多账号轮询!

Claude 2.0 已经发布了一段时间&#xff0c;经过我的非暴力测试&#xff0c;比 ChatGPT 3.5 的能力是要强的&#xff0c;有更强大的上下文 100k&#xff0c;相当于 10 万字的上下文记忆,非常适合处理长文档和大的代码段&#xff0c;虽说有些方面略逊色 ChatGPT 4.0 &#xff0c…

Scala(第一章Scala入门)

文章目录 1.1 概述 1.1.1 为什么学习Scala1.1.2 Scala发展历史1.1.3 Scala和Java关系1.1.4 Scala语言特点 1.2 Scala环境搭建1.3 Scala插件安装1.4 HelloWorld案例 1.4.1 创建IDEA项目工程1.4.2 class和object说明1.4.3 Scala程序反编译 1.5 关联Scala源码1.6官方编程指南 1.1…

【云原生】Docker 详解(一):从虚拟机到容器

Docker 详解&#xff08;一&#xff09;&#xff1a;从虚拟机到容器 1.虚拟化 要解释清楚 Docker&#xff0c;首先要解释清楚 容器&#xff08;Container&#xff09;的概念。要解释容器的话&#xff0c;就需要从操作系统说起。操作系统太底层&#xff0c;细说的话一两本书都说…

C#实现SqlServer数据库同步

实现效果&#xff1a; 设计思路&#xff1a; 1. 开启数据库及表的cdc&#xff0c;定时查询cdc表数据&#xff0c;封装sql语句(通过执行类型&#xff0c;主键;修改类型的cdc数据只取最后更新的记录)&#xff0c;添加到离线数据表&#xff1b; 2. 线程定时查询离线数据表&#xf…

IoT 物联网安全事件的持续检测和监控解决方案

对于IoT物联网安全事件的持续检测和监控&#xff0c;可以采用以下解决方案&#xff1a; 设备管理和漏洞修复&#xff1a;确保设备的固件和软件及时更新&#xff0c;并修补已知的漏洞。建立一个设备清单&#xff0c;并定期审查和更新其中的设备。 流量分析和异常检测&#xff1a…

Java EE 突击 9 - Spring Boot 日志文件

Spring Boot 日志文件 学习目标一 . 日志有什么用1.1 日志格式说明 二 . 自定义日志打印2.1 得到日志对象2.2 使用日志对象提供的方法 , 输出自定义的日志内容2.3 日志的级别 三 . 日志持久化3.1 在配置文件里面设置日志名称3.2 设置日志的保存目录 四 . 日志级别的设置五 . 简…

Dockerfile定制Tomcat镜像

Dockerfile中的打包命令 FROM &#xff1a; 以某个基础镜像作为此镜像的基础 RUN &#xff1a; RUN后面跟着linux常用命令&#xff0c;如RUN echo xxx >> xxx,注意&#xff0c;RUN 不能用于执行命令&#xff0c;因为每个RUN都是独立运行的&#xff0c;RUN 的cd对镜像中的…