2、Mybatis-Plus

Mybatis-Plus 官网

1、使用

1.1、导入依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.7</version>
    <relativePath/>
</parent>

<dependencies>
    <!--mybatis-plus 启动器-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.3.4</version>
    </dependency>
    <!--lombok 插件-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
    <!--mysql 驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

1.2、配置

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/mp?serverTimezone=UTC
    username: root
    password: root

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml       # Mapper 所对应的 XML 文件位置
  type-aliases-package: com.zzw.pojo             # MyBaits 别名包扫描路径
  type-enums-package: com.zzw.enums              # 配置扫描通用枚举
  config-location: classpath:mybatis-config.xml  # MyBatis 配置文件位置
  configuration:
    # 此属性在 MyBatis 中原默认值为 false, 在 MyBatis-Plus 中默认为 true, 该参数不能和 mybatis-plus.config-location 同时存在
    map-underscore-to-camel-case: false          # 关闭自动驼峰映射
    cache-enabled: false                         # 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存, 默认为 true(二级缓存)
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # sql 日志
  global-config:
    db-config:
      id-type: auto                              # 全局默认主键类型, 设置后, 即可省略实体对象中的 @TableId(type = IdType.AUTO) 配置
      table-prefix: tb_                          # 表名前缀, 全局配置后可省略 @TableName() 配置

2、注解

2.1、@TableName

表名注解

属性 类型 必须指定 默认值 描述
value String "" 表名

2.2、@TableId

主键注解

属性 类型 必须指定 默认值 描述
value String "" 主键字段名
type Enum IdType.NONE 主键类型
* 主键生成策略
* Mybatis 支持的数据增长策略
  - `AUTO(0)`         数据库 ID 自增, 使用数据库的自增策略, 注意: 该类型请确保数据库设置了 id 自增, 否则无效
  - `NONE(1)`         未设置主键类型
  - `INPUT(2)`        用户输入 ID
  - `ASSIGN_ID(3)`    雪花算法生成 ID,唯一 Long 数据
  - `ASSIGN_UUID(4)`  UUID 算法生成 ID,唯一字符串

2.3、@TableField

字段注解(非主键)

属性 类型 必须指定 默认值 描述
value String "" 数据库字段名
exist boolean true 是否为数据库表字段
select boolean true 是否进行 select 查询

2.4、@TableLogic

表字段逻辑处理注解(逻辑删除),使用场景:可以进行数据恢复

属性 类型 必须指定 默认值 描述
value String "" 逻辑未删除值
delval String "" 逻辑删除值

2.5、@DS

切换数据源,@DS 可以注解在方法上或类上,同时存在就近原则,方法上注解优先于类上注解

注解 结果
没有 @DS 默认数据源
@DS("dsName") dsName 可以为组名也可以为具体某个库的名称

2.6、示例

Pojo 继承 Model<Pojo>
PojoMapper 继承 BaseMapper<Pojo>
PojoService 继承 IService<Pojo>
PojoServiceImpl 继承 ServiceImpl<PojoMapper, Pojo>

@TableName("tb_user"):指定对应数据库的表名
@TableId(type = IdType.AUTO):这个字段为主键,且自增回填
@TableField(value = "user_name"):这个字段在数据库中的名称
@TableField("exist = false"):该字段在数据库表中不存在
@TableField("select = false"):大字段或敏感字段可设置为 false,不加入 select 查询范围

// 枚举类: 主键自增策略
public enum IdType {
    AUTO(0),
    NONE(1),
    INPUT(2),
    ASSIGN_ID(3),
    ASSIGN_UUID(4);
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User {

    @TableId(type = IdType.AUTO)
    private Integer id;

    @TableField(value = "user_name")
    private String username;

    @TableField(select = false)
    private String password;

    private String name;
    private Integer age;
    private String email;

    @Version
    private Integer version;
}
@Mapper
public interface UserMapper extends BaseMapper<User> {
}

3、Mapper 方法

PojoMapper 接口直接调用

3.1、插入操作

@Test
void testInsert() {
    User user = new User(null, "lili", "123456", "丽丽", 17, "test6@qq.com");
    int count = userMapper.insert(user);
    System.out.println("影响行数: " + count);
    System.out.println("id: " + user.getId()); // 主键自增回填, 会默认基于雪花算法的策略生成 id
}

3.2、更新操作

1、根据 Id 最新

@Test
void testUpdateById() {
    User user = new User();
    user.setId(1);             // 更新的条件
    user.setAge(21);           // 更新的字段
    user.setPassword("666");   // 更新的字段
    int count = userMapper.updateById(user);
    System.out.println("影响行数: " + count);
}

2、根据条件更新

// QueryWrapper<User>
@Test
void  testUpdate1() {
    User user = new User();
    user.setAge(17);           // 更新的字段
    user.setPassword("777");   // 更新的字段

    QueryWrapper<User> wrapper = new QueryWrapper<>(); // 更新的条件
    wrapper.eq("user_name", "zhangsan");               // column 是数据库的字段名, 不是 Pojo 属性名

    int count = userMapper.update(user, wrapper); // 主键回填
    System.out.println("影响行数: " + count);
}
// UpdateWrapper<User>
@Test
void  testUpdate2() {
    UpdateWrapper<User> wrapper = new UpdateWrapper<>();
    wrapper
        .set("age", 27)                      // 更新的字段
        .set("password", "999")              // 更新的字段
        .eq("user_name", "zhangsan");        // 更新的条件

    int count = userMapper.update(null, wrapper);
    System.out.println("影响行数: " + count);
}

3.3、删除操作

1、根据 Id 删除

@Test
void deleteById1() {
    User user = new User();
    user.setId(11);   // 删除的条件
    int count = userMapper.deleteById(user);
    System.out.println("影响行数: " + count);
}
@Test
void deleteById2() {
    int count = userMapper.deleteById(11);
    System.out.println("影响行数: " + count);
}

2、根据 Id 批量删除

@Test
void deleteBatchIds() {
    int count = userMapper.deleteBatchIds(Arrays.asList(11, 12, 13));
    System.out.println("影响行数: " + count);
}

3、根据条件删除

// Map<String, Object>
@Test
void deleteByMap() {
    Map<String, Object> map = new HashMap<>();
    map.put("user_name", "lili");   // 删除的条件
    map.put("age", 17);             // 删除的条件
    int count = userMapper.deleteByMap(map);
    System.out.println("影响行数: " + count);
}
// QueryWrapper<User>
@Test
void delete1() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("name", "丽丽");           // 删除的条件
    wrapper.eq("email", "test6@qq.com"); // 删除的条件
    int count = userMapper.delete(wrapper);
    System.out.println("影响行数: " + count);
}
// User
@Test
void delete2() {
    User user = new User();
    user.setName("丽丽");            // 删除的条件
    user.setEmail("test6@qq.com");  // 删除的条件
    QueryWrapper<User> wrapper = new QueryWrapper<>(user);
    int count = userMapper.delete(wrapper);
    System.out.println("影响行数: " + count);
}

3.4、查询操作

1、根据 Id 查询

@Test
void selectById() {
    User user = userMapper.selectById(11);
    System.out.println(user);
}

2、根据 Id 批量查询

@Test
void selectBatchIds() {
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
    users.forEach(System.out::println);
}

3、通过 Map 条件查询全部记录

@Test
public void testSelectByMap() {
    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);
}

4、根据 Wrapper 条件,查询一条记录

@Test
void selectOne() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("name", "张三").eq("password", "999"); // 查询条件
    User user = userMapper.selectOne(wrapper); // 根据条件查询一条数据, 如果结果超过一条会报错
    System.out.println(user);
}

5、根据 Wrapper 条件,查询总记录数

@Test
void selectCount() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.gt("age", "15"); // 查询条件
    Long count = userMapper.selectCount(wrapper); // 根据条件查询数据条数
    System.out.println("查询结果记录数: " + count);
}

6、根据 Wrapper 条件,查询全部记录

@Test
void selectList() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.gt("age", "15"); // 查询条件
    List<User> users = userMapper.selectList(wrapper); // 根据条件查询全部记录
    users.forEach(System.out::println);
}

3.5、分页查询

1、根据 Wrapper 条件

@Configuration
public class MybatisPlusConfig {

    // 配置分页插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}
@Test
void selectPage() {
    Page<User> page = new Page<>(1, 2); // 当前页是 1, 页大小是 2
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.gt("age", "15");   // 查询条件

    IPage<User> iPage = userMapper.selectPage(page, wrapper); // 根据条件查询全部记录并分页(这里的 ipage == page)

    System.out.println("数据总条数: " + iPage.getTotal());
    System.out.println("数据总页数: " + iPage.getPages());
    System.out.println("当前页码数: " + iPage.getCurrent());
    System.out.println("页大小: " + iPage.getSize());
    System.out.println("是否有上一页: " + page.hasPrevious()); // 直接用 page 获取, page == ipage
    System.out.println("是否有下一页: " + page.hasNext());     // 直接用 page 获取, page == ipage

    List<User> records = iPage.getRecords();
    records.forEach(System.out::println);
}

2、根据 xml 自定义分页

// UserMapper 中定义接口方法

/**
 * 根据年龄查询用户列表, 分页显示
 * @param page 分页对象, xml 中可以从里面进行取值, 传递参数 Page 即自动分页, 必须放在第一位
 * @param age  年龄
 * @return
 */
IPage<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);
<!--UserMapper.xml-->

<!--SQL 片段, 记录基础字段-->
<sql id="BaseColumns">id, username, age, email</sql>

<!--IPage<User> selectPageVo(Page<User> page, Integer age);-->
<select id="selectPageVo" resultType="User">
    select <include refid="BaseColumns"/>
    from t_user 
    where age > #{age}
</select>

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

4.1、wapper 介绍

image-20220419205528005

* Wrapper: 条件构造抽象类, 最顶端父类

    - AbstractWrapper: 用于查询条件封装, 生成 sql 的 where 条件

    - QueryWrapper          : 查询条件封装
    - UpdateWrapper         : Update 条件封装
    - AbstractLambdaWrapper : 使用 Lambda 语法

    - LambdaQueryWrapper  : 用于 Lambda 语法使用的查询 Wrapper
    - LambdaUpdateWrapper : Lambda 更新封装 Wrapper

4.2、UpdateWrapper

@Test
public void test07() {
    // 将 (年龄大于 20 或邮箱为 null) 并且 (用户名中包含有 a 的) 用户信息修改
    // 组装 set 子句以及修改条件
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();

    // lambda 表达式内的逻辑优先运算
    // UPDATE t_user SET age= ?, email= ? WHERE (username LIKE ? AND (age > ? OR email IS NULL))
    updateWrapper
        .set("age", 18)
        .set("email", "user@atguigu.com")
        .like("username", "a")
        .and(i -> i.gt("age", 20).or().isNull("email"));

    User user = new User();
    user.setName("张三");

    // 这里必须要创建 User 对象, 否则无法应用自动填充; 如果没有自动填充, 可以设置为 null
    // int result = userMapper.update(user, updateWrapper);
    // UPDATE t_user SET username = ?, age = ?, email = ? WHERE (username LIKE ? AND (age > ? OR email IS NULL))

    int result = userMapper.update(null, updateWrapper);

    System.out.println(result);
}

4.3、LambdaQueryWrapper

@Test
public void test09() {
    // 定义查询条件, 有可能为 null(用户未输入)
    String username = "a";
    Integer ageBegin = 10;
    Integer ageEnd = 24;
    LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();

    // 避免使用字符串表示字段, 防止运行时错误
    queryWrapper
        .like(StringUtils.isNotBlank(username), User::getName, username)
        .ge(ageBegin != null, User::getAge, ageBegin)
        .le(ageEnd != null, User::getAge, ageEnd);

    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
}

4.4、LambdaUpdateWrapper

@Test
public void test10() {
    // 组装 set 子句
    LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
    updateWrapper
        .set(User::getAge, 18)
        .set(User::getEmail, "user@atguigu.com")
        .like(User::getName, "a")
        .and(i -> i.lt(User::getAge, 24).or().isNull(User::getEmail)); // lambda 表达式内的逻辑优先运算

    User user = new User(); // 为了主键回填
    int result = userMapper.update(user, updateWrapper);
    System.out.println("受影响的行数: " + result);
}

5、QueryWrapper

方法的第一个参数都是 condition,用来实现动态 SQL

5.1、allEq

allEq(Map<R, V> params);
allEq(Map<R, V> params, boolean null2IsNull);
allEq(boolean condition, Map<R, V> params, boolean null2IsNull);
* `condition`  : 执行条件
* `params`     : key 为数据库字段名, value 为字段值
* `null2IsNull`: 为 true 则在 map 的 value 为 null 时调用 isNull 方法; 为 false 时则忽略 value 为 null 的


`params = {"id": 1, "name": "老王", "age": null}`

* `allEq(params)`
    - `id = 1 and name = "老王" and age is null`

* `allEq(params, false)`
    - `id = 1 and name = "老王"`

allEq(BiPredicate<R, V> filter, Map<R, V> params);
allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull);
allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull);
* `filter`: 过滤函数, 是否允许字段传入比对条件中

`params = {"id": 1, "name": "老王", "age": null, "password": "123456"}`

* `allEq((k, v) -> !k.equals("password"), params)`
    - `id = 1 and name = "老王" and age is null`

* `allEq((k, v) -> !k.equals("password"), params, false)`
    - `id = 1 and name = "老王"`

5.2、条件查询

  • eq:=
  • ne:!=
  • gt:>
  • ge:>=
  • lt:<
  • le:<=
  • between:BETWEEN 值 1 AND 值 2
  • notBetween:NOT BETWEEN 值 1 AND 值 2
  • in
  • notIn
  • isNull
  • isNotNull

5.3、模糊查询

  • like:like("name", "王") ---> name like "%王%"
  • notLike:notLike("name", "王") ---> name not like "%王%"
  • likeLeft:likeLeft("name", "王") ---> name like "%王"
  • likeRight:likeRight("name", "王") ---> name like "王%"
like(boolean condition, R column, Object val);

5.4、排序条件

  • orderBy:orderBy(true, true, "id", "name") ---> order by id ASC, name ASC
  • orderByAsc:orderByAsc("id", "name") ---> order by id ASC, name ASC
  • orderByDesc:orderByDesc("id", "name") ---> order by id DESC, name DES
orderBy(boolean condition, boolean isAsc, List<R> columns);

5.5、逻辑查询

  • or:拼接 OR,主动调用 or 表示紧接着下一个方法不是用 and 连接!(不调用 or 则默认为使用 and 连接)
    wrapper.eq("name", "李四").or().eq("age", 24); ---> name = "李四" OR age = 24

  • and:默认就是 and 连接,所以此处的 and 用于嵌套
    and(i -> i.eq("name", "李白").ne("status", "活着")) ---> and (name = "李白" and status != "活着")

5.6、select 子句

在 MP 查询中,默认查询所有的字段,如果有需要也可以通过 select 方法进行指定字段
wrapper.eq("name", "李四").or().eq("age", 24).select("id", "name", "age");

5.7、子查询

@Test
public void test06() {
    // 查询 id 小于等于 3 的用户信息
    // SELECT * FROM t_user WHERE (id IN (select id from t_user where id <= 3))

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.inSql("id", "select id from t_user where id <= 3");

    List<User> list = userMapper.selectList(queryWrapper);
    list.forEach(System.out::println);
}

5.8、groupBy、having

6、IService<Pojo>

6.1、插入

方法名 说明
boolean save(T entity) 插入一条记录
boolean saveBatch(Collection<T> entityList) 插入(批量)
boolean saveBatch(Collection<T> entityList, int batchSize) 插入(批量)插入批次数量
boolean saveOrUpdate(T entity) TableId 注解存在更新记录,否插入一条记录
boolean saveOrUpdateBatch(Collection<T> entityList) 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) 批量修改插入(每次的数量)

6.2、删除

方法名 说明
boolean removeById(Serializable id) 根据 ID 删除
boolean removeById(T entity) 根据实体(ID)删除
boolean removeByIds(Collection<? extends Serializable> idList) 删除(根据 ID 批量删除)
boolean remove(Wrapper<T> queryWrapper) 根据 entity 条件,删除记录

6.3、修改

方法名 说明
boolean updateById(T entity) 根据 ID 选择修改
updateBatchById(Collection<T> entityList) 根据 ID 批量更新
updateBatchById(Collection<T> entityList, int batchSize) 根据 ID 批量更新(更新批次数量)
boolean update(Wrapper<T> updateWrapper) 根据 UpdateWrapper 条件,更新记录
boolean update(T entity, Wrapper<T> updateWrapper) 根据 whereEntity 条件,更新记录

6.4、查询

方法名 说明
long count() 查询总记录数
long count(Wrapper<T> queryWrapper) 根据 Wrapper 条件,查询总记录数
T getById(Serializable id) 根据 ID 查询
List<T> listByIds(Collection<? extends Serializable> idList) 查询(根据 ID 批量查询)
T getOne(Wrapper<T> queryWrapper) 根据 Wrapper,查询一条记录
List<T> list() 查询所有
List<T> list(Wrapper<T> queryWrapper) 查询列表
E page(E page) 无条件翻页查询
E page(E page, Wrapper<T> queryWrapper) 翻页查询
posted @ 2023-08-06 12:04  lidongdongdong~  阅读(90)  评论(0)    收藏  举报