mybatis-plus笔记

常用注解

@TableName

操作数据库表时,Mapper接口继承BaseMapper,泛型名和数据库表名对应,如果数据表名为t_user,而BaseMapper的泛型为实体类User,导致找不到数据库的表。
1)解决方法1:实体类使用@TableName注解,value值为表名

@Data
@AllArgsConstructor
@NoArgsConstructor

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

2)解决方法2,如果多张表的表名为t_user/t_cat/t_xxx,不需要为每一个实体类添加@TableName注解,在MyBatis全局配置即可,为所有表名添加前缀

mybatis-plus:
  global-config: 	# MyBatisPlus全局配置
    db-config:  	# 配置数据库
      table-prefix: t_  #配置表名前缀为t_
@TableId

MyBatisPlus在实现CRUD默认会将Id作为主键,在插入数据时,如果主键不叫Id则添加功能会失败

解决方案:@TableId注解标识属性,将此属性对应的字段指定为主键

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_user")
public class User {
   // 将当前属性所对应的字段作为主键
    @TableId
    private Long id;
    private String name;
    private String sex;
    private Integer age;
}

1)value属性

问题:实体类中被标识为主键的属性名为id,而数据库的主键为uid,则id属性不会对应uid字段上

解决方案:使用@TableId的value属性设置当前主键字段的字段名为uid

package com.dfbz.entity;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    @TableId(value = "uid")
    private Long id;
    private String name;
    private String sex;
    private Integer age;
}

2)主键策略
在枚举类IdType中定制有如下几种注解策略:

public enum IdType {
    
    // 数据自增(该类型请确保数据库设置了 ID自增 否则无效)
    AUTO(0),
    
    // 采用雪花算法生成ID
    NONE(1),
    
    // 交由用户自己分配ID(必须分配,不分配则出现异常,除非数据库表本身设置了自增)
    INPUT(2),

    // 采用雪花算法((主键类型为number或string)
    // 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
    ASSIGN_ID(3),
    
    // 分配UUID (主键类型为 string)
    // 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
    ASSIGN_UUID(4),
    
    // 不推荐使用,推荐使用ASSIGN_ID
    @Deprecated
    ID_WORKER(3),
    
    // 不推荐使用,推荐使用ASSIGN_ID
    @Deprecated
    ID_WORKER_STR(3),
    
    // 不推荐使用,推荐使用ASSIGN_UUID
    @Deprecated
    UUID(4);

    private final int key;

    IdType(int key) {
        this.key = key;
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    // 使用数据库自增策略
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private String sex;
    private Integer age;
}

全局配置自增策略:

mybatis-plus:
  global-config: 	# MyBatisPlus全局配置
    db-config:  	# 配置数据库
      id-type: auto # 统一设置id生成策略

@TableField

如果实体类的普通属性名,和数据库非主键的字段名不一致解决方案:@TableField设置对应字段名

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @TableId(type = IdType.AUTO)
    private Long id;
    
    @TableField("user_name")
    private String name;
    
    @TableField("user_sex")
    private String sex;
    
    @TableField("user_age")
    private Integer age;
}
@TableLogic

在实际开发中,我们删除一条记录可能只是修改其状态,并不是真正的从数据库中删除掉;这样的删除成为逻辑删除;

逻辑删除:表中设置字段为删除状态 比如删除为1 未删除为0 则查询时,只会查到状态为0的数据(可以进行数据恢复)。
物理删除:从表中删除。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    /*
        表示该列为逻辑删除字段
        0: 表示未删除
        1: 表示已删除
     */
    @TableLogic
    private Integer isDelete;
}
@EnumValue

在数据库中,经常会有一些字段符合枚举类型;
例如:

    sex:0代表男,1代表女
    status:0代表未开始,1代表进行中,2代表已结束
    is_secret:0代表私密,1代表公开等
    is_mark:0代表未签收,1代表拒签,2代表已签收

我们通常都是将以上字段设置为char或者int类型,然后通过对应的字符/数字代表对应的含义,然后到Java代码中我们通常都需要做类似于如下的判断

<span>${sex==0?'男':'女'}</span>

<if test=${status==0}>未开始</if>
<if test=${status==1}>进行中</if>
<if test=${status==2}>已结束</if>

if(status=0){
	// 未开始....
}else if(status==1){
	// 进行中
} else if(status==2){
	已结束
}

以上代码比较繁琐,并且可读性不是很高;

在MyBatisPlus中支持我们定义一个枚举与数据库中的字段对应起来,然后在枚举类中,使用@EnumValue注解标注真实的值(与数据库的字段对应),然后定义一个String类型的字段表示这个枚举项代表的字符串含义;

public enum SexEnum {
    MAN(0, "男"),
    WOMAN(1, "女");

    // 这一列的值和数据库表映射
    @EnumValue      
    private Integer sexValue;
    
    // 这个字段就是这个枚举项的字符串含义
    private String sexName;

    private SexEnum(Integer sexValue, String sexName) {
        this.sexValue = sexValue;
        this.sexName = sexName;
    }
}

扫描枚举包:

#配置日志
mybatis-plus:
  # 扫描枚举包
  type-enums-package: com.dfbz.enum_

定义实体类:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private Integer id;
    private String name;
    /*
        使用枚举类型
            当从数据库中查询到了0则赋值SexEnum.MAN给sex变量
            当从数据库中查询到了1则赋值SexEnum.WOMAN给sex变量
     */
    private SexEnum sex;
}
@Version

1)乐观锁与悲观锁

在多个客户端(线程/进程)操作同一个资源并发生写的操作时,我们就需要保证数据的安全性了。
如图所示:
在这里插入图片描述当我们在购买车票时,首先会进行票数的查询,例如A1在购买车票时查询到票还有100张,准备购买第100张票,与此同时,A2也查询到票数为100,也将购买第100张票;
在并发修改某一资源时,我们必须保证线程安全的问题。在操作之前先加锁,这种方式就是采用悲观锁的方式;

悲观锁的概念:悲观锁简单的理解就是程序处于悲观状态,在操作任何资源时都认为其他程序会来修改当前资源,自己不放心,因此在操作资源时加锁。

在这里插入图片描述悲观锁虽然保证了程序的安全性,同时效率也降低了很多,在一个客户端操作时,其他客户端均不可操作,降低了系统的并发性能。

乐观锁概念:乐观锁简单理解就是程序一直处于乐观状态,在操作任何资源时认为其他程序不会来修改当前资源,整个过程不加锁,不加锁效率是可以保证,但不是又回到了我们最初的那个状态吗?即线程安全问题。

我们可以在每条记录中分配一个_version字段,每当我们对记录进行更新时,此版本号都会自增。我们可以借助该字段帮我们完成乐观锁。保证线程安全问题。
在这里插入图片描述

-- 要修改数据之前,先查该数据上一次修改的时间戳
select version from table_ where id=1; 

-- 修改数据时,更新版本号
update table_ set goods_name='小苹果', version=version+1 where version=${version};

@Version实现乐观锁

package com.dfbz.entity;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer count;

    @Version            // 乐观锁字段
    private Integer version;
}

Mapper接口:

import org.springframework.stereotype.Repository;

@Repository
public interface GoodsMapper extends BaseMapper<Goods> {
}

测试代码:

@Autowired
private GoodsMapper goodsMapper;

@Test
public void test6() throws Exception {
    Goods goods = goodsMapper.selectById(1L);
    goods.setCount(goods.getCount() - 1);

    goodsMapper.updateById(goods);      // 修改完毕后,version在本次version的基础上+1
}

MyBatis Plus绑定Mapper.xml

1)在application.yml中扫描Mapper.xml:

#配置日志
mybatis-plus:
  # 扫描mapper.xml文件
  mapper-locations: classpath:com/dfbz/mapper/*.xml
 
  # 配置包别名
  type-aliases-package: com.dfbz.entity

2)在Mapper接口中扩展方法:

@Repository
public interface UserMapper extends BaseMapper<User> {
    List<User> findAll();
}

3)编写Mapper.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">
<!--namespace 名称空间,指定对哪个接口进行映射-->
<mapper namespace="com.dfbz.mapper.UserMapper">
    <select id="findAll" resultType="user">
        select * from user
    </select>
</mapper>

Mapper的分页查询

Mapper分页查询配置

在MyBatis中提供有Page对象来帮助我们实现分页查询,在Page对象中有如下成员:
在这里插入图片描述MyBatisPlus的分页逻辑底层是通过分页插件来完成的,因此我们首先要配置MyBatisPlus的分页插件;


@Configuration
@MapperScan("com.dfbz.mapper")          // mapper接口的所在位置
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new
                PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

Mapper完成分页查询

在BaseMapper中主要提供有如下方法来完成分页查询:

<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper):
    参数1:分页配置类
    参数2:分页查询条件
    解释:根据分页配置和分页查询条件来完成分页查询,当前页数据为指定类型
<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper)
    参数1:分页配置类
    参数2:分页查询条件
    解释:根据分页配置和分页查询条件来完成分页查询,当前页数据为Map类型

1)无条件分页查询

@SpringBootTest(classes = MyBatisPlusApplication.class)
@RunWith(SpringRunner.class)
public class Demo06_BaseMapper的分页查询 {

    @Autowired
    private UserMapper userMapper;

    /**
     * 无条件分页查询
     * @throws Exception
     */
    @Test
    public void test1() throws Exception {

        // 封装分页信息
        Page<User> page = new Page<>(1,3);

        /*
         执行分页查询,并将结果封装到page中
            参数1: 分页配置
            参数2: 查询条件
         */
        userMapper.selectPage(page, null);

        // 当前页数据
        List<User> pageData = page.getRecords();
        for (User user : pageData) {
            System.out.println(user);
        }

        System.out.println("------------");

        System.out.println("当前页:" + page.getCurrent());
        System.out.println("每页显示的条数:" + page.getSize());
        System.out.println("总记录数:" + page.getTotal());
        System.out.println("总页数:" + page.getPages());
        System.out.println("是否有上一页:" + page.hasPrevious());
        System.out.println("是否有下一页:" + page.hasNext());
    }
}

2)带条件分页查询:

/**
 * 带条件分页查询
 *
 * @throws Exception
 */
@Test
public void test2() throws Exception {

    QueryWrapper<User> wrapper = Wrappers.query();
    wrapper.like("name", "a");

    // 封装分页信息
    Page<User> page = new Page<>(1, 3);

    /*
     执行分页查询,并将结果封装到page中
        参数1: 分页配置
        参数2: 查询条件
     */
    userMapper.selectPage(page, wrapper);

    // 当前页数据
    List<User> pageData = page.getRecords();
    for (User user : pageData) {
        System.out.println(user);
    }

    System.out.println("------------");

    System.out.println("当前页:" + page.getCurrent());
    System.out.println("每页显示的条数:" + page.getSize());
    System.out.println("总记录数:" + page.getTotal());
    System.out.println("总页数:" + page.getPages());
    System.out.println("是否有上一页:" + page.hasPrevious());
    System.out.println("是否有下一页:" + page.hasNext());
}

3)将分页数据的查询结果以Map类型返回

/**
 * 查询结果以Map返回
 *
 * @throws Exception
 */
@Test
public void test3() throws Exception {

    // 封装分页信息
    Page page = new Page<>(1, 3);

    userMapper.selectMapsPage(page, null);

    // 每一条记录都是一个HashMap
    List<HashMap<String,Object>> pageData = page.getRecords();
    for (HashMap userMap : pageData) {
        System.out.println(userMap);
    }

    System.out.println("------------");

    System.out.println("当前页:" + page.getCurrent());
    System.out.println("每页显示的条数:" + page.getSize());
    System.out.println("总记录数:" + page.getTotal());
    System.out.println("总页数:" + page.getPages());
    System.out.println("是否有上一页:" + page.hasPrevious());
    System.out.println("是否有下一页:" + page.hasNext());
}

原文链接:https://blog.csdn.net/Bb15070047748/article/details/129212543

  • 25
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值