MybatisPlus

MybatisPlus

  • 一、MyBatisPlus基础
    • 1.1 MyBatisPlus介绍
    • 1.2 MyBatisPlus入门
    • 2. 继承BaseMapper<对应的想要返回类的类名>
    • 1.3 常用注解
      • 1.3.1 @TableName
      • 1.3.2 @Tableid
      • 1.3.3 @TableField
    • 1.4 常用配置
  • 二、条件构造器
    • 2.2 自定义SQL
    • 2.3 Service接口
    • 2.4 基于Restful风格实现下列小练习
    • 2.5 IService的Lambda查询
    • 2.6 批量删除
  • 三、拓展业务
    • 3.1 静态工具
    • 3.2 逻辑删除
    • 3.3、枚举处理器
    • 3.4 JSON处理器
    • 3.5 分页插件功能
    • 3.6 通用分页实体

一、MyBatisPlus基础

1.1 MyBatisPlus介绍

MyBatisPlus:就是相当于开发mybatis的应用小能手,能够简化我们的开发

1.2 MyBatisPlus入门

  1. 导入对应的start包,可以在这里找到对应想要的坐标信息
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatisplus-spring-boot-starter -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatisplus-spring-boot-starter</artifactId>
    <version>1.0.5</version>
</dependency> 

2. 继承BaseMapper<对应的想要返回类的类名>

@Mapper
public interface BookDao extends BaseMapper<Book> { 
} 

测试:

@SpringBootTest
class MyBatisPlusQuickstartApplicationTests {

    @Autowired
    private BookDao bookDao;
    @Test
    void contextLoads() {
        System.out.println(bookDao.selectById(14));
        List<Book> books = bookDao.selectList(null);
        System.out.println(books);
    } 
}

1.3 常用注解

MvBatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息

当符合以下规定就不用进行配置,MybaitisPlus会自动识别:

  1. 类名驼峰转下划线作为表名
  2. 名为id的字段作为主键
  3. 变量名驼峰转下划线作为表的字段名

MybatisPlus_第1张图片

  • MybatisPlus中比较常用的几个注解如下
    • @TableName:用来指定表名
    • @Tableid:用来指定表中的主键字段信息
    • @TableField:用来指定表中的普通字段信息

1.3.1 @TableName

当表名和类的名字不相符合,那么就要添加这个注解来进行消除错误

MybatisPlus_第2张图片

1.3.2 @Tableid

如果主键不存在或者主键的名字不为id那么就要加上这个注解

  • ldType枚举:
    • AUTO : 数据库自增长
    • INPUT : 通过set方法自行输入
    • ASSIGN ID : 分配 ID,接口ldentifierGenerator的方法nextld来生成id默认实现类为DefaultldentifierGenerator雪花算法

1.3.3 @TableField

  • 常用的场景:
    • 成员变量名与数据库字段名不一致
    • 成员变量名以is开头,且是布尔值
    • 成员变量名与数据库关键字冲突
    • 成员变量不是数据库字段
@TableName("tbl_book")
public class Book {
    @TableId(type = IdType.AUTO)
    private Integer id;
    @TableField("`type`")//进行转意
    private String type;
    @TableField(exist = false) //将其表面部位数据库当中的字段
    private String description;
    private String name; 
    public Book() {
    }

MybatisPlus_第3张图片

1.4 常用配置

mybatis-plus:
            type-aliases-package: com.itheima.mp.domain.po # 别名扫描包
            mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,默认值
            configuration:
              map-underscore-to-camel-case: true # 是否开启下划线和驼峰的映射
              cache-enabled: false # 是否开启二级缓存
            global-config:
              db-config:
                id-type: assign_id # id为雪花算法生成
                update-strategy: not_null # 更新笑略:只更新非空字段
                table-prefix: tbl_ #表面所有表面前面都加一个tbl_

这些配置不需要记下来,只需要我们需要配置的时候在官网上面进行查询

二、条件构造器

MyBatisPlus支持各种复杂的where条件,可以满足日常开发的所有需求

MybatisPlus_第4张图片

@Test
    void testQueryWrapper(){
        //首先编写了查询的条件
       QueryWrapper<Book> wrapper = new QueryWrapper<Book>()
               .select("id","username","info","balance")
               .like("username","o")
               .ge("balance","1000");

       //进行查询操作
       List<Book> books = bookDao.selectList(wrapper);
       books.forEach(System.out::println);
   }

利用LambdaQueryWrapper书写:

@Test
    void testQueryWrapper(){

        LambdaQueryWrapper<Book> wrapper = new LambdaQueryWrapper<Book>()
                .select(User::getId, User::getUsername, User::getInfo, User::getBalance)
                .like(User::getUsername, "o")
                .ge(User::getBalance, "1000");

        //进行查询操作
        List<Book> books = bookDao.selectList(wrapper);
        books.forEach(System.out::println);
    }

MybatisPlus_第5张图片

@Test
    void testQueryWrapper2(){
       //首先编写条件
        QueryWrapper<Book> wrapper = new QueryWrapper<Book>()
               .eq("name", "jack");
       //然后创建需要修改的对象属性
       User user = new User;
       user.setBalance(2000);
       bookDao.update(user,wrapper);
   }

MybatisPlus_第6张图片

  @Test
    void testUpdateWrapper(){
        //首先编写条件
        List<Long> ids=List.of(1L,4L,2L);
        UpdateWrapper<Book> wrapper = new UpdateWrapper<Book>()
                .setSql("balance = balance -200")
                .in("id", ids);
        //然后创建需要修改的对象属性
        User user = new User;
        user.setBalance(2000);
        bookDao.update(user,wrapper);
    }

2.2 自定义SQL

我们可以利用MyBatisPlus的Wrapper来构建复杂的Where条件,然后自己定义SQL语句中剩下的部分

基本步骤:

  1. 基于Wrapper构建where条件
 @Test
    void testQueryWrappers(){
        List<Long> ids=List.of(1L,4L,2L);
        int mount = 200;
        //编写条件
        QueryWrapper<User> wrapper = new QueryWrapper<User>()
                .in("id", ids);

        bookDao.updateIds(wrapper,mount);
  1. 在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew
@Update("update tb_user set balance = balance - #{mount} ${ew.customSqlSegment}")
void updateIds(@Param("ew") QueryWrapper<User> wrapper,@Param("mount") int mount);

2.3 Service接口

利用的就是MybatisPlus创建好的IServiceServiceImpl两个父类,service和serviceImpl分别继承上述两个父类,就拥有了一系列MybaitisPlus封装好的方法

步骤:

  • 创建好BookService和 BookServiceImpl类,然后进行继承
@Service
public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements BookService {

}
public interface BookService extends IService<Book> {
} 
  • 测试
 /*
    * 用来测试service
    * */
    @Autowired
    private BookService service;
    @Test
    void testService(){
        Book book = new Book();
        book.setName("大学英语");
        book.setDescription("学习英语成就背时人生!!!!");
        book.setType("四级英语");
        service.save(book);
    }

    /*
    * 查询
    * */

    @Test
    void testServiceQuery(){
        List<Book> books = service.listByIds(List.of(15L, 16L, 22L));
        books.forEach(System.out::println);
    }

2.4 基于Restful风格实现下列小练习

MybatisPlus_第7张图片

  1. 新增用户
@Autowired
    private AccountService accountService;

    /*增添用户*/
    @PostMapping
    public void InsertAccount(Account account){
        accountService.save(account);
    }
  1. 删除用户
@DeleteMapping("/{id}")
    public void DeleteAccount(@PathVariable Long id){
        accountService.removeById(id);
    }
  1. 根据ID查询用户
@GetMapping("/{id}")
    public Account QueryAccountById(@PathVariable Long id){
        return accountService.getById(id);
    }
  1. 根据IDS批量查询
@GetMapping
    public List<Account> QueryAccountByIds(@RequestParam("ids") List<Long> ids){
        return accountService.listByIds(ids);
    }
  1. 根据Id扣减余额

Controller中的代码:

 @PutMapping("/{id}/deduction/{money}")
    public void deductionMoneyById(@PathVariable Long id ,@PathVariable Long money){
        accountService.deductionMoney(id,money);
    }

AccountService中的代码:

public interface AccountService extends IService<Account> {
    void deductionMoney(Long id, Long money);
}

AccountServiceImpl当中的代码:

 public void deductionMoney(Long id, Long money) {
        //查询用户
        Account account = this.getById(id);
        //验证用户信息
        if (account == null || account.getStatus() == 0){
            //代表的是不能进行减去操作
            throw new RuntimeException("用户不存在或账户冻结");
        }
        //查询是否金额能够减去
        if(account.getMoney() < money ){
            throw new RuntimeException("用户的余额不足");
        }
        //进行金额消减
        baseMapper.deductionMoneyById(id,money);
    }

AccountDao当中的代码:

@Mapper
public interface AccountDao extends BaseMapper<Account> { 
    @Update("update tbl_account set money = money - #{money} where id = #{id};")
    void deductionMoneyById(@Param("id") Long id,@Param("money") Long money); 
}

2.5 IService的Lambda查询

MybatisPlus_第8张图片

/*lambda查询用户,Condition是利用一个类来接收传输过来的数据*/
    @GetMapping("/list")
    public List<Account> QueryAccountLambda(Condition condition){
        List<Account> accounts = accountService.lambdaQuerys(
                condition.getUsername(),
                condition.getStatus(),
                condition.getMaxBalance(),
                condition.getMinBalance()
        );
        return accounts;
    }
@Override
    public List<Account> lambdaQuerys(
            @Param("username") String username,
            @Param("status") Integer status,
            @Param("maxBalance") Long maxBalance,
            @Param("minBalance") Long minBalance)
    {
        return lambdaQuery()
                .eq(username != null,Account::getUsername, username)
                .eq(status != null ,Account::getStatus, status)
                .le(maxBalance!= null,Account::getMoney, maxBalance)
                .ge(minBalance!= null,Account::getMoney, minBalance)
                .list();
    }

MybatisPlus_第9张图片
在Service当中实现的逻辑处理:

 @Override
    public void deductionMoney(Long id, Long money) {
        //查询用户
        Account account = this.getById(id);
        //验证用户信息
        if (account == null || account.getStatus() == 0){
            //代表的是不能进行减去操作
            throw new RuntimeException("用户不存在或账户冻结");
        }
        //查询是否金额能够减去
        if(account.getMoney() < money ){
            throw new RuntimeException("用户的余额不足");
        }
        //进行金额消减
        long deMoney = account.getMoney() - money;
        System.out.println(deMoney);
        lambdaUpdate()
                .set(Account::getMoney,deMoney)
                .set(deMoney == 0,Account::getStatus,0)
                .eq(Account::getId,id)
                .eq(Account::getMoney,account.getMoney()) //乐观锁,防止多线程并发问题
                .update(); //必须要进行更新操作
    }

2.6 批量删除

MybatisPlus_第10张图片

 @Autowired
    private AccountService service;

    /*首先创建一个自动增加的数据*/
    private Account buildAccount(int i){
        Account account = new Account();
        account.setMoney(2000+i);
        account.setStatus(1);
        account.setUsername("zhangsan" + i);
        return account;
    }


    @Test
    void InsertIds() {
        //首先创建一个容量为1000的list容器
        List<Account> list = new ArrayList<>(1000);

        //记录下当前时间
        long now = System.currentTimeMillis();

        for (int i = 0; i < 10000; i++) {
            list.add(buildAccount(i));
            //判断是否存满
            if (i%1000 == 0){
                //已经存满,加载数据
                service.saveBatch(list);
                //将list数据清空
                list.clear();
            }
        }
        long after = System.currentTimeMillis();
        System.out.println("总共用时:"+(after-now));
    }

在Datasource的url当中&这个参数:
在这里插入图片描述

三、拓展业务

下载插件IDEA当中的MybatisPlus

MybatisPlus_第11张图片
下载完后就点击这个进行Datasource的配置
MybatisPlus_第12张图片

MybatisPlus_第13张图片

点击这个进行配置文件的放置

MybatisPlus_第14张图片
MybatisPlus_第15张图片

3.1 静态工具

MybatisPlus_第16张图片
出现静态工具的原因是:
以为如果使用的是service进行创作,如果两张表相互嵌套相互使用,就会出现循环依赖,为了去除这个循环依赖,所以引入了静态工具

在这里插入图片描述

 Db.lambdaQuery(Account.class)
                .eq(Account::getId, id)
                .list()

3.2 逻辑删除

  • 逻辑删除就是基于代码逻辑模拟删除效果,但并不会真正删除数据。思路如下
    • 在表中添加一个字段标记数据是否被删除
    • 当删除数据时把标记置为1
    • 查询时只查询标记为0的数据

MybatisPlus提供了逻辑删除功能,无需改变方法调用的方式,而是在底层帮我们自动修改CRUD的语句。我们要做的就是在application.yaml文件中配置逻辑删除的字段名称和值即可:

mybatis-plus:
  global-config:
    db-config: 
      logic-delete-field: deleted# 全局逻辑删除的实体字名,字段类型可以是boolean、integer
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

就是说明我们在进行delete删除数据的时候不再是进行数据的删除,只是把deleted修改为1,在查询的时候加上where条件进行查询是否是1,如果为1,就不会在页面当中显示出来

逻辑删除的弊端:

  • 逻辑删除本身也有自己的问题,比如:会导致数据库表垃圾数据越来越多,影响查询效率
  • SQL中全都需要对逻辑删除字段做判断,影响查询效率因此,我不太推荐采用逻辑删除功能,
  • 如果数据不能删除,可以采用把数据迁移到其它表的办法

3.3、枚举处理器

步骤:

  1. 给枚举中的与数据库对应value值添加@EnumValue注解

  2. 在配置文件中配置统一的枚举处理器,实现类型转换


public enum SexEnum {
 
    MAN(1, "男"),
    WOMAN(2, "女");
 
    @EnumValue
    private Integer key;
 
    @JsonValue
    private String display;
 
    SexEnum(Integer key, String display) {
        this.key = key;
        this.display = display;
    }
 
    public Integer getKey() {
        return key;
    }
 
    public String getDisplay() {
        return display;
    }
}

3.4 JSON处理器

将数据库当中的JSON类型的String进行转换:

MybatisPlus_第17张图片

  • 步骤:
    在这里插入图片描述
    在这里插入图片描述

3.5 分页插件功能

  1. 首先创建一个配置配去配置拦截器
@Configuration
public class MyBatisConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //1. 创建分页条件
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        //2.将分页的条件放入到拦截器当中
        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        return interceptor;
    }
}
  1. 调用方法,把条件插入
 /*测试分页工具*/
    @Test
    void testPage(){
        //给与条件
        int pageNum = 1;
        int pageSize = 1;
        Page<Book> page = Page.of(pageNum, pageSize);

        //添加排序条件,可以添加多个,如果前一个相同,就按照后面的进行操作
        page.addOrder(new OrderItem("id", true));

        Page<Book> p =  service.page(page);  
		//可以查询表的其他信息 
        
        //获取到查询数据的总条数
        long total = p.getTotal();
        System.out.println("total"+total);
        //获取到当前分页的全部数据
        List<Book> records = p.getRecords();
        records.forEach(System.out::println);
        //获取到页码的数量
        long pages = p.getPages();
        System.out.println("pages:"+pages);
    }

3.6 通用分页实体

pojo.Book层:

@Data
@TableName("tbl_book")
public class Book {
    private Integer id;
    private String type;
    private String name;
    private String description;
}

PageInfo(就是封装的分页信息类):

@Data
public class PageInfo<T> {
    /*查询到的总条数*/
    private Long total;
    /*查询到的总页数*/
    private Long pages;
    /*查询到当前页的所有数据*/
    private List<T> info;
}

MybatisConfig(注解封装的拦截器,用来拦截信息添加分页功能):

@Configuration
@MapperScan("com.itheima.dao")
public class MyBatisConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //1. 创建分页条件
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        //2.将分页的条件放入到拦截器当中
        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        return interceptor;
    }
}

BookController:

@RestController
@RequestMapping("/books")
public class BookController {
    @Autowired
    private BookService service;

   
    @GetMapping("/pages")
    PageInfo<Book> myQueryPages(BookQuery bookQuery){
        return service.QueryPages(bookQuery);
    } 
}

BookDao:

@Mapper
public interface BookDao extends BaseMapper<Book>{
    @Select("select * from tbl_book where id > #{id}")
    Page<Book> selectPageVo(@Param("page") Page<User> page, @Param("id") Integer id);
}

TemplateQuery(查询条件类的模板《父类》):

@Data
public class TemplateQuery {
    //页码
    private int queryPage;
    //size
    private int querySize;
    //排序条件
    private String ordered;
    //升序还是降序(true升序)
    private boolean desc;
}

BookQuery(创建放置查询条件的类):

@Data
public class BookQuery extends TemplateQuery{
    /*查询的书名字*/
    private String name;
    /*查询的科目*/
    private String type;
}

Bookservice:

public interface BookService extends IService<Book> {
    PageInfo<Book> QueryPages(BookQuery bookQuery);
} 

BookServiceImpl:

@Service
public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements BookService {
    @Override
    public PageInfo<Book> QueryPages(BookQuery bookQuery) {
        //获取到传输过来的数据
        int queryPage = bookQuery.getQueryPage();
        String name = bookQuery.getName();
        int querySize = bookQuery.getQuerySize();
        String ordered = bookQuery.getOrdered();
        String type = bookQuery.getType();

        //分页查询条件
        Page<Book> page = Page.of(queryPage, querySize);

        //排序条件
        //进行判断
        if(ordered != null){
            page.addOrder(new OrderItem(ordered,bookQuery.isDesc()));
        }else {
            //设置默认值
            page.addOrder(new OrderItem("id",true));
        }


        //分页条件
        lambdaQuery()
                .like(name !=null,Book::getName,name)
                .like(type != null,Book::getType,type)
                .page(page);

        //将数据进行封装
        PageInfo<Book> bookPageInfo = new PageInfo<>();

        bookPageInfo.setPages(page.getPages());
        bookPageInfo.setInfo(page.getRecords());
        bookPageInfo.setTotal(page.getTotal());

        return bookPageInfo;
    }
}

你可能感兴趣的:(mybatis,mybatisPlus)