MybatisPlus简单到入门

一、MybatisPlus简介

1、入门案例(重点):

1.SpringBoot整合MP
   1).创建新模块选择,Spring项初始化。
   2).选择当前模块使用的技术,只保留MySQL Driver就行,不要选择mybatis避免与后面导入mybatisPlus的依赖发生冲突。
   `使用联网创建工程,有两个弊端,所以在以后工作中基本不使用:
     1、必须联网
     2、只能选最近几个比较新的SpringBoot版本,企业开发追求稳定,一般不会用太新的版本`
         企业工作中,直接创建一个Maven工程就行,自行指定继承的SpringBoot父工程
   3).企业中手动创建项目,指定版本
        group: com.itheima
        项目名:mp_demo
        jdk:使用8
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.0</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>

    <!--注意事项1:由于mybatisPlus并未被收录到idea的系统内置配置,无法直接选择加入-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.1</version>
    </dependency>

    <!--数据源 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.16</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
   4).制作实体类与表结构
      (类名与表名对应,属性名与字段名对应)
create database if not exists mybatisplus_db character set utf8;
use mybatisplus_db;
CREATE TABLE user (
    id bigint(20) primary key auto_increment,
    name varchar(32) not null,
    password  varchar(32) not null,
    age int(3) not null ,
    tel varchar(32) 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,'tom','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');
package com.itheima.domain;
//pojo, entity, domain

@Data
public class User {
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
    
    //可以使用lombok自动生成
    //自行添加getter、setter、toString()等方法
}
  5).设置Jdbc参数(application.yml)
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource #可选,如果使用必须引入Druid坐标
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC
    username: root
    password: root
  6).定义数据接口,继承BaseMapper
      
@Mapper
public interface UserDao extends BaseMapper<User> {
    //注意:BaseMapper后边必须指定泛型User,以此来确定操作的表
}
  7).测试类中注入dao接口,测试功能
      SpringBoot引导类(必须放到某一个包下)

@SpringBootApplication
public class Mybatisplus01QuickstartApplication {

    public static void main(String[] args) {
        SpringApplication.run(Mybatisplus01QuickstartApplication.class, args);
    }
}
/************************************************************************************/

打开自动生成的测试类,编写单元测试:
    
@SpringBootTest
public class Mybatisplus01QuickstartApplicationTests {

    @Autowired
    private UserDao userDao;

    @Test
    void testGetAll() {
        List<User> userList = userDao.selectList(null);
        System.out.println(userList);
    }
    
    @Test
    void testFindById() {
        //业务层: get(list), remove, modify(update), save
        //数据层:select, delete, update, insert
        User user = userDao.selectById(1);
        System.out.println(user);
    }
}

2、MyBatisPlus概述

MyBatisPlus介绍

- MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率。
- 官网:https://mybatis.plus/  https://mp.baomidou.com


MyBatisPlus特性:

- 无侵入:只做增强不做改变,不会对现有工程产生影响
- 强大的 CRUD 操作:内置通用 BaseMapper,少量配置即可实现单表CRUD 操作
- 支持 Lambda:编写查询条件无需担心字段写错
- 支持主键自动生成(雪花算法)
- 内置分页插件(MyBatis分页需要用到Pagehelper通过拦截器给SQL追加limit 0,10)
- ……

二、标准数据层开发【重点】

1、CRUD操作

功能自定义接口MP接口
新增boolean save(T t)int insert(T t)
删除boolean delete(int id)int deleteById(Serializable id)
修改boolean update(T t)int updateById(T t)
根据id查询T getById(int id)T selectById(Serializable id)
查询全部List<T> getAllList<T> selectList()
分页查询PageInfo<T> getAll(int page,int size)IPage<T> selectPage(IPage<T> page)
按条件查询List<T> getAll(Condition condition)IPage<T> selectPage(Wrapper<T> queryWrapper)
编写单元测试,完成对user表的增删查改:
@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {

    @Autowired
    private UserDao userDao;

    @Test
    void testSave() {
        User user = new User();
        user.setName("程序员");
        user.setPassword("it");
        user.setAge(12);
        user.setTel("400000");
        userDao.insert(user);
    }

    @Test
    void testDelete() {
        userDao.deleteById(2L);
    }

    @Test
    void testUpdate() {
        User user = new User();
        user.setId(1L);
        user.setName("Tom888");
        user.setPassword("tom888");
        userDao.updateById(user);
    }

    @Test
    void testGetById() {
        User user = userDao.selectById(2L);
        System.out.println(user);
    }


    @Test
    void testGetAll() {
        List<User> userList = userDao.selectList(null);
        System.out.println(userList);
    }
} 

2、Lombok插件【常用】

Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发。
<!--自动生成get,set,toString等方法-->
<!--实体类上添加@Data即可-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

常用注解:@Data,为当前实体类在编译期设置对应的get/set方法,无参/无参构造方法,toString方法,hashCode方法,equals方法等
package com.itheima.domain;

import lombok.*;
/*
    1 生成getter和setter方法:@Getter、@Setter
      生成toString方法:@ToString
      生成equals和hashcode方法:@EqualsAndHashCode

    2 统一成以上所有:@Data

    3 生成空参构造: @NoArgsConstructor
      生成全参构造: @AllArgsConstructor

    4 lombok还给我们提供了builder的方式创建对象,好处就是可以链式编程。 @Builder【扩展】
 */
@Data
public class User {
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
}

3、分页功能

1、引入Pagehelper坐标(通过拦截器给sql语句加limit 0,10)
2、Pagehelper.startPage(size, page)
1).设置分页拦截器作为Spring管理的bean
    
@Configuration
public class MybatisPlusConfig {
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        //1 创建MybatisPlusInterceptor拦截器对象
        MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
        //2 添加分页拦截器:当调用selectPage方法时自动在最后追加分页实现:limit 0,10
        mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mpInterceptor;
    }
}

2).执行分页查询
    
//分页查询
@Test
void testSelectPage(){
    //1 创建IPage分页对象,设置分页参数
    IPage<User> page=new Page<>(1,3);
    //2 执行分页查询
    userDao.selectPage(page,null);
    //框架底层,会把查询到的数据放到page对象中
    //page.setRecords(new ArrayList<>());
    //page.setTotal(100);
    
    //3 获取分页结果
    System.out.println("当前页码值:"+page.getCurrent());
    System.out.println("每页显示数:"+page.getSize());
    System.out.println("总页数:"+page.getPages());
    System.out.println("总条数:"+page.getTotal());
    System.out.println("当前页数据:"+page.getRecords());
}
3).输出SQL到控制台

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC
    username: root
    password: root
# 开启mp的日志(输出到控制台)
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

三、DQL编程控制【重点】

DQL:数据查询Query语言(select)
DML:数据操作语言(insert,update,delete)

1、简化日志【了解】

SpringBoot底层是logback框架记录日志的
做法:在resources下新建一个logback.xml文件,名称固定,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>

</configuration>
# 取消SpringBoot启动banner图标

#spring:
  main:
    banner-mode: off # 关闭SpringBoot启动图标(banner)
    
## 取消MybatisPlus启动banner图标

# mybatis-plus日志控制台输出
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    banner: off # 关闭mybatisplus启动图标

2、条件查询方式

1、条件查询方式
    MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合。

//方式一:按条件查询
QueryWrapper<User> qw = new QueryWrapper<>();
//lt小于:less than; gt大于:greater than
//le小于等于:less than or equal
//ge大于等于:greater than or equal
qw.lt("age", 18); //where age < 18
List<User> userList = userDao.selectList(qw); //select * from user
System.out.println(userList);
2、lambda格式按条件查询
    
//方式二:lambda格式按条件查询qw.lambda()
QueryWrapper<User> qw = new QueryWrapper<User>();
qw.lambda().lt(User::getAge, 18);
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
3、lambda格式按条件查询(推荐)
    
//方式三:lambda格式按条件查询
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();

lqw.lt(User::getAge, 18);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);


//方式三变种1:通过Wrappers的静态方法获取
//LambdaQueryWrapper<User> lqw = Wrappers.lambdaQuery();
//lqw.lt(User::getAge, 18);
//List<User> userList = userDao.selectList(lqw);
//System.out.println(userList);

//方式三变种2:链式编程
//List<User> users = userDao.selectList(
//        Wrappers.<User>lambdaQuery() //产生查询条件对象
//                .lt(User::getAge, 18));
//System.out.println(users);

3、组合条件查询

1、并且关系(and)
    
//并且关系:and
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//并且关系:10到30岁之间,默认多个条件使用and连接
lqw.lt(User::getAge, 30).gt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
2、或者关系(or)
    
//或者关系
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//或者关系:小于18岁或者大于10岁
//where age < 18 or age > 10
lqw.lt(User::getAge, 18).or().gt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
3NULL值处理
   
    在多条件查询中,有条件的值为空
    
    1).模拟用户查询条件类:UserQuery 继承需要限制条件的类,定义相同类型的属性限制需要被限制的字段。
    
@Data
public class UserQuery extends User {
    //此时对年龄进行限制
    private Integer age2; //最大年龄
}
/********************************************************************************************************/
    2).if语句控制条件追加
        
//模拟页面传递过来的查询数据
UserQuery uq = new UserQuery();
uq.setAge(10);
uq.setAge2(30);

LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.lt(User::getAge, uq.getAge2());

//null判定
if (null != uq.getAge()) {
    lqw.gt(User::getAge, uq.getAge());
}
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
/*************************************************************************************************************/
    3).条件参数控制【常用】
 
//null判断:第二种,条件参数控制
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//先判定第一个参数是否为true,如果为true连接当前条件
lqw.lt(null != uq.getAge2(), User::getAge, uq.getAge2());
lqw.gt(null != uq.getAge(), User::getAge, uq.getAge());
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);

/**************************************************************************************************************/
    4).条件参数控制(链式编程)
        
//先判定第一个参数是否为true,如果为true连接当前条件
//lqw.lt(null != uq.getAge2(), User::getAge, uq.getAge2());
//lqw.gt(null != uq.getAge(), User::getAge, uq.getAge());
lqw
   .lt(null != uq.getAge2(), User::getAge, uq.getAge2())
   .gt(null != uq.getAge(), User::getAge, uq.getAge());

4、查询投影

1.查询结果包含实体类中部分属性【常用】
    
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.select(User::getId, User::getName, User::getAge);

/*QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("id", "name", "age", "tel");*/

List<User> userList = userDao.selectList(lqw);
System.out.println(userList);    
2 查询结果包含实体类中未定义的属性【了解】
    这个统计SQL在企业开发中,一般会使用MyBatis在xml中编写sql
SELECT COUNT(*) count,tel FROM user GROUP BY tel
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("count(*) as count, tel"); //统计

lqw.groupBy("tel"); //分组
//qw.lambda().groupBy(User::getTel);

List<Map<String, Object>> userList = userDao.selectMaps(lqw);
System.out.println(userList);

//System.out.println(userDao.selectCount(null)); //查询总记录数
# 3、查询条件设置

多条件查询有哪些组合?

- 范围匹配(> 、 = 、between)
- 模糊匹配(like)
- 空判定(null)
- 包含性匹配(in)
- 分组(group)
- 排序(order by)
- ……
- 用户登录(eq匹配)

LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//eq()等同于=
lqw.eq(User::getName, "Jerry").eq(User::getPassword, "jerry");
//如果查询返回的不是一条数据或者null,如果有多条直接抛异常
User loginUser = userDao.selectOne(lqw); 
System.out.println(loginUser);
- 购物设定价格区间、户籍设定年龄区间(le ge匹配 或 between匹配)
    
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//范围查询 lt le gt ge eq between
lqw.between(User::getAge, 10, 30);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
- 查信息,搜索新闻(非全文检索版:like匹配)
    
//模糊匹配
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//%,_必须是一个
//likeLeft() == where name like '%J'
//likeRight() == where name like 'J%'
lqw.like(User::getName, "J"); //where name like '%J%'
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
/*****************************************************************/
- 排序
    
//排序
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//lqw.orderByAsc(User::getAge); //升序
lqw.orderByDesc(User::getAge); //降序
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);

# 查询API

- 更多查询条件设置参看https://baomidou.com/pages/10c804/

5、映射匹配兼容【了解】

# 创建新的表tbl_user做测试:

USE mybatisplus_db;
CREATE TABLE tbl_user (
	id BIGINT ( 20 ) PRIMARY KEY auto_increment,
	`name` VARCHAR ( 32 ) NOT NULL,
	pwd VARCHAR ( 32 ) NOT NULL,
	age INT ( 3 ) NOT NULL,
	tel VARCHAR ( 32 ) NOT NULL,
	deleted INT(1) DEFAULT '0',
	version INT(11) DEFAULT '1'
);

insert into tbl_user(name,pwd,age,tel) values('snake','123456',28,'12345678910');
insert into tbl_user(name,pwd,age,tel) values('张益达','123456',22,'12345678910');
insert into tbl_user(name,pwd,age,tel) VALUES('张大炮','123456',16,'12345678910');
// 1.表字段与编码属性设计不同步

   在模型类属性上方,使用@TableField属性注解,通过value属性,设置当前属性对应的数据库表中的字段关系。

@TableField(value = "pwd")//给password定义了别名对应数据库中的字段
private String password;

// 2.编码中添加了数据库中未定义的属性

    在模型类属性上方,使用@TableField注解,通过exist属性,设置属性在数据库表字段中是否存在,默认为true。此属性无法与value合并使用。
        
//select online from user;
@TableField(exist = false) //设置的属性在数据库表字段中是否存在(默认为true存在)
private Integer online;

// 3.采用默认查询开放了更多的字段查看权限

    在模型类属性上方,使用@TableField注解,通过select属性:设置该属性是否参与查询。此属性与select()映射配置不冲突。
@TableField(value="pwd",select=false) //将密码阶段设置不参与查询
private String password;


//4.表名与编码开发设计不同步

     在模型类上方,使用@TableName注解,通过value属性,设置当前类对应的数据库表名称。
         
@Data
@TableName("tbl_user")
public class User {
    /*
        id为Long类型,因为数据库中id为bigint类型,
        并且mybatis有自己的一套id生成方案,生成出来的id必须是Long类型
     */
    private Long id;
    private String name;
    @TableField(value = "pwd",select = false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist = false) //表示online字段不参与CRUD操作
    private Boolean online;
}

四、DML编程控制【重点】

DQL:数据查询语言(select)

DML:数据操作语言(insert, update, delete)

1.ID生成策略控制(Insert)

不同的表应用不同的id生成策略

- 日志:自增(1,2,3,4,……)
- 购物订单:特殊规则(FQ23948AK3843)
- 外卖单:关联地区日期等信息(10 04 20200314 34 91)
- 关系表:可省略id
- ……

- id生成策略控制
名称:@Tableld
类型:属性注解
位置:模型类中用于表示主键的属性定义上方
作用:设置当前类中主键属性的生成策略
相关属性
     type:设置主键属性的生成策略,值参照IdType枚举值
public class User{
   @Tableld(type = IdType.AUTO)
    /**
    *   AUTO(0):使用数据库id自增策略控制id生成
    *   NONE(1):不设置id生成策略
    *  INPUT(2):用户手工输入id
    *  ASSIGN_ID(3):雪花算法生成id(可兼容数值型与字符串型)
    *  ASSIGN_UUID(4):以UUID生成算法作为id生成策略
    */
   private Long id;
} 
** 雪花(snowflake)算法:Twitter开源的一个生成不重复ID的算法scala **

- 其核心思想是:第一个是符号位,0代表是正数;使用41bit作为毫秒数;10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号。
0001001101110110101011000011010100110001101000010001000000000010
占位符时间戳(41)数据中心(5)机器ID(5)流水号
    计算机使用一个叫做实时时钟(Real-Time Clock,简称 RTC)的硬件设备来跟踪时间。RTC一般以晶振的形式存在,每秒钟产生一次脉冲。
    操作系统通过读取RTC的脉冲来确定时间的流逝,从而计算出当前的日期和时间。当我们电脑中硬件设备受到外界影响就会导致时间的不准确过快或
    者过慢,从而会使得时间校正,而在这段或快或慢的时间段中可能出现由雪花算法生成的id,但是矫正完之后,又有可能生成新的雪花id和上一个id相同。

  - 时钟回拨:可能导致生成重复的ID,可以使用第三方成熟方案:百度IdGenerator,美团Leaf
全局策略配置

mybatis-plus:
  global-config:
    db-config:
      id-type: assign_id #雪花算法
      table-prefix: tbl_   # tbl_ + User.java -> select * from tbl_user
      
# 设置前缀为tbl_的表的id 设置成雪花算法生成的id

2.批量操作

1、按照主键删除多条记录
   
//删除指定多条数据
List<Long> list = new ArrayList<>();
list.add(1402551342481838081L);
list.add(1402553134049501186L);
list.add(1402553619611430913L);

//where id in (1,2,3)
userDao.deleteBatchIds(list);

2、 根据主键查询多条记录
    
//查询指定多条数据
List<Long> list = new ArrayList<>();
list.add(1L);
list.add(3L);
list.add(4L);
List<User> users = userDao.selectBatchIds(list);

3.逻辑删除

  
- 删除操作业务问题:业务数据从数据库中丢弃
- 物理删除:将数据从硬盘(数据库)当中删除(delete from user where id = 1)
- 逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,
- 数据保留在数据库中(update user set deleted=1 where id = 1)

1、数据库表中添加逻辑删除标记字段,deleted默认值为0

- 注意:不要使用SQL的关键字做为表名或者字段名,容易出错
    所以:订单order - orders
    是否删除字段delete -> deleted
2、实体类中添加对应字段,并设定当前字段为逻辑删除标记字段
 
@Data
public class User {

    private Long id;
    
    //逻辑删除字段,标记当前记录是否被删除
    //@TableLogic(value = "0", delval = "1")
    private Integer deleted;
    
}
3、配置全局逻辑删除字面值
        2和3,任选一种即可
mybatis-plus:
  global-config:
    db-config:
      table-prefix: tbl_
      # 逻辑删除字段名
      logic-delete-field: deleted
      # 逻辑删除字面值:未删除为0
      logic-not-delete-value: 0
      # 逻辑删除字面值:删除为1
      logic-delete-value: 1
- 逻辑删除本质:其实是修改操作。如果加了逻辑删除字段,查询数据时也会自动带上逻辑删除字段。

/删除一条数据,观察字段deleted变化

//deleted会由0 -> 1

userDao.deleteById(1450393635949465602L);

`说明:当加入了逻辑删除之后,后续查询或者更新时,自动多一个条件(where deleted = 0)`

4.乐观锁与悲观锁

乐观锁和悲观锁都是用于解决并发场景下的数据竞争问题,但是却是两种完全不同的思想。

- 它们的使用非常广泛,也不局限于某种编程语言或数据库。

数据库自身解决并发两种策略:

- 悲观锁(Pessimistic Lock):
  很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,降低了性能,适用于写多读少。
三种常见的悲观锁解决方案的区别:

1. synchronized关键字:
   - 线程访问资源时自动获取锁,代码块执行完毕后自动释放锁。
   - 无法手动中断获取锁的线程,只能等待锁的释放。
   - 只支持独占锁,不支持公平性设置。
   - 隐式地进行锁的获取和释放,使用相对较简单。

2. ReentrantLock类:
   - 可以手动地获取锁和释放锁,更灵活地控制锁的粒度。
   - 可以设置是否公平获取锁,即等待时间较长的线程优先获取锁。
   - 支持可中断获取锁,即等待获取锁的线程可被中断。
   - 支持多个条件变量(Condition),可以精确地进行线程通信和等待。

3. ReadWriteLock接口:
   - 提供读写锁的支持,可以允许多个线程同时读取共享数据,但只允许一个线程写入数据。
   - 读锁是共享的,可同时被多个线程持有,但写锁是独占的,只能被一个线程持有。
   - 读锁的并发性能好于独占锁,适用于读操作频繁、写操作较少的场景。
   - 可以通过读写锁的方式提高并发性能,减少对共享资源的争用。

    总的来说,synchronized是最基本且简便的悲观锁实现方式,ReentrantLock类提供了更高级的功能和灵活性,
    而ReadWriteLock接口则适用于读多写少的场景。根据具体需求和场景,可以选择合适的锁机制来保障线程安全性和性能。
-- 关闭数据库自动提交

-- 在查询语句后添加for update,加行锁
select * from tb_user where id = 1 for update;

-- 更新数据
-- update xxx

-- 事务提交或回滚后,锁会释放
commit;
rollback;
- 乐观锁(Optimistic Lock)
   很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,它并非是真的锁,但是在更新的时候会判断一下在此期间别人
   有没有去更新这个数据,可以使用版本号等机制;使用读多写少,性能比悲观锁高。
1.数据库表中添加锁标记字段, 默认值为1
2.实体类中添加对应字段,并设定当前字段为逻辑删除标记字段

@Data
public class User {

	private Long id;
    
    //省略其他属性
	
    @Version //用这个字段实现乐观锁
    private Integer version;
}
3、配置乐观锁拦截器实现锁机制对应的动态SQL语句拼装
   
- 如果是常用的功能,直接就能用(逻辑删除)
- 乐观锁并不是所有的更新都要用,如果需要用,得自己来添加拦截器
//mybatis plus的配置类

@Configuration
public class MpConfig {
    @Bean
    public MybatisPlusInterceptor mpInterceptor() {
        //1.定义Mp拦截器
        MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
        //2.添加分页拦截器
        mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        //3.添加乐观锁拦截器: set version = version+1 where version =?
        mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mpInterceptor;
    }
}
4、使用乐观锁机制在修改前必须先获取到对应数据的verion方可正常进行
    
@Test
void testUpdate() throws InterruptedException {
    //1.先通过要修改的数据id将当前数据查询出来
    //目的:在更新之前必须先知道当前的version
    User user = userDao.selectById(222L);
    System.out.println(user);
    //2.将要修改的属性逐一设置进去
    user.setName("Jock888");
    Thread.sleep(15 * 1000); //在休眠期间使用MySQL客户端去修改version值
    //UPDATE tbl_user SET name=?, age=?, tel=?, version=?
    // WHERE id=? AND version=? AND deleted=0
    int num = userDao.updateById(user);
    if (num == 0 ) { //在我查询 和 修改 之间,有其他人(线程)修改了此数据,导致版本号发生了变化
        //数据库受影响行数==0,说明修改失败,有两种处理方案:
        //1. 提示用户修改失败,让用户自行决定是否再次修改
        System.err.println("修改失败,需要再次修改");

        //2. TODO 重新调用查询+修改,自动重试(危险)
    } else {
        System.out.println("修改成功");
    }
}
- 1、乐观锁实现,必须先查询,再更新

- 2、乐观锁拦截器负责添加2个sql片段:
    update user set version = 原来的版本号+1 where version=原来查询出来的版本号 
    //UPDATE tbl_user SET name=?, age=?, tel=?, version=? WHERE id=? AND version=?

五、代码生成器【了解】

1.MyBatisPlus提供模板

根据数据库中的表字段,自动生成实体类、数据层、业务层、表现层代码。
第一步:添加代码生成器相关依赖
<!--代码生成器-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
</dependency>

<!--模板引擎:velocity, jsp, freemarker-->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.3</version>
</dependency>
第二步:编写代码生成器类

public class Generator {
    public static void main(String[] args) {
        //1. 创建代码生成器对象,执行生成代码操作
        AutoGenerator autoGenerator = new AutoGenerator();
        
        //设置生成目录
        autoGenerator.setGlobalConfig(new GlobalConfig().setOutputDir("c:/work"));

        //2. 数据源相关配置:读取数据库中的信息,根据数据库表结构生成代码
        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);

         //3. 执行生成操作
        autoGenerator.execute();
    }
}

2.开发者自定义配置

//设置全局配置

GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(System.getProperty("user.dir")+"/mybatisplus_04_generator/src/main/java");   
 //设置代码生成位置
 
globalConfig.setOpen(false);    //设置生成完毕后是否打开生成代码所在的目录
globalConfig.setAuthor("Wen阿杜");    //设置作者
globalConfig.setFileOverride(true);     //设置是否覆盖原始生成的文件
globalConfig.setMapperName("%sDao");    //设置数据层接口名,%s为占位符,指代模块名称
globalConfig.setIdType(IdType.ASSIGN_ID);   //设置Id生成策略
autoGenerator.setGlobalConfig(globalConfig);
//设置包名相关配置

PackageConfig packageInfo = new PackageConfig();
packageInfo.setParent("com.abc");   //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径
packageInfo.setEntity("pojo");    //设置实体类包名:entity, domain
packageInfo.setMapper("dao");   //设置数据层包名:mapper
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);  //设置是否启用lombok
autoGenerator.setStrategy(strategyConfig);

3、其他实现

- 使用MyBatisX插件
- 开源:https://gitee.com/renrenio/renren-security/tree/master/renren-generator
- 公司自己实现的……

MyBatis和MyBatisPlus的使用场景:

  • 单表操作:使用MP,不用写SQL语句
  • 多表关联查询:使用MyBatis在xml中写SQL实现
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 如果你想要入门 MyBatis-Plus,可以按照以下步骤进行: 1. 首先,你需要在你的项目中引入 MyBatis-Plus 的依赖。可以在你的项目的 pom.xml 文件中添加以下依赖: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>最新版本号</version> </dependency> ``` 请确保将最新版本号替换为你想要使用的 MyBatis-Plus 版本号。 2. 然后,你需要配置 MyBatis-Plus。你可以在你的项目中创建一个 MyBatisPlusConfig 类,并在该类中注册一些必要的 Bean。例如,如果你想要使用乐观锁插件,你可以在 MyBatisPlusConfig 类中注册一个 OptimisticLockerInterceptor 的 Bean,如下所示: ```java @Configuration public class MyBatisPlusConfig { @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } } ``` 这样就完成了乐观锁插件的注册。 3. 最后,你需要在 MyBatis 的配置文件中配置 MyBatis-Plus 的一些属性。例如,如果你想要在控制台输出 MyBatis-Plus 的日志,你可以在配置文件中添加以下配置: ``` mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl ``` 这样就完成了 MyBatis-Plus 的入门配置。 总结起来,要入门 MyBatis-Plus,你需要引入依赖、配置 MyBatisPlusConfig 类和配置 MyBatis-Plus 的属性。这样就可以开始使用 MyBatis-Plus 来简化开发和提高效率了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [MybatisPlus入门教程](https://blog.csdn.net/qq_44732432/article/details/129221273)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值