这里我们将MyBatisPlus(以下简称mp)框架整合到SpringBoot项目中, mp框架相对于
基础版mybatis
和通用版mybatis
有如下几个优点:
xml文件零配置
: 对于单表查询几乎实现零xml文件配置.丰富的查询条件
: 对于多条件查询利用Wrapper接口
可以实现和Hibernate
相媲美的查询.自带分页功能
: 通过将PaginationInterceptor类
注入到Spring容器中就可以实现分页查询.其实还有很多优点我这里就不一一列举了, 此片文章只讨论 单表查询 以及 单数据源配置 , 且没有利用到 mp框架的自动生成代码功能 , 要想详细的了解请访问如下网址:
SpringBoot
: 2.1.4.RELEASE.mybatis
2.0.1mybatis-plus
: 3.1.0创建SpringBoot项目引入Web, mybatis, mysql, lombok
插件.
外部网站搜索引入mybatis-plus-boot-starter
maven依赖.
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.1.0version>
dependency>
创建实体类, 我们这里以Book
类为例子, 其中数据库信息如下:
create table test.t_book
(
book_id bigint(11) auto_increment comment '主键'
primary key,
book_name varchar(24) not null comment '书名',
book_author varchar(24) not null comment '作者',
book_price decimal(10, 2) not null comment '价格',
push_date datetime not null comment '发布时间'
)
comment '书籍商品信息';
实体类Book
信息如下:
package com.pyi.config.mybatisplus.model;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* @author pyi
* @date 2019-04-09
*/
// @Data 注解来源于 `lombok` 插件, 实际上的作用是帮助自动生成`Get/Set/ToString/Equal方法`
// 具体信息我们可以访问 https://projectlombok.org/features/all 了解.
@Data
// @TableName 当数据库名称和类名不一致的时候通过 value 属性指定映射关系
@TableName
public class Book {
/**
* 这里要是没有作全局主键类型配置, 那么就需要 借用 `@TableId` 的属性 type 指明填充主键的方式, 填充参考 IdType.class
*/
@TableId
private Integer bookId;
private String bookName;
private String bookAuthor;
private BigDecimal bookPrice;
private Date pushDate;
}
这里我们值得注意的是:
@Data
注解来源于lombok
插件. 需要了解的朋友可以访问 lombok
Book
和 t_book
不一致为什么没有借助@TableName
注解修改映射名称? 这个我们后面会有说到.
@TableId
没有指定主键 的填充方式, 那么默认的填充方式是什么?
带着上面的问题我们`接下来作下面的事情.
创建mappper
包专门用来存放mapper接口
. 并且创建BaseMapper
接口继承mp 的BaseMapper
接口.
package com.pyi.config.mybatisplus.mapper;
/**
* @author pyi
* @date 2019-04-09
*/
public interface BaseMapper<T> extends com.baomidou.mybatisplus.core.mapper.BaseMapper<T> {
}
然后就创建我们的业务mappe接口
继承我们自己创建的基类BaseMapper
接口即可.
package com.pyi.config.mybatisplus.mapper;
import com.pyi.config.mybatisplus.model.Book;
/**
* @author pyi
* @date 2019-04-09
*/
public interface BookMapper extends BaseMapper<Book> {
}
注意要是我们没有使用全局@MapperScan
注解指向我们的mapper
包的话, 我们就要为每个业务mapper
使用@Mapper
将业务mapper
注入到Spring容器中.
在resource
下创建mapper
文件夹专门用来存放mapper.xml
映射配置文件. 这里我们可以在下面创建我们的业务mapper.xml
文件. 这里我们创建一个空的BookMapper.xml
以方便后续写多表查询的时候需要用到.
<mapper namespace="com.pyi.config.mybatisplus.mapper.BookMapper">
mapper>
在SpringBoot
启动类同级或者是子级下面创建MyBatisPlusConfig.class类
, 方便利用SpringBoot
启动类中@ComponentScan
注解结合MyBatisPlusConfig.class
中@Configuration
注解, 实现将 MyBatisPlusConfig.class
作为配置类注入到Spring容器
中.
package com.pyi.config.mybatisplus.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.pyi.config.mybatisplus.mapper.BaseMapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* MybatisPlus配置类
* @author pyi
* @date 2019-04-10
*/
@Configuration
// @MapperScan 注解指示mapper接口的位置, 注意使用 markerInterface属性将基础映射类过滤掉不参与Spring扫描
@MapperScan(basePackages = "com.pyi.config.mybatisplus.mapper", markerInterface = BaseMapper.class)
public class MybatisPlusConfig {
// 要想在查询中使用分页Page功能, 那么必须配置 PaginationInterceptor, 将其注入到Spring容器中.
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
return paginationInterceptor.setDialectType("mysql");
}
}
配置SpringBoot的配置文件application.context
.
# 配置数据源信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=Yi844579582!@#
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=true
# 配置MyBatis-Plus(具体各参数含义可参照 MyBatisPlusProperties.class 的各个属性))
mybatis-plus.mapper-locations=classpath*:/mapper/*.xml
# 配置MyBatis-Plus表映射的相关属性(这里是全局配置, 要是没有配置的话, 我们必须在对象里面单独配置 具体含义请参照 DbConfig,class 类)
mybatis-plus.global-config.db-config.db-type=mysql
mybatis-plus.global-config.db-config.id-type=auto
mybatis-plus.global-config.db-config.table-prefix=t_
mybatis-plus.global-config.db-config.table-underline=true
# 添加日志处理(这里我们是为了在测试类中打印mybatis执行的sql语句)
logging.level.com.pyi.config.mybatisplus = debug
这里我们就第二步中创建的实体类的几个遗留问题简单说下:
Book
和 t_book
不一致为什么没有借助@TableName
注解修改映射名称? 因为我们在全局配置中使用了mybatis-plus.global-config.db-config.table-prefix=t_
配置, 这里就说明将所有的类都会添加t_
开头用来与数据库表名映射.@TableId
没有指定主键 的填充方式, 那么默认的填充方式是什么? 这里我们用到了mybatis-plus.global-config.db-config.id-type=auto
属性指定默认的填充方式是auto自动增长
, 要是不指定的话, 默认采用的是id_worker
属性. 具体各个参数的含义我们可以访问 IdType
类进行查询.SpringBoot
的项目都会有一个配置类, 比如mp框架是MyBatisPlusProperties
类, 里面的属性就是可以在SpringBoot
全局配置中进行配置的. 要想了解各个参数的含义, 大家可以访问 MyBatisPlusProperties
进行查看. 这里要说的是, 这个方法可以扩展到其他的SpringBoot
项目. 大家遇到不懂的配置的时候就可以到相应的类中进行查看注释, 了解其原理是干嘛用的.注: 一般baseMapper已经有大部分的接口供我们调用, 一般配置到第七步我们就结束了. 但是如果项目中涉及到批量插入
等批量操作的时候, 那么我们就还需要进行最后一步创建Service层
.
这里我们创建BookService
接口和BookServiceImpl
实现类, 并将BookServiceImpl
继承mp框架的ServiceImpl
类.
package com.pyi.config.mybatisplus.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pyi.config.mybatisplus.mapper.BookMapper;
import com.pyi.config.mybatisplus.model.Book;
import com.pyi.config.mybatisplus.service.BookService;
import org.springframework.stereotype.Service;
/**
* @author pyi
* @date 2019-04-10
*/
@Service
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements BookService{
}
注意Service层我们要引入Service
注解, 目的是为了将其注入到Spring容器中.
这里的ServiceImpl
类对BaseMapper
的方法都做了封装, 且还包含有一些批量的操作, 比如saveBatch()
方法等,具体涉及到哪些方法, 大家可以去这个类中查看.
这里我们编写了几个增删改查的典型方法供大家参考.
package com.pyi.config.mybatisplus;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pyi.config.mybatisplus.model.Book;
import com.pyi.config.mybatisplus.service.impl.BookServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.math.BigDecimal;
import java.util.*;
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class MybatisplusApplicationTests {
@Autowired
private BookServiceImpl bookService;
/**
* 单次插入
*/
@Test
public void save() {
Book book = new Book();
book.setBookName("MyBatis-Plus");
book.setBookAuthor("pyi");
book.setBookPrice(BigDecimal.valueOf(33.33));
book.setPushDate(new Date());
// 单插入
boolean save = bookService.save(book);
System.out.println(save);
}
/**
* 批量插入
*/
@Test
public void saveBatch() {
List<Book> bookList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Book book = new Book();
book.setBookName("MyBatis-Plus" + i);
book.setBookAuthor("pyi");
book.setBookPrice(BigDecimal.valueOf(33.33));
book.setPushDate(new Date());
bookList.add(book);
}
// 这里saveBatch建立了事务, 一旦出现异常会自动回滚
boolean b = bookService.saveBatch(bookList);
System.out.println(b);
}
/**
* 删除我们将两种特殊方式
* 1. 按列删除 removeByMap()
* 2. 按条件删除 remove(Wrap)
*/
@Test
public void delete() {
// DELETE
// FROM t_book
// WHERE book_id = 10;
Map<String, Object> columnMap = new HashMap<>();
// 注意这里的 key => 用的是数据库的字段
columnMap.put("book_id", 10);
boolean removeByMap = bookService.removeByMap(columnMap);
System.out.println(removeByMap);
// DELETE
// FROM t_book
// WHERE book_author = 'pyi' AND book_price BETWEEN '30' AND '40' AND push_date >= '2019-04-10 04:03:21';
QueryWrapper<Book> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("book_author", "pyi")
.between("book_price", "30", "40")
.ge("push_date", "2019-04-10 04:03:21");
System.out.println(bookService.remove(queryWrapper));
}
/**
* 修改我们只讨论条件修改的方式
*/
@Test
public void update() {
// UPDATE t_book SET book_name = concat(book_name, '_1'),book_price=44.44
// WHERE book_author = 'pyi' AND book_id > 2;
UpdateWrapper<Book> updateWrapper = new UpdateWrapper<>();
Map<String, Object> updateMap = new HashMap<>();
updateMap.put("book_author", "pyi");
updateWrapper.setSql("book_name = concat(book_name, '_1')")
.set("book_price", 44.44)
.allEq(updateMap)
.gt("book_id", 2);
System.out.println(bookService.update(updateWrapper));
}
@Test
public void select() {
// 根据主键查询
System.out.println(bookService.getById(1));
// 根据条件查询并获取部分字段 (注意 getMap只会返回一个Map 对象, 要是条件满足多个则会返回第一个)
QueryWrapper<Book> queryWrapper = new QueryWrapper<>();
queryWrapper.select("book_id", "book_name")
.ge("book_id", 1)
.last(" limit 1");
System.out.println(bookService.getMap(queryWrapper));
// getOne 返回一个对象, 要是有多个对象返回第一个, 注意要是 第二个参数true(默认) 那么返回多个对象就会抛出异常
System.out.println(bookService.getOne(queryWrapper, false));
// list 返回多个对象集合
bookService.list(new QueryWrapper<Book>().gt("book_id",1)).forEach(System.out::println);
}
/**
* 注意, 要使用分页必配置 PaginationInterceptor到Spring容器中
*/
@Test
public void pagination() {
// SELECT COUNT(1) FROM t_book;
// SELECT book_id,book_name,book_author,book_price,push_date FROM t_bookLIMIT 1,1;
Page<Book> bookPage = new Page<>(2, 1);
IPage<Book> bookIPage = bookService.page(bookPage, Wrappers.emptyWrapper());
bookIPage.getRecords().forEach(System.out::println);
}
}
SpringBoot整合MyBatisPlus