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入门
- 导入对应的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 {@Autowiredprivate BookDao bookDao;@Testvoid contextLoads() {System.out.println(bookDao.selectById(14));List<Book> books = bookDao.selectList(null);System.out.println(books);}
}
1.3 常用注解
MvBatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息
当符合以下规定就不用进行配置,MybaitisPlus会自动识别:
- 类名驼峰转下划线作为表名
- 名为id的字段作为主键
- 变量名驼峰转下划线作为表的字段名
- MybatisPlus中比较常用的几个注解如下
- @TableName:用来指定表名
- @Tableid:用来指定表中的主键字段信息
- @TableField:用来指定表中的普通字段信息
1.3.1 @TableName
当表名和类的名字不相符合,那么就要添加这个注解来进行消除错误
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() {}
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条件,可以满足日常开发的所有需求
@Testvoid 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书写:
@Testvoid 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);}
@Testvoid testQueryWrapper2(){//首先编写条件QueryWrapper<Book> wrapper = new QueryWrapper<Book>().eq("name", "jack");//然后创建需要修改的对象属性User user = new User;user.setBalance(2000);bookDao.update(user,wrapper);}
@Testvoid 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语句中剩下的部分
基本步骤:
- 基于Wrapper构建where条件
@Testvoid 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);
- 在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创建好的IService<Book>
和 ServiceImpl<BookDao, Book>
两个父类,service和serviceImpl分别继承上述两个父类,就拥有了一系列MybaitisPlus封装好的方法
步骤:
- 创建好BookService和 BookServiceImpl类,然后进行继承
@Service
public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements BookService {}
public interface BookService extends IService<Book> {
}
- 测试
/** 用来测试service* */@Autowiredprivate BookService service;@Testvoid testService(){Book book = new Book();book.setName("大学英语");book.setDescription("学习英语成就背时人生!!!!");book.setType("四级英语");service.save(book);}/** 查询* */@Testvoid testServiceQuery(){List<Book> books = service.listByIds(List.of(15L, 16L, 22L));books.forEach(System.out::println);}
2.4 基于Restful风格实现下列小练习
- 新增用户
@Autowiredprivate AccountService accountService;/*增添用户*/@PostMappingpublic void InsertAccount(Account account){accountService.save(account);}
- 删除用户
@DeleteMapping("/{id}")public void DeleteAccount(@PathVariable Long id){accountService.removeById(id);}
- 根据ID查询用户
@GetMapping("/{id}")public Account QueryAccountById(@PathVariable Long id){return accountService.getById(id);}
- 根据IDS批量查询
@GetMappingpublic List<Account> QueryAccountByIds(@RequestParam("ids") List<Long> ids){return accountService.listByIds(ids);}
- 根据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查询
/*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;}
@Overridepublic 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();}
在Service当中实现的逻辑处理:
@Overridepublic 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 批量删除
@Autowiredprivate AccountService service;/*首先创建一个自动增加的数据*/private Account buildAccount(int i){Account account = new Account();account.setMoney(2000+i);account.setStatus(1);account.setUsername("zhangsan" + i);return account;}@Testvoid 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
下载完后就点击这个进行Datasource的配置
点击这个进行配置文件的放置
3.1 静态工具
出现静态工具的原因是:
以为如果使用的是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、integerlogic-delete-value: 1 # 逻辑已删除值(默认为 1)logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
就是说明我们在进行delete删除数据的时候不再是进行数据的删除,只是把deleted修改为1,在查询的时候加上where条件进行查询是否是1,如果为1,就不会在页面当中显示出来
逻辑删除的弊端:
- 逻辑删除本身也有自己的问题,比如:会导致数据库表
垃圾数据越来越多
,影响查询效率 - SQL中全都需要对逻辑删除字段做判断,影响查询
效率
因此,我不太推荐采用逻辑删除功能, - 如果数据不能删除,可以采用把数据迁移到其它表的办法
3.3、枚举处理器
步骤:
-
给枚举中的与数据库对应value值添加
@EnumValue
注解 -
在配置文件中配置统一的枚举处理器,实现类型转换
public enum SexEnum {MAN(1, "男"),WOMAN(2, "女");@EnumValueprivate Integer key;@JsonValueprivate 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进行转换:
- 步骤:
3.5 分页插件功能
- 首先创建一个配置配去配置拦截器
@Configuration
public class MyBatisConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//1. 创建分页条件PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);//2.将分页的条件放入到拦截器当中interceptor.addInnerInterceptor(paginationInnerInterceptor);return interceptor;}
}
- 调用方法,把条件插入
/*测试分页工具*/@Testvoid 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 {@Beanpublic 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 {@Autowiredprivate 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;//sizeprivate 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 {@Overridepublic 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;}
}