MybatisPlus学习总结

news2024/11/16 11:53:55

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

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

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

相关文章

SQL,group by分组后分别计算组内不同值的数量

SQL&#xff0c;group by分组后分别计算组内不同值的数量 如现有一张购物表shopping 先要求小明和小红分别买了多少笔和多少橡皮&#xff0c;形成以下格式 SELECT name,COUNT(*) FROM shopping GROUP BY name;SELECT name AS 姓名,SUM( CASE WHEN cargo 笔 THEN 1 ELSE 0 END)…

PyPy 通过采用即时编译技术,能够显著提升 Python 代码的执行效率。

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 提升 Python 代码性能至接近 C 语言速度&#xff0c;无需修改源代码。遵循 Python 之父吉多・范罗苏姆的建议&#xff1a;“如果你想让你的代码神奇地运行得更快&#xff0c;你应该试试用 PyPy。” Yo…

二维前缀和与二维差分的表示

前缀和&#xff1a; 上述图片是求范围内的总和的图和公式 上述图片是初始化前缀和数组的图和公式 差分&#xff1a; 上图是差分公式 #include<iostream> #include<climits> #include<algorithm> #include<cstring> #include<cstdio> #include&l…

基于视图能力的县域治理视频基座数字化、智慧化解决方案

一、方案背景 县域治理方案是我国地方治理体系的重要组成部分&#xff0c;对于促进县域经济社会发展、维护社会稳定、推进全面深化改革具有重要意义。随着科技的不断进步&#xff0c;视频监管已经成为了现代社会治理的重要手段之一。县域治理视频监管方案是通过视频监控、数据…

SpringBoot国际化配置流程(超详细)

前言 最新第一次在做SpringBoot的国际化&#xff0c;网上搜了很多相关的资料&#xff0c;都是一些简单的使用例子&#xff0c;达不到在实际项目中使用的要求&#xff0c;因此本次将结合查询的资料以及自己的实践整理出SpringBoot配置国际化的流程。 SpringBoot国际化 "i…

关于svn安装报错2503问题的解决方法

问题&#xff1a; SVN在安装时&#xff0c;一直报错&#xff0c;安装失败 The installer has encountered an unexpected error installing this package.The error code is 2503 权限问题&#xff0c;右键以管理员权限运行。如果你也是像我一样&#xff0c;右键没有以管理员…

springboot 使用@profiles.active@多配置文件切换

项目配置文件结构&#xff1a; 主配置文件内容&#xff1a; pom配置文件&#xff1a; <profiles><profile><id>dev</id><properties><profiles.active>dev</profiles.active></properties></profile><profile>…

43 带 fixed 列的 el-table 不兼容于 sortablejs

前言 这是一个基于 sortablejs 来实现的 el-table 的拖拽功能的基础实现 然后 这个过程中遇到的一个比较特殊的问题是, 关于 el-table-column 的 fixed 的属性, 对于 sortablejs 这边来定位目标选择列 影响的一个问题 在基础的用例中, 使用 “.el-table__body-wrapper tbo…

数组的常见算法

数组的常见算法 数值型数组特征值统计 这里的特征值涉及到&#xff1a;平均值、最大值、最小值、总和等 举例1&#xff1a;数组统计&#xff1a;求总和、均值 public class TestArrayElementSum {public static void main(String[] args) {int[] arr {4,5,6,1,9};//求总和、…

污水处理迈入3D可视化新时代:智慧环保触手可及

在科技日新月异的今天&#xff0c;环保事业也迎来了前所未有的发展机遇。污水处理作为环保领域的重要组成部分&#xff0c;其技术的革新与进步&#xff0c;对于保护水资源、维护生态平衡具有重要意义。 传统的污水处理机组往往存在着操作复杂、监控困难等问题&#xff0c;使得污…

2024年【熔化焊接与热切割】报名考试及熔化焊接与热切割模拟试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 熔化焊接与热切割报名考试考前必练&#xff01;安全生产模拟考试一点通每个月更新熔化焊接与热切割模拟试题题目及答案&#xff01;多做几遍&#xff0c;其实通过熔化焊接与热切割作业考试题库很简单。 1、【单选题】…

ORA-04031 错误分析及处理方法

一、问题描述 使用普通用户登录数据库报ORA-04031错误 $ sqlplus / as sysdbaSQL*Plus: Release 11.2.0.1.0 Production on Mon Mar 25 09:14:59 2024Copyright (c) 1982, 2009, Oracle. All rights reserved.Connected to: Oracle Database 11g Enterprise Edition Releas…

学点儿Java_Day12_IO流

1 IO介绍以及分类 IO: Input Output 流是一组有顺序的&#xff0c;有起点和终点的字节集合&#xff0c;是对数据传输的总称或抽象。即数据在两设备间的传输称为流&#xff0c;流的本质是数据传输&#xff0c;根据数据传输特性将流抽象为各种类&#xff0c;方便更直观的进行数据…

详细分析Linux中的core dump异常(附 Demo排查)

目录 1. 基本知识2. 进阶知识3. Demo4. 彩蛋 1. 基本知识 Core dump 是指在程序异常终止时&#xff0c;操作系统将程序的内存映像保存到磁盘上的一种机制。 在 Linux 系统中&#xff0c;core dump 提供了一种调试程序错误的重要方式&#xff0c;它记录了程序在崩溃时的内存状态…

文献学习(自备)

收官大作&#xff0c;多组学融合的新套路发NC&#xff01;&#xff01; - 知乎 (zhihu.com) Hofbauer cell function in the term placenta associates with adult cardiovascular and depressive outcomes | Nature Communications 病理性胎盘炎症会增加几种成人疾病的风险&a…

CAD自动轻量化,工业仿真动画快速制作

随着现代工业的蓬勃发展&#xff0c;制造业企业在产品宣传展示、工作流程讲解、机械维修维护等方面对展示形式提出了更高的要求。工业动画以其直观、生动的特点&#xff0c;能够深入剖析产品的结构、工作原理和操作流程&#xff0c;为企业带来了全新的宣传展示方式。 但是由于…

Obsidian插件-高亮块(Admonition)

在插件市场里面搜索Admonition并安装插件&#xff0c;就可以使用高亮块了。 添加高亮块 用法稍微有一些不同。按照下面的格式&#xff0c;输入Markdown就可以创建一个高亮块。 内容内容内容输入*ad-*会出现相应的类型可以选择

【QGIS从shp文件中筛选目标区域导出为shp】

文章目录 1、写在前面2、QGIS将shp文件中目标区域输出为shp2.1、手动点选2.2、高级过滤 3、上述shp完成后&#xff0c;配合python的shp文件&#xff0c;即可凸显研究区域了 1、写在前面 利用shp文件制作研究区域mask&#xff0c;Matlab版本&#xff0c;请点击 Matlab利用shp文…

超分之SwinIR官方代码解读

文章目录 一、解读SwinIR模型文件&#xff1a;network_swinir.py1. 带有相对为位置偏置的(W-MSA)2. STL(Swin Transformer)3. RSTB&#xff08;Residual Swin Transformer Block&#xff09;4. SwinIR&#xff08;主框架网络&#xff09; 二、解读SwinIR测试主文件&#xff1a;…

企业员工培训考试系统开发方案

一、引言 在当今知识经济时代&#xff0c;企业对员工的综合素质和专业技能有着越来越高的要求。为了适应这一趋势&#xff0c;构建一个全面而高效的企业员工培训考试系统变得尤为重要。该系统旨在通过提供多样化的培训课程和全面的考核机制&#xff0c;促进员工持续学习和能力…