MybatisPlus学习总结

MybatisPlus.xmind

一、MybatisPlus快速入门

1.基本介绍

官网:
简介 | MyBatis-Plus
MyBatis Plus是一个基于MyBatis的增强工具,它简化了MyBatis的使用,提供了一系列的增强功能,使开发更加方便快捷。
MyBatis Plus的主要特点包括:

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作

2.快速入门

准备数据库和表

 DROP TABLE IF EXISTS `user`;

CREATE TABLE `user`
(
    id BIGINT NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    age INT 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');

创建springboot项目

引入依赖
   <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
配置文件
# 应用服务 WEB 访问端口
server.port=8082
#下面这些内容是为了让MyBatis映射
#指定Mybatis的Mapper文件
mybatis.mapper-locations=classpath:mapper/*xml
#指定Mybatis的实体目录
#mybatis.type-aliases-package=com.pkq.springboot.mybatis.entity
# MySQL数据库驱动
#这个驱动也可以省略,可以根据使用的MySQL自动加载相应的驱动
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据源名称
spring.datasource.name=defaultDataSource
# 数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/mybatisPlus?serverTimezone=UTC&zeroDateTimeBehavior=convertToNull
# 数据库用户名和密码
spring.datasource.username=root
spring.datasource.password=123456



MybatisPlus默认开启驼峰映射

实体类
package com.pkq.demo.pojo;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * ClassName: User
 * Package: com.pkq.demo.pojo
 * Description:
 *
 * @Author pkq
 * @Create 2024/3/22 15:42
 * @Version 1.0
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")//指定表名
@Builder
public class User {
    private Integer id;
    private  String name;
    private Integer age;
    private String email;
}

如果表的名字和实体类名字一样,就不要进行二者之间的映射

mapper接口
package com.pkq.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pkq.demo.pojo.User;

/**
 * ClassName: UserMapper
 * Package: com.pkq.demo.mapper
 * Description:
 *
 * @Author pkq
 * @Create 2024/3/22 15:48
 * @Version 1.0
 */
public interface UserMapper extends BaseMapper<User> {
}

扫描mapper

在boot启动类添加@MapperScan进行扫描Mapper文件夹

package com.pkq.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.pkq.demo.mapper")
public class DemoApplication {

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

}

测试
package com.pkq.demo;

import com.pkq.demo.mapper.UserMapper;
import com.pkq.demo.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.List;

/**
 * ClassName: TestUser
 * Package: com.pkq.demo
 * Description:
 *
 * @Author pkq
 * @Create 2024/3/22 15:52
 * @Version 1.0
 */
@SpringBootTest
public class TestUser {
    @Resource
    private UserMapper userMapper;
    @Test
    public void testUser(){
        //通过条件查询来查询一个list,如果没有条件,则写null
        List<User> users = userMapper.selectList(null);
        for (User user:users){
            System.out.println(user);
        }

    }
}

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)
配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

image.png

二、增删改查

1.BaseMapper源码

image.png

package com.baomidou.mybatisplus.core.mapper;
public interface BaseMapper<T> extends Mapper<T> {
/**
* 插入一条记录
* @param entity 实体对象
*/
int insert(T entity);
/**
* 根据 ID 删除
* @param id 主键ID
*/
int deleteById(Serializable id);
/**
* 根据实体(ID)删除
* @param entity 实体对象
* @since 3.4.4
*/
更多Java –大数据 – 前端 – UI/UE - Android - 人工智能资料下载,可访问百度:尚硅谷官网(www.atguigu.com)
int deleteById(T entity);
/**
* 根据 columnMap 条件,删除记录
* @param columnMap 表字段 map 对象
*/
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* 根据 entity 条件,删除记录
* @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where
语句)
*/
int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 删除(根据ID 批量删除)
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends
Serializable> idList);
/**
* 根据 ID 修改
* @param entity 实体对象
*/
int updateById(@Param(Constants.ENTITY) T entity);
/**
* 根据 whereEntity 条件,更新记录
* @param entity 实体对象 (set 条件值,可以为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成
where 语句)
*/
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER)
Wrapper<T> updateWrapper);
/**
* 根据 ID 查询
* @param id 主键ID
*/
T selectById(Serializable id);
/**
* 查询(根据ID 批量查询)
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends
Serializable> idList);
/**
* 查询(根据 columnMap 条件)
* @param columnMap 表字段 map 对象
*/
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object>
columnMap);
/**
更多Java –大数据 – 前端 – UI/UE - Android - 人工智能资料下载,可访问百度:尚硅谷官网(www.atguigu.com)
* 根据 entity 条件,查询一条记录
* <p>查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常
</p>
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {
List<T> ts = this.selectList(queryWrapper);
if (CollectionUtils.isNotEmpty(ts)) {
if (ts.size() != 1) {
throw ExceptionUtils.mpe("One record is expected, but the query
result is multiple records");
}
return ts.get(0);
}
return null;
}
/**
* 根据 Wrapper 条件,查询总记录数
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T>
queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
* <p>注意: 只返回第一个字段的值</p>
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录(并翻页)
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
<P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER)
Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录(并翻页)
* @param page 分页查询条件
* @param queryWrapper 实体对象封装操作类
*/
<P extends IPage<Map<String, Object>>> P selectMapsPage(P page,
@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

2.insert

image.png

   @Test
    public void testInsertUser(){
        //新增用户信息
        User user=new User();
        user.setName("皮卡丘");
        user.setAge(20);
        user.setEmail("pkq@pkq.com");
        int row = userMapper.insert(user);
        System.out.println("受影响的行数:"+row);
        System.out.println(user);

    }

image.png

最终执行插入操作以后,id的值不是自增的,因为MyBatis-Plus在实现插入数据时,会默认基于雪花算法的策略生成id
:

3.delete

image.png

3.1通过id删除

@Test
public void testDeleteById(){
//通过id删除用户信息
//DELETE FROM user WHERE id=?
    int result = userMapper.deleteById(1475754982694199298L);
    System.out.println("受影响行数:"+result);
}

3.2通过id批量删除

 @Test
    public void testDeleteBatchIds(){
//通过多个id批量删除
//DELETE FROM user WHERE id IN ( ? , ? , ? )
        List<Long> idList = Arrays.asList(1L, 2L, 3L);
        int result = userMapper.deleteBatchIds(idList);
        System.out.println("受影响行数:"+result);
    }

3.3通过map来删除

@Test
public void testDeleteByMap(){
    //根据map集合中所设置的条件删除记录
    //DELETE FROM user WHERE name = ? AND age = ?
    Map<String, Object> map = new HashMap<>();
    map.put("age", 23);
    map.put("name", "张三");
    int result = userMapper.deleteByMap(map);
    System.out.println("受影响行数:"+result);
}

4.update

@Test
public void testUpdateById(){
    User user = new User(4L, "admin", 22, null);
    //UPDATE user SET name=?, age=? WHERE id=?
    int result = userMapper.updateById(user);
    System.out.println("受影响行数:"+result);
}

5.select

5.1根据id查询信息

@Test
public void testSelectById(){
    //根据id查询用户信息
    //SELECT id,name,age,email FROM user WHERE id=?
    User user = userMapper.selectById(4L);
    System.out.println(user);
}

5.2根据多个id查询

@Test
public void testSelectBatchIds(){
    //根据多个id查询多个用户信息
    //SELECT id,name,age,email FROM user WHERE id IN ( ? , ? )
    List<Long> idList = Arrays.asList(4L, 5L);
    List<User> list = userMapper.selectBatchIds(idList);
    list.forEach(System.out::println);
}

5.3通过map条件进行查询

@Test
public void testSelectByMap(){
    //通过map条件查询用户信息
    //SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
    Map<String, Object> map = new HashMap<>();
    map.put("age", 22);
    map.put("name", "admin");
    List<User> list = userMapper.selectByMap(map);
    list.forEach(System.out::println);
}

5.4查询所有数据

  @Test
    public void testUser(){
        //通过条件查询来查询一个list,如果没有条件,则写null
      //SELECT id,name,age,email FROM user
        List<User> users = userMapper.selectList(null);
        for (User user:users){
            System.out.println(user);
        }

    }

**通过观察BaseMapper中的方法,大多方法中都有Wrapper类型的形参,此为条件构造器,可针 **
**对于SQL语句设置不同的条件,若没有条件,则可以为该形参赋值null,即查询(删除/修改)所 **
有数据

6.通用service

MyBatis-Plus中有一个接口 IService和其实现类 ServiceImpl,封装了常见的业务层逻辑

说明:
通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行 remove 删
除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,
泛型 T 为任意实体对象
建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承
Mybatis-Plus 提供的基类
官网地址:https://baomidou.com/pages/49cc81/#service-crud-%E6%8E%A5%E5%8F%
A3
:::
MybatisPlus为了开发更加快捷,对业务层也进行了封装,直接提供了相关的接口和实现类;我们在进行业务层开发时,可以继承它提供的接口和实现类,使得编码更加高效;
实现流程:

  1. 定义一个服务扩展接口,该接口继承公共接口IService;
  2. 定义一个服务实现类,该类继承ServiceImpl<Mapper,Entity>,并实现自定义的扩展接口;

注意事项:
1.ServiceImpl父类已经注入了UserMapper对象,名称叫做baseMapper,所以当前实现类直接可以使用baseMapper完成操作2.因为ServiceImpl已经实现了IService下的方法,所以当前服务类没有必要再次实现
思想:共性的业务代码交给框架封装维护,非共性的业务,在接口UserService定义,然后在当前的服务类下实现;

image.png

6.1创建service接口及其实现类

package com.pkq.demo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.pkq.demo.pojo.User;

/**
 * ClassName: UserService
 * Package: com.pkq.demo.service
 * Description:
 *
 * @Author pkq
 * @Create 2024/3/23 14:44
 * @Version 1.0
 */
public interface UserService extends IService<User> {
}

package com.pkq.demo.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pkq.demo.mapper.UserMapper;
import com.pkq.demo.pojo.User;
import com.pkq.demo.service.UserService;
import org.springframework.stereotype.Service;

/**
 * ClassName: UserServiceImpl
 * Package: com.pkq.demo.service.impl
 * Description:
 *
 * @Author pkq
 * @Create 2024/3/23 14:44
 * @Version 1.0
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

6.2测试查询的记录总数

package com.pkq.demo;

import com.pkq.demo.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * ClassName: MybatisPlusTest
 * Package: com.pkq.demo
 * Description:
 *
 * @Author pkq
 * @Create 2024/3/23 14:47
 * @Version 1.0
 */
@SpringBootTest
public class MybatisPlusTest {
    @Autowired
    private UserService userService;
    @Test
    public void testIserVice(){
        long count = userService.count();
        System.out.println(count);
    }
}

image.png

6.3测试批量插入

 @Test
    public void testSaveBatch(){
        ArrayList<User> userArrayList=new ArrayList<>();
        for(int i=0;i<5;i++){
            User user = new User();
            user.setName("pkq"+i);
            user.setAge(20+i);
            userArrayList.add(user);
        }
        userService.saveBatch(userArrayList);
        List<User> list = userService.list();
        for (User user:list){
            System.out.println(user);
        }
    }

image.png

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@d13baac] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1398428044 wrapping com.mysql.cj.jdbc.ConnectionImpl@71179b6f] will not be managed by Spring
==>  Preparing: SELECT id,name,age,email FROM user
==> Parameters: 
<==    Columns: id, name, age, email
<==        Row: -1954500607, 皮卡丘, 20, pkq@pkq.com
<==        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
<==        Row: 562069505, pkq0, 20, null
<==        Row: 704675842, pkq1, 21, null
<==        Row: 704675843, pkq2, 22, null
<==        Row: 767590402, pkq3, 23, null
<==        Row: 767590403, pkq4, 24, null
<==      Total: 11
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@d13baac]
User(id=-1954500607, name=皮卡丘, age=20, email=pkq@pkq.com)
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)
User(id=562069505, name=pkq0, age=20, email=null)
User(id=704675842, name=pkq1, age=21, email=null)
User(id=704675843, name=pkq2, age=22, email=null)
User(id=767590402, name=pkq3, age=23, email=null)
User(id=767590403, name=pkq4, age=24, email=null)

  /**
     * @Description 测试条件查询,且仅返回一个
     * getOne:sql查询的结果必须为1条或者没有,否则报错 !!!!
     */
    @Test
    public void test2(){
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class);
        wrapper.gt(User::getAge,20);
        User one = userService.getOne(wrapper);
        System.out.println(one);
    }

    /**
     * @Description 根据条件批量查询
     */
    @Test
    public void test3(){
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class);
        wrapper.gt(User::getAge,20);
        List<User> list = userService.list(wrapper);
        System.out.println(list);
    }

    /**
     * @Description 根据条件批量查询并分页
     */
    @Test
    public void test4(){
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class);
        wrapper.gt(User::getAge,20);
        //构建分页对象
        IPage<User> page=new Page<>(2,3);
        userService.page(page,wrapper);
        System.out.println(page.getRecords());
        System.out.println(page.getPages());
        System.out.println(page.getTotal());
    }

    /**
     * @Description 测试服务层save保存单条操作
     */
    @Test
    public void test5(){
        User user1 = User.builder().name("wangwu").userName("laowang4").
                email("444@163.com").age(20).password("333").build();
        boolean isSuccess = userService.save(user1);
        System.out.println(isSuccess?"保存成功":"保存失败");
    }

    /**
     * @Description 测试服务层批量保存
     */
    @Test
    public void test6(){
        User user2 = User.builder().name("wangwu2").userName("laowang2").
                email("444@163.com").age(20).password("333").build();
        User user3 = User.builder().name("wangwu3").userName("laowang3").
                email("444@163.com").age(20).password("333").build();
        boolean isSuccess = userService.saveBatch(Arrays.asList(user2, user3));
        System.out.println(isSuccess?"保存成功":"保存失败");
    }
    
    /**
     * @Description 根据id删除操作
     */
    @Test
    public void test7(){
        boolean isSuccess = userService.removeById(17l);
        System.out.println(isSuccess?"保存成功":"保存失败");
    }

    /**
     * @Description 根据条件批量删除
     */
    @Test
    public void test8(){
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class);
        wrapper.gt(User::getId,12)
                .gt(User::getAge,20);
        boolean remove = userService.remove(wrapper);
        System.out.println(remove);
    }

    /**
     * @Description 测试根据id更新数据
     */
    @Test
    public void test9(){
        //UPDATE tb_user SET password=?, t_name=? WHERE id=?
        User user2 = User.builder().name("wangwu2").password("333").id(3l).build();
        boolean success = userService.updateById(user2);
        System.out.println(success);
    }

    /**
     * @Description 测试根据条件批量更新
     */
    @Test
    public void test10(){
        LambdaUpdateWrapper<User> wrapper = Wrappers.lambdaUpdate(User.class);
        //UPDATE tb_user SET age=? WHERE (id IN (?,?,?))
        wrapper.in(User::getId,Arrays.asList(1l,3l,5l)).set(User::getAge,40);
        boolean update = userService.update(wrapper);
        System.out.println(userService);
    }

三、常用注解

经过以上的测试,在使用MyBatis-Plus实现基本的CRUD时,我们并没有指定要操作的表,只是在 Mapper接口继承BaseMapper时,设置了泛型User,而操作的表为user表
由此得出结论,MyBatis-Plus在确定操作的表时,由BaseMapper的泛型决定,即实体类型决
定,且默认操作的表名和实体类型的类名一致

如果实体类的名字和表的名字不一致,我们就需要使用@TableName注解

@TableName

描述:表名注解,标识实体类所对应的表
使用的位置:实体类

@TableName("t_user")
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

**在开发的过程中,我们经常遇到以上的问题,即实体类所对应的表都有固定的前缀,例如t_或tbl_ **
**此时,可以使用MyBatis-Plus提供的全局配置,为实体类所对应的表名设置默认的前缀,那么就 **
不需要在每个实体类上通过@TableName标识实体类对应的表

mybatis-plus:
	configuration:
	# 配置MyBatis日志
		log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
	global-config:
		db-config:
			# 配置MyBatis-Plus操作表的默认前缀
			table-prefix: t_

@TableId

  • 描述:主键注解
  • 使用位置:实体类主键字段
@TableName("t_user")
public class User {
    @TableId
    private Long uid;
    private String name;
    private Integer age;
    private String email;
}
  • MyBatis-Plus在实现CRUD时,会默认将id作为主键列,并在插入数据时,默认基于雪花算法的策略生成id
  • 如果实体类和表中的主键不是id,而且其他字段,比如uid,那么就会报错,我们需要在实体类的uid属性上面添加@TableId注解标识为主键

@TableId的value属性

如果实体类主键对应属性为id,表中主键字段是uid,则需要通过@TableId注解的value属性,指定表中的主键字段

@TableName("t_user")
public class User {
    @TableId(value="uid")
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

@TableId的type属性

type属性用来定义主键策略

常用主键策略
描述
IdType.ASSIGN_ID(默认)基于雪花算法的策略生成数据id,和数据库id是否设置自增无关
IdType.AUTO使用数据库的自增策略,注意:该类型要确保数据库设置了id自增,否则无效
生成策略应用场景特点
IdType.AUTO数据库主键自增(确保数据库设置了 主键自增 否则无效)1.使用数据库自带的主键自增值;
2.数据库自增的主键值会回填到实体类中;
3.数据库服务端生成的;
IdType.ASSIGN_ID主键类型为number类型或数字类型String1.MP客户端生成的主键值;
2.生成的主键值是数字形式的字符串
3.主键对应的类型可以是数字类型或者数字类型的字符串
4.底层基于雪花算法,让数据库的唯一标识也参与id的生成运算,保证id在分布式环境下,全局唯一(避免id的主键冲突问题);
IdType.ASSIGN_UUID主键类型为 string(包含数字和字母组成)1.生成的主键值包含数字和字母组成的字符串;
2.注意事项:如果数据库中主键值是number类型的,可不可用
全局配置主键生成策略
mybatis-plus:
	configuration:
		# 配置MyBatis日志
		log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
	global-config:
		db-config:
			# 配置MyBatis-Plus操作表的默认前缀
			table-prefix: t_
			# 配置MyBatis-Plus的主键策略
			id-type: auto

雪花算法

背景

  • 要选择一个合数发方案来面对数据规模的增长,面对逐渐增长的访问压力和数据量
  • 数据库的扩展方式主要有:业务分库,主从复制,数据库分表

数据库分表

不同业务数据分散存储到不同的数据库服务器,能够支撑百万甚至千万用户规模的业务,但如果业务继续发展,同一业务的单表数据也会达到单台数据库服务器的处理瓶颈。例如,淘宝的几亿用户数据,如果全部存放在一台数据库服务器的一张表中,肯定是无法满足性能要求的,此时就需要对单表数据进行拆分。
单表数据拆分有两种方式:垂直分表和水平分表。示意图如下
image.png

垂直分表

把表中不经常使用而且占很大空间的列拆分出去

水平分表

当表中的数据记录很多的时候,考虑水平分表,但是也会导致更加复杂,需要考虑全局唯一性id

主键自增:可以按照范围来分段,比如1999999放到表1,10000001999999放到表2
如果分段过大,导致单表依然可能存在性能问题,如果分段过小,会导致切分后子表数量过多。
优点:可以随着数据的增加,平滑的扩充新表,比如现在用户100万,若增加到500万,只需要增加新的表即可,原有数据不需要变化。
缺点:分布不均匀,可能有的分段存储的数据量只有很少几条,而其他分段存储数据量很多

**取模 **
①同样以用户 ID 为例,假如我们一开始就规划了 10 个数据库表,可以简单地用 user_id % 10 的值来
表示数据所属的数据库表编号,ID 为 985 的用户放到编号为 5 的子表中,ID 为 10086 的用户放到编号
为 6 的子表中。
②复杂点:初始表数量的确定。表数量太多维护比较麻烦,表数量太少又可能导致单表性能存在问题。
③优点:表分布比较均匀。
④缺点:扩充新的表很麻烦,所有数据都要重分布

雪花算法

雪花算法是由Twitter公布的分布式主键生成算法,它能够保证不同表的主键的不重复性,以及相同表的主键的有序性。
①核心思想:
长度共64bit(一个long型)。
首先是一个符号位,1bit标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0。
41bit时间截(毫秒级),存储的是时间截的差值(当前时间截 - 开始时间截),结果约等于69.73年。
10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID,可以部署在1024个节点)。
12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID)。

image.png

@TableField

MyBatis-Plus在执行SQL语句时,要保证实体类中的属性名和
表中的字段名一致 。如果实体类中的属性名和字段名不一致的情况,会出现什么问题呢?

  • 如果实体类属性用的是驼峰命名,数据库表的字段使用下划线风格,MybatisPlus会自动把下划线命名风格转化为驼峰命名

  • 如果实体类的属性和表中的字段命名不一样,需要在实体类属性上面添加@TableField注解来设置属性对应的字段名
    :::tips
    注解@TableField作用:

  • 指定表中普通字段与实体类属性之间的映射关系;

  • 忽略实体类中多余属性与表中字段的映射关系(@TableField(exist = false));

以下情况可以省略:

  • 名称一样
  • 数据库字段使用_分割,实体类属性名使用驼峰名称(自动开启驼峰映射)
    :::
@TableName("t_user")
public class User {
    @TableId(type=IdType.ASSIGN_ID)
    private Long id;
	@TableField("userName")
    private String name;
    private Integer age;
    private String email;
	//增删改查操作时,忽略该属性
    @TableField(exist = false)
    private String address;
}

@TableLogic(逻辑删除)

  • 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
  • 逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录
  • 使用场景:可以进行数据恢复
  • 本质:修改操作

实际在删除数据时,为了数据留痕,一般选择逻辑删除,也就是为删除表添加逻辑删除字段,通过修改字段状态值来表示数据是否被删除;
image.png
修改表的结构
image.png
0表示未删除,1表示删除

# 设置mp运行时行为
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台输出sql
  global-config:
    db-config:
      logic-delete-field: is_delete # 约定全局删除字段
      logic-delete-value: 1
      logic-not-delete-value: 0

实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")//指定表名
public class User {
    private Integer id;
    private  String name;
    private Integer age;
    private String email;
    @TableLogic
    private Integer isDelete;
}

删除前数据库信息
image.png

 @Test
    public void testDeletetUser(){
        List<Long> list= Arrays.asList(1L,2L,3L);
        int result = userMapper.deleteBatchIds(list);
        System.out.println(result);

    }

image.png
测试查询功能,看看能否查询到逻辑删除的数据
image.png
被逻辑删除的数据是查询不到的。

四、条件构造器和常用接口

1.wrapper

image.png

  • Wrapper : 条件构造抽象类,最顶端父类
    • AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
      • QueryWrapper : 查询条件封装
      • UpdateWrapper : Update 条件封装
      • AbstractLambdaWrapper : 使用Lambda 语法
        • LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
        • LambdaUpdateWrapper : Lambda 更新封装Wrapper

2.QueryWrapper

eq( ) :  等于 =
ne( ) :  不等于 <> 或者 !=
gt( ) :  大于 >
ge( ) :  大于等于  >=
lt( ) :  小于 <
le( ) :  小于等于 <=
between ( ) :  BETWEEN1 AND2 
notBetween ( ) :  NOT BETWEEN1 AND2 
in( ) :  in
notIn( ) :not in

sql中反向查询eg:not like != 等等,查询时是不会走索引的;

2.1数据库准备

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for tb_user  没有给自增
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of tb_user
-- ----------------------------
BEGIN;
INSERT INTO `tb_user` VALUES (1, '赵一伤', '123456', 'zys', 19, 'zys@itcast.cn');
INSERT INTO `tb_user` VALUES (2, '钱二败', '123456', 'qes', 18, 'qes@itcast.cn');
INSERT INTO `tb_user` VALUES (3, '孙三毁', '123456', 'ssh', 20, 'ssh@itcast.cn');
INSERT INTO `tb_user` VALUES (4, '李四摧', '123456', 'lsc', 20, 'lsc@itcast.cn');
INSERT INTO `tb_user` VALUES (5, '周五输', '123456', 'zws', 20, 'zws@itcast.cn');
INSERT INTO `tb_user` VALUES (6, '吴六破', '123456', 'wlp', 21, 'wlp@itcast.cn');
INSERT INTO `tb_user` VALUES (7, '郑七灭', '123456', 'zqm', 22, 'zqm@itcast.cn');
INSERT INTO `tb_user` VALUES (8, '王八衰', '123456', 'wbs', 22, 'wbs@itcast.cn');
INSERT INTO `tb_user` VALUES (9, '张无忌', '123456', 'zwj', 25, 'zwj@itcast.cn');
INSERT INTO `tb_user` VALUES (10, '赵敏', '123456', 'zm', 26, 'zm@itcast.cn');
INSERT INTO `tb_user` VALUES (11, '赵二伤', '123456', 'zes', 25, 'zes@itcast.cn');
INSERT INTO `tb_user` VALUES (12, '赵三伤', '123456', 'zss1', 28, 'zss1@itcast.cn');
INSERT INTO `tb_user` VALUES (13, '赵四伤', '123456', 'zss2', 29, 'zss2@itcast.cn');
INSERT INTO `tb_user` VALUES (14, '赵五伤', '123456', 'zws', 39, 'zws@itcast.cn');
INSERT INTO `tb_user` VALUES (15, '赵六伤', '123456', 'zls', 29, 'zls@itcast.cn');
INSERT INTO `tb_user` VALUES (16, '赵七伤', '123456', 'zqs', 39, 'zqs@itcast.cn');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

2.2基本查询

要求:查询用户中姓名包含"伤",密码为"123456",且年龄为19或者25或者29,查询结果按照年龄降序排序;

package com.pkq.demo;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.pkq.demo.mapper.UserMapper;
import com.pkq.demo.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.List;

/**
 * ClassName: QueryWrapperTest
 * Package: com.pkq.demo
 * Description:
 *
 * @Author pkq
 * @Create 2024/3/28 10:40
 * @Version 1.0
 */
@SpringBootTest
public class QueryWrapperTest {
    @Resource
    private UserMapper userMapper;
	   /**
     * @Description 测试条件查询
     * 要求:查询用户中姓名包含"伤",密码为"123456",且年龄为19或者25或者29,查询结果按照年龄降序排序;
     * 如果查询的条件过于复杂,mp还适合么?
     *        简单的操作,直接使用mp
     *        但是非常复杂的操作,比如多表关联查询 复杂条件查询等,建议使用xml下sql实现;
     */
    @Test
    public void test1(){
        /**
         * SELECT id,user_name,password,name,age,email FROM tb_user
         * WHERE (user_name LIKE ? AND password = ? AND age IN (?,?,?)) ORDER BY age DESC
         *
         * Parameters: %伤%(String), 123456(String), 19(Integer), 25(Integer), 29(Integer)
         */
        QueryWrapper<User> queryWrapper=new QueryWrapper<>();
        queryWrapper.like("user_name","伤")
                .eq("password","123456")
                .in("age",19,25,29)
                .orderByDesc("age");
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user:userList){
            System.out.println(user);
        }

    }
}

image.png

2.3QueryWrapper逻辑查询or

2.3.1 OR查询说明
  1. 通过QueryWrapper多条件查询时,默认使用and关键字拼接SQL;
  2. 通过QueryWrapper调用or()方法时,底层会使用or关键字拼接方法左右的查询条件;
2.3.2代码

业务要求:查询用户名为"lisi"或者年龄大于23的用户信息;

/**
     * 查询用户名为"lisi"或者年龄大于23的用户信息;
     */
    @Test
    public void test2(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name","lisi")
                .or()
                .gt("age",23);
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user:userList){
            System.out.println(user);
        }
    }

image.png

2.4QueryWrapper实现模糊查询like

2.4.1模糊查询常用方法
  • like(“表列名”,“条件值”); 作用:查询包含关键字的信息,底层会自动添加匹配关键字,比如:%条件值%
  • likeLeft(“表列名”,“条件值”); 作用:左侧模糊搜索,也就是查询以指定条件值结尾的数据,比如:%条件值
  • likeRight(“表列名”,“条件值”);作用:右侧模糊搜索,也就是查询以指定条件值开头的数据,比如:条件值%
2.4.2代码
/**
     * 模糊查询
     */
    @Test
    public void testWrapper3(){
        //1.创建查询条件构建器
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        //2.设置条件
        wrapper.likeLeft("user_name","zhang");
        /*
            SELECT id,user_name,password,name,age,email
             from tb_user
             where user_name like ?
             %zhang
         */
        List<User> users = userMapper.selectList(wrapper);
        System.out.println(users);
    }

image.png

2.5QueryWrapper排序查询

2.5.1 核心方法
  • orderByAsc 升序排序,方法内可传入多个字段
  • orderByDesc 降序排序,方法内可传入多个字段

Wrapper 条件构造器中 order by 排序相关的方法如下:

orderBy(boolean condition, boolean isAsc, R... columns)

// 按 id, age 字段进行升序
wrapper.orderBy(true, true, "id", "age");

参数说明:

  • condition : 是否组装条件;
  • isAsc : 是否升序,true 为升序,false 为降序;
2.5.2 代码实现

需求:先根据age升序排序,如果年龄相同则按照id降序排序;

 /**
     * 需求:先根据age升序排序,如果年龄相同则按照id降序排序;
     */
    @Test
    public void testWrapper4(){
        //1.创建查询条件构建器
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        //2.设置条件
        wrapper.orderByAsc("age").orderByDesc("id");
        /*
            select * from tb_user where user_name = ? or age < ? and name in (?,?) order by age asc
         */
        List<User> users = userMapper.selectList(wrapper);
        System.out.println(users);
    }

image.png

2.6QueryWrapper限定字段查询

2.6.1方法说明

MP查询时,默认将表中所有字段数据映射查询,但是有时我们仅仅需要查询部分字段信息,这是可以使用select()方法限定返回的字段信息,避免I/O资源的浪费;
示例:
wrapper.select(“字段1”,“字段2”,…)

2.6.2 代码示例
@Test
public void testWrapper5(){
    //1.创建查询条件构建器
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //2.设置条件
    wrapper.eq("user_name","lisi")
    .or()
    .lt("age",23)
    .in("name","李四","王五")
    //.orderBy(true,true,"age")
    .orderByDesc("age")
    .select("id","user_name");
    /*
             select id,user_name from tb_user where user_name = ? or age < ? and name in (?,?) order by age asc
          */
    List<User> users = userMapper.selectList(wrapper);
    System.out.println(users);
}

2.7QueryWrapper实现分页条件查询

4.7.1 方法说明
//参数1:分页对象
//参数2:查询条件
mapper.selectPage(page,wrapper);
4.7.2 代码实现

需求:查询年龄大于等于23的用户信息,并显示第2页,每页大小为4;

 @Test
    public void testWrapper6(){
        int currentPage=2;//当前第几页
        int currentPageSize=4;//每页有多少条
        //创建分页对象
        IPage<User> pageInfo=new Page<>(currentPage,currentPageSize);
        QueryWrapper<User> wrapper=new QueryWrapper<>();
        wrapper.gt("age",23);
        //分页查询 pageInfo==userIPage -->true
        IPage<User> userIPage = userMapper.selectPage(pageInfo,wrapper);
        //获取分页参数
        long pages = pageInfo.getPages();//总页数
        long current = userIPage.getCurrent();//当前页
        List<User> records = userIPage.getRecords();//当前页的数据
        long total = userIPage.getTotal();//总记录数
        long size = userIPage.getSize();//每页显示条数
        System.out.println("总页数:"+pages);
        System.out.println("当前页:"+current);
        System.out.println("总记录数:"+total);
        System.out.println("每页显示条数:"+size);

        System.out.println("当前页的数据:");
        for (User user:records){
            System.out.println(user);
        }
    }
JDBC Connection [HikariProxyConnection@1202874820 wrapping com.mysql.cj.jdbc.ConnectionImpl@71e7adbb] will not be managed by Spring
==>  Preparing: SELECT COUNT(*) AS total FROM tb_user WHERE (age > ?)
==> Parameters: 23(Integer)
<==    Columns: total
<==        Row: 8
<==      Total: 1
==>  Preparing: SELECT id,user_name,password,name,age,email FROM tb_user WHERE (age > ?) LIMIT ?,?
==> Parameters: 23(Integer), 4(Long), 4(Long)
<==    Columns: id, user_name, password, name, age, email
<==        Row: 13, 赵四伤, 123456, zss2, 29, zss2@itcast.cn
<==        Row: 14, 赵五伤, 123456, zws, 39, zws@itcast.cn
<==        Row: 15, 赵六伤, 123456, zls, 29, zls@itcast.cn
<==        Row: 16, 赵七伤, 123456, zqs, 39, zqs@itcast.cn
<==      Total: 4
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6ba060f3]
总页数:2
当前页:2
总记录数:8
每页显示条数:4
当前页的数据:
User(id=13, userName=赵四伤, password=123456, name=zss2, age=29, email=zss2@itcast.cn)
User(id=14, userName=赵五伤, password=123456, name=zws, age=39, email=zws@itcast.cn)
User(id=15, userName=赵六伤, password=123456, name=zls, age=29, email=zls@itcast.cn)
User(id=16, userName=赵七伤, password=123456, name=zqs, age=39, email=zqs@itcast.cn)

3.LambdaQueryWrapper查询

3.1 使用QueryWrapper开发存在的问题

  1. 使用QueryWrapper查询数据时需要手写对应表的列名信息,及其容易写错,开发体验不好;
  2. 使用QueryWrapper查询数据时,表的列名硬编码书写,后期一旦表结构更改,则会带来很大的修改工作量,维护性较差;

LambdaQueryWrapper可以解决上述出现问题,开发推荐使用;

3.2 代码实现

@Test
    public void testLambdaQueryWrapper1() throws Exception{
        // LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<User> wrapper = Wrappers.<User>lambdaQuery();

        //        wrapper.like("user_name", "%伤%")
        //                .eq("password","123456")
        //                .ge("age", 28)
        //                .between("age",29 , 39);  // 包含边界值
        wrapper.like(User::getUserName, "伤")
                .eq(User::getPassword, "123456")
                .in(User::getAge, Arrays.asList(19,25,29))
                .orderByDesc(User::getAge)
                .select(User::getId, User::getUserName);
        List<User> users = userMapper.selectList(wrapper);
        System.out.println(users);
    }

image.png

4.LambdaQueryWrapper删除和更新操作

image.png

4.1 条件删除

/**
     * 条件删除
     * @throws Exception
*/
@Test
public void testWrapper5() throws Exception{

  LambdaQueryWrapper<User> wrapper = Wrappers.<User>lambdaQuery().eq(User::getUserName, "武大郎");
  int i = userMapper.delete(wrapper);
  System.out.println(i);
}

4.2条件更新

image.png

/**
     * 条件更新
     * @throws Exception
*/
@Test
public void testWrapper6() throws Exception{

  /**
     * UPDATE tb_user SET t_name=? WHERE (id = ?)
     */
  // 参数1: 最新的值
  User user = new User();
  user.setUserName("张三丰");

  // 参数2:更新时条件
  LambdaQueryWrapper<User> wrapper = Wrappers.<User>lambdaQuery();
  wrapper.eq(User::getId, 15);

  int update = userMapper.update(user, wrapper);
  System.out.println(update);
}


/**
     * 条件更新
     * @throws Exception
     */
@Test
public void testWrapper7() throws Exception{
  /**
         * UPDATE tb_user SET t_name=?, user_name=? WHERE (id = ?)
         */
  // 参数1: 最新的值
  // 参数2:更新时条件
  LambdaUpdateWrapper<User> wrapper = Wrappers.<User>lambdaUpdate();
  wrapper.eq(User::getId, 15)
    .set(User::getUserName, "张三丰666")
    .set(User::getName,"zsf666");

  int update = userMapper.update(null, wrapper);
  System.out.println(update);
}

image.png

5.自定义查询接口实现分页查询

目前我们使用MP自带的分页插件可以很友好的实现分页查询操作,但是如果一些查询需要我们自定义SQL,那该如何实现分页查询操作呢?

5.1 自定义接口中直接传入Page分页对象即可

public interface UserMapper extends BaseMapper<User> {
    /**
     * 查询大于指定id的用户信息,并分页查询实现
     * @param page
     * @param id
     * @return
     */
    IPage<User> findGtIdByPage(IPage<User> page, @Param("id") Long id);
}

5.2定义xml映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pkq.demo.mapper.UserMapper">

	<select id="findGtIdByPage" resultType="com.pkq.demo.pojo.User">
		 select id,user_name,password,name,age,email from tb_user where id > #{id}
	</select>
</mapper>

5.3测试

 @Test
    public void test2(){
        IPage<User> userIPage=new Page<>();
        userMapper.findGtIdByPage(userIPage,5L);
        //获取分页参数
        long pages = userIPage.getPages();//总页数
        long current = userIPage.getCurrent();//当前页
        List<User> records = userIPage.getRecords();//当前页的数据
        long total = userIPage.getTotal();//总记录数
        long size = userIPage.getSize();//每页显示条数
        System.out.println("总页数:"+pages);
        System.out.println("当前页:"+current);
        System.out.println("总记录数:"+total);
        System.out.println("每页显示条数:"+size);

        System.out.println("当前页的数据:");
        for (User user:records){
            System.out.println(user);
        }
    }
==>  Preparing: SELECT COUNT(*) AS total FROM tb_user WHERE id > ?
==> Parameters: 5(Long)
<==    Columns: total
<==        Row: 11
<==      Total: 1
==>  Preparing: select id,user_name,password,name,age,email from tb_user where id > ? LIMIT ?
==> Parameters: 5(Long), 10(Long)
<==    Columns: id, user_name, password, name, age, email
<==        Row: 6, 吴六破, 123456, wlp, 21, wlp@itcast.cn
<==        Row: 7, 郑七灭, 123456, zqm, 22, zqm@itcast.cn
<==        Row: 8, 王八衰, 123456, wbs, 22, wbs@itcast.cn
<==        Row: 9, 张无忌, 123456, zwj, 25, zwj@itcast.cn
<==        Row: 10, 赵敏, 123456, zm, 26, zm@itcast.cn
<==        Row: 11, 赵二伤, 123456, zes, 25, zes@itcast.cn
<==        Row: 12, 赵三伤, 123456, zss1, 28, zss1@itcast.cn
<==        Row: 13, 赵四伤, 123456, zss2, 29, zss2@itcast.cn
<==        Row: 14, 赵五伤, 123456, zws, 39, zws@itcast.cn
<==        Row: 15, 张三丰666, 123456, zsf666, 29, zls@itcast.cn
<==      Total: 10
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1db87583]
总页数:2
当前页:1
总记录数:11
每页显示条数:10
当前页的数据:
User(id=6, userName=吴六破, password=123456, name=wlp, age=21, email=wlp@itcast.cn)
User(id=7, userName=郑七灭, password=123456, name=zqm, age=22, email=zqm@itcast.cn)
User(id=8, userName=王八衰, password=123456, name=wbs, age=22, email=wbs@itcast.cn)
User(id=9, userName=张无忌, password=123456, name=zwj, age=25, email=zwj@itcast.cn)
User(id=10, userName=赵敏, password=123456, name=zm, age=26, email=zm@itcast.cn)
User(id=11, userName=赵二伤, password=123456, name=zes, age=25, email=zes@itcast.cn)
User(id=12, userName=赵三伤, password=123456, name=zss1, age=28, email=zss1@itcast.cn)
User(id=13, userName=赵四伤, password=123456, name=zss2, age=29, email=zss2@itcast.cn)
User(id=14, userName=赵五伤, password=123456, name=zws, age=39, email=zws@itcast.cn)
User(id=15, userName=张三丰666, password=123456, name=zsf666, age=29, email=zls@itcast.cn)

五、MybatisPlus分页插件

创建配置类MyBatisPlusConfig

package com.pkq.demo.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;

/**
 * ClassName: MybatisPlusConfig
 * Package: com.pkq.demo.config
 * Description:
 *
 * @Author pkq
 * @Create 2024/3/28 10:11
 * @Version 1.0
 */
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量,-1不受限制
        paginationInterceptor.setMaxLimit(-1L);
        //如果配置多个插件,切记分页最后添加
        interceptor.addInnerInterceptor(paginationInterceptor);
        return interceptor;
    }
}

测试

@Test
    public void testPage(){
        int currentPage=2;//当前第几页
        int currentPageSize=4;//每页有多少条
        //创建分页对象
        IPage<User> pageInfo=new Page<>(currentPage,currentPageSize);
        //分页查询 pageInfo==userIPage -->true
        IPage<User> userIPage = userMapper.selectPage(pageInfo,null);
        //获取分页参数
        long pages = userIPage.getPages();//总页数
        long current = userIPage.getCurrent();//当前页
        List<User> records = userIPage.getRecords();//当前页的数据
        long total = userIPage.getTotal();//总记录数
        long size = userIPage.getSize();//每页显示条数
        System.out.println("总页数:"+pages);
        System.out.println("当前页:"+current);
        System.out.println("总记录数:"+total);
        System.out.println("每页显示条数:"+size);

        System.out.println("当前页的数据:");
        for (User user:records){
            System.out.println(user);
        }
    }

image.png
image.png

六、 MP代码生成器

6.1 开发现状

开发中当有一个新的业务要实现时,通常我们需要构建一下信息:

  • 定义PO类数据库表和实体类的映射 Java Bean,打各种mp的注解。
  • 定义DAO层需要编写接口 Mapper ,接口 Mapper 需要去继承 MP 中的 BaseMapper 接口。
  • 定义Service层编写 Service 层接口和实现类。业务接口需要去继承 MP 中的 IService,业务实现类需要继承 MP 中的 ServiceImpl 和 实现业务接口。
  • 定义Controller层编写 Controller 并标注 Spring MVC 中的相关注解。 显然上述存在固定的流程,且存在大量重复操作,you now 代码价值低且没效率!

6.2 MP逆向工程介绍

针对目前开发的现状,MP的代码生成器就可以一展身手了;
通过MP代码生成器可以生成模板性的代码,减少手工操作的繁琐,使开发人员聚焦于业务开发之上,提升开发效率;
AutoGenerator 类是MyBatis-Plus 的核心代码生成器类,通过 AutoGenerator 可以快速生成 Mapper接口、Entity实体类、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

6.3 代码生成

参考代码:资料\mp_generator

或者是gitee开源链接:https://gitee.com/jitheima/mp_generator.git

完整代码:
image.png
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

说明:以后在项目中使用时,先在本工程生成,然后就可以把代码拷贝到对应的项目目录中使用了;

6.4代码生成说明

代码生成说明

当有一个新的业务实现时,对接口的功能实现上,我们通常来说需要构建下面的信息:

  • PO类数据库表和实体类的映射 Java Bean。
  • DAO层需要编写接口 Mapper ,接口 Mapper 需要去继承 MP 中的 BaseMapper 接口。
  • Service层编写 Service 层接口和实现类。业务接口需要去继承 MP 中的 IService,业务实现类需要继承 MP 中的 ServiceImpl 和 实现业务接口。
  • Controller层编写 Controller 并标注 Spring MVC 中的相关注解。

从上面的各类代码中可以放下,代码都是模板性的,如果用手工copy、修改的方式来实现,太烦人也没效率,而这时就是代码生成器小展身手的时候,使用代码生成器生成模板性的代码,减少手工操作的繁琐,集中精力在业务开发上,提升开发效率。
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Mapper接口、Entity实体类、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

具体使用步骤

  1. 打开 CodeGenerator 类
  2. 修改对应的参数即可相关参数介绍:
DB_TYPE: 数据库类型, 默认是 MySQL
DB_NAME: 数据库名,用户修改为自己的数据库
HOST_NAME: 数据库IP, 默认 localhost
JDBC_USERNAME: 数据库用户名, 默认:root
JDBC_PASSWORD: 数据库密码,默认:root
TABLES: 需要生成代码的表, 数组
PACKAGE_PARENT_NAME: 代码生成的包结构
IS_DTO: 是否生成DTO, 默认:false
AUTHOR: 作者名称, 默认:itheima
  1. 运行main方法执行生成代码
  2. 拷贝到自己的项目中即可

以后在项目中使用,在这里生成后,可以把代码拷贝到对应的目录里使用,在整个黑马头条项目开发阶段,使用了当前生成的mapper和实体类。

七、MybatisX插件[扩展]

7.1 MybatisX插件介绍

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

安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。

功能:

  • Java 与 XML 调回跳转
  • Mapper 方法自动生成 XML

mybatisx-001.gif

7.2 基于MybatisX实现逆向工程

image.png

image.png
image.png

八.乐观锁[扩展]

当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:

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

乐观锁就是当前操作者认为在自己操作资源的过程中,其他人操作相同资源的可能性极低,所以无需加锁,而是通过设置一个版本号来加以约束;
悲观锁:排它锁,比如synchronized关键字就是悲观锁,当前线程做操作时,不允许其它线程做操作;
乐观锁:当前线程做操作时,允许其它线程做操作,但是如果其它线程做了操作,则当前操作失败
乐观锁在数据库中有什么优势?
避免长事务场景锁定数据资源,导致其它线程操作该资源时阻塞,如果阻塞过多,那么导致数据库连接资源耗尽,进而数据库宕机了;(事务阻塞,不会释放连接资源)
本质上就是在操作前,先获取操作行的version版本号,然后再做前天操作,然后最后再更新这一行,更新时,给sql条件一个判断版本号的sql片段: select version,xxx from user where id=100; version=30 --> 做其他操作(20s);—> update user set xxx,version=version+1 where xxxx and version=30;
使用场景:
1.业务操作周期长,如果业务整个加入事务,导致数据库资源锁定周期过长,性能降低;
2.如果资源争抢过于激烈,会导致失败重试次数过多,导致性能降低;
示例:
image.png

@Data
@NoArgsConstructor//主要用于mybatis底层反射构建user实体类对象
@AllArgsConstructor//主要是lombok基于构建者模式构建对象
@Builder
/**
 * 如果变的名称与实体类名称一致,该注解可省略
 */
@TableName("tb_user")//指定表名
public class User {
    private Integer id;
    private  String userName;
    private String password;
    private String name;
    private Integer age;
    private String email;
    @TableLogic
    private Integer isDeleted;
    @Version
    private Integer version;

}

配置乐观锁拦截器:

    /**
     * 注册插件
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        //构建mp的插件注册器bean,通过该bean可批量注册多个插件
        MybatisPlusInterceptor plusInterceptor = new MybatisPlusInterceptor();
        //配置乐观锁拦截器
        OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor = new OptimisticLockerInnerInterceptor();
        //注册
        plusInterceptor.addInnerInterceptor(optimisticLockerInnerInterceptor);
        return plusInterceptor;
    }

测试:

 /**
     * @Description 测试乐观锁
     */
    @Test
    public void testOp(){
        User user = userMapper.selectById(5);
        System.out.println(user);
        user.setName("zws777");
        userMapper.updateById(user);
    }

使用mp的乐观锁,需要先自己根据主键id查询用户信息,信息中包含了此时的version数据,然后再更新,更新时会将查询的version值作为更新条件取更新;
效果:
image.png
image.png

九、MP字段自动填充

1.MP字段自动填充

1.1 背景说明

说明:
image.png
实际开发中有些字段存在大量的重复操作,比如create_time update_time等,需要经常在实体类中设置new Date(),比较繁琐,可以借助MP的自动填充功能

1.2 定义实体类

@TableName("tb_user")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
	//指定插入时自动填充的字段
    @TableField(value = "create_time",fill = FieldFill.INSERT)
    private Date createTime;
  	//自定插入或者更新时自动填充的字段
    @TableField(value = "update_time",fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
}

1.3 配置填充规则

package com.itheima.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;


@Component
public class FillDataHandler implements MetaObjectHandler {

    /**
     * 定义自动填充的方法
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        //设置insert操作时的时间点
        metaObject.setValue("createTime",new Date());
        //设置update操作时的时间点
        metaObject.setValue("updateTime",new Date());
    }

    /**
     * 定义更新时填充的方法
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        //设置update操作时的时间点
        metaObject.setValue("updateTime",new Date());
    }
}

1.4 测试

更新测试:

    /**
     * @Description 根据主键id更新用户信息(开发最常用)
     */
    @Test
    public void test04(){
        long id=12l;
        //把主键信息和要更新的信息封装到实体类中,设置什么值,就更新什么值,为null的属性,不做处理
        User user = User.builder().id(id).userName("张三丰2").age(120).build();
        //UPDATE tb_user SET real_name=?, age=? WHERE id=?
        //如何获取real_name字段的? user--->User.class--->通过反射获取userName Field字段对象---》
        // 获取字段上的注解对象----》value值就可获取--》real_name
        int count = userMapper.updateById(user);
        System.out.println(count);
    }

插入测试:

    /**
     * @Description 测试插入
     */
    @Test
    public void test02(){
        User user =
                User.builder()
                        .userName("itheima4")
                        .name("itcast4")
                        .age(14)
                        .email("itcast@itcast4.cn")
                        .password("44444")
                        .build();
        //插入数据
        int count = userMapper.insert(user);
        System.out.println("受影响行数:"+count);
    }

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

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

相关文章

3月23日笔记

广播域与泛洪范围是相同的 广播&#xff1a;在同一个泛洪范围内&#xff0c;强迫交换机泛洪&#xff08;主动&#xff09; 泛洪&#xff08;被动&#xff09; ARP的工作原理&#xff1a;ARP先通过广播发送请求包&#xff0c;所有收到该广播包的设备都会将其中的源IP和源MAC相…

《Vision mamba》论文笔记

原文出处&#xff1a; [2401.09417] Vision Mamba: Efficient Visual Representation Learning with Bidirectional State Space Model (arxiv.org) 原文笔记&#xff1a; What&#xff1a; Vision Mamba: Efficient Visual Representation Learning with Bidirectional St…

【Python】#2 基本数据类型

文章目录 一、数字类型1. 整数类型2. 浮点数类型tips&#xff1a;为什么浮点数计算的小数部分经常“错误”&#xff1f;如 为什么0.10.20.3在计算机中不为真 3. 复数形式<classcomplex>4. 数字类型的操作符与部分函数tips: 数字类型的类型提升tips:Python中除法 基本数据…

电商控价的效果有哪些

品牌在做价格治理时&#xff0c;肯定是不再希望线上平台出现低价、窜货链接&#xff0c;但现实却难如品牌所愿&#xff0c;有几个难以实现的原因&#xff0c;首先&#xff0c;电商平台链接上架下架是很容易的&#xff0c;此刻将链接治理下架&#xff0c;下一刻店铺可能又会再上…

《QT实用小工具·二》图片文字转base64编码

1、概述 源码放在文章末尾 base64编码转换类 图片转base64字符串。base64字符串转图片。字符转base64字符串。base64字符串转字符。后期增加数据压缩。Qt6对base64编码转换进行了重写效率提升至少200%。 下面是demo演示&#xff1a; 项目部分代码如下所示&#xff1a; #ifn…

python pytz是什么

pytz模块常用于时区的转换&#xff0c;常常配合datetime一起使用。我们知道datetime除了data方法生成的时间是没有时区概念&#xff0c;其他如time、datetime等都是有时区概念&#xff0c;即指定了tzinfo信息。 >>> import datetime >>> datetime.datetime.n…

【机器学习】深入探讨基于实例的学习及K-最近邻算法

深入探讨基于实例的学习及K-最近邻算法 在机器学习的众多策略中&#xff0c;基于实例的学习方法因其简单性和高效性而备受关注。这种方法的核心理念在于利用已知的数据实例来预测新数据的标签或属性。本文将深入探讨其中的两个重要概念&#xff1a;最近邻算法和K-最近邻算法&a…

浏览器工作原理与实践--块级作用域:var缺陷以及为什么要引入let和const

在前面《07 | 变量提升&#xff1a;JavaScript代码是按顺序执行的吗&#xff1f;》这篇文章中&#xff0c;我们已经讲解了JavaScript中变量提升的相关内容&#xff0c;正是由于JavaScript存在变量提升这种特性&#xff0c;从而导致了很多与直觉不符的代码&#xff0c;这也是Jav…

考研数学|高效刷透汤家凤《1800》经验分享

当然不需要换老师&#xff0c;如果你在基础阶段连汤老师的课都听不进去&#xff0c;那么换其他老师的话&#xff0c;很大可能也是白搭。 如果你现在对于1800还是一筹莫展的话&#xff0c;那么很明显&#xff0c;这反映出前期基础不扎实&#xff0c;没有真正理解和掌握这部分内…

【NOI】树的初步认识

文章目录 前言一、树1.什么是树&#xff1f;2.树的基本概念3.树的基本术语3.1 节点3.1.1 根节点3.1.2 父节点、子节点3.1.3 兄弟节点、堂兄弟节点3.1.4 祖先节点、子孙节点3.1.5 叶子节点/终端节点3.1.6 分支节点/非终端节点 3.2 边3.3 度3.3.1 树的度 3.4 层次3.4.1 树的深度3…

学习JavaEE的日子 Day32 线程池

Day32 线程池 1.引入 一个线程完成一项任务所需时间为&#xff1a; 创建线程时间 - Time1线程中执行任务的时间 - Time2销毁线程时间 - Time3 2.为什么需要线程池(重要) 线程池技术正是关注如何缩短或调整Time1和Time3的时间&#xff0c;从而提高程序的性能。项目中可以把Time…

磐启微PAN1020低功耗SOC芯片

PAN1020低功耗蓝牙芯片 典型应用 ⚫ 电视和机顶盒遥控器 ⚫ 无线游戏手柄 ⚫ 无线鼠键 ⚫ 智能家居 需要此物料&#xff0c;可联系周小姐 主要特性 ⚫ RF - 2.4GHz 射频收发机&#xff08;兼容 BLE4.2&#xff09; - 接收灵敏度&#xff1a;-90 dBm1Mbps - 接收信号&a…

智慧公厕解决方案打造更加智能的卫生空间

一、智慧公厕方案概述 智慧公厕方案旨在解决现有公厕存在的诸多问题&#xff0c;包括民众用厕困难、环境卫生状况不佳、管理效率低下等方面。针对民众的需求和管理方面的挑战&#xff0c;智慧公厕提供了一套综合解决方案&#xff0c;包括智能导航、环境监测、资源管理等功能&a…

jvm(虚拟机)运行时数据区域介绍

Java虚拟机&#xff08;JVM&#xff09;运行时数据区域是Java程序在运行过程中使用的内存区域&#xff0c;它主要包括以下几个部分&#xff1a; 程序计数器&#xff08;Program Counter Register&#xff09;&#xff1a; 程序计数器是一块较小的内存区域&#xff0c;是线程私有…

25G SFP28 AOC线缆最新数据传输解决方案

在当今云计算、大数据、人工智能等领域&#xff0c;对高速数据传输的需求不断增加。传统的1G和10G网络已经无法满足数据中心日益增长的流量&#xff0c;因此迫切需要更高速的解决方案。25G SFP28 AOC有源光缆迎合了这一需求&#xff0c;成为连接数据中心、服务器、存储等25G设备…

RPA使用Native Messaging协议实现浏览器自动化

RPA 即机器人流程自动化&#xff0c;是一种利用软件机器人或人工智能来自动化业务流程中规则性、重复性任务的技术。RPA 技术可以模拟和执行人类在计算机上的交互操作&#xff0c;从而实现自动化处理数据、处理交易、触发通知等任务。帮助企业或个人实现业务流程的自动化和优化…

速通数据结构第二站 顺序表

文章目录 速通数据结构与算法系列 1 速通数据结构与算法第一站 复杂度 http://t.csdnimg.cn/sxEGF 感谢佬们支持&#xff01; 目录 系列文章目录 前言一、顺序表 0 结构体 1 接口声明 2 初始化和销毁 3 扩容函数 4 打印和判空 5 尾插 …

B2902A是德科技B2902A精密型电源

181/2461/8938产品概述&#xff1a; Agilent B2902A 精密源/测量单元 (SMU) 是一款 2 通道、紧凑且经济高效的台式 SMU&#xff0c;能够源和测量电压和电流。它用途广泛&#xff0c;可以轻松、高精度地执行 I/V&#xff08;电流与电压&#xff09;测量。4 象限源和测量功能的集…

RabbitMQ安装及使用笔记

RabbitMQ安装及使用笔记 RabbitMQ是一个开源的消息代理软件&#xff0c;它实现了高级消息队列协议&#xff08;AMQP&#xff09;&#xff0c;用于在分布式系统中进行消息传递。 1.安装 利用docker load命令加载mq镜像 docker load -i mq.tar 基于Docker来安装RabbitMQ&#xff…

【QT入门】 QListWidget各种常见用法详解之列表模式

往期回顾 【QT入门】 Qt代码创建布局之setLayout使用-CSDN博客 【QT入门】 Qt代码创建布局之多重布局变换与布局删除技巧-CSDN博客 【QT入门】 QTabWidget各种常见用法详解-CSDN博客 【QT入门】 QListWidget各种常见用法详解之列表模式 QListWidget有列表和图标两种显示模式&a…