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 {
@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会自动识别:
- 类名驼峰转下划线作为表名
- 名为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条件,可以满足日常开发的所有需求
@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);
}
@Test
void testQueryWrapper2(){
//首先编写条件
QueryWrapper<Book> wrapper = new QueryWrapper<Book>()
.eq("name", "jack");
//然后创建需要修改的对象属性
User user = new User;
user.setBalance(2000);
bookDao.update(user,wrapper);
}
@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语句中剩下的部分
基本步骤:
- 基于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);
- 在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
* */
@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风格实现下列小练习
- 新增用户
@Autowired
private AccountService accountService;
/*增添用户*/
@PostMapping
public 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批量查询
@GetMapping
public 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;
}
@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();
}
在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 批量删除
@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
下载完后就点击这个进行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、integer
logic-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, "女");
@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进行转换:
- 步骤:
3.5 分页插件功能
- 首先创建一个配置配去配置拦截器
@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;
}
}
- 调用方法,把条件插入
/*测试分页工具*/
@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;
}
}