SpringBoot整合MybatisPlus(小白也秒懂)
话不多说,上流程
创建好对应的数据库和表
-- 删除tb_brand表
drop table if exists tb_brand;
-- 创建tb_brand表
create table tb_brand
(
-- id 主键
id int primary key auto_increment,
-- 品牌名称
brand_name varchar(20),
-- 企业名称
company_name varchar(20),
-- 排序字段
ordered int,
-- 描述信息
description varchar(100),
-- 状态:0:禁用 1:启用
status int
);
-- 添加数据
insert into tb_brand (brand_name, company_name, ordered, description, status)
values ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
('华为', '华为技术有限公司', 100, '华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1);
创建SpringBoot项目
目录结构
导入pom.xml配置
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 日志依赖-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<!-- mybatisplus依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!-- 数据源依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
yml文件配置
server:
port: 8889
spring:
application:
name: mybatis_test
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_test?useSSL=false&serverTimezone=UTC
username: root
password: 1234
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印SQL日志到控制台
global-config:
db-config:
id-type: auto #主键id生成策略。
创建对应的实体类
@Data
@Entity
@TableName("tb_brand")
public class Brand implements Serializable {
private Integer id;
// 品牌名称
private String brandName;
// 企业名称
private String companyName;
// 排序字段
private Integer ordered;
// 描述信息
private String description;
// 状态:0:禁用 1:启用
private Integer status;
}
这里的@TableName
注解是依赖映射数据库对应表的名字的,如实体类名与表名一致可以不加。
创建对应的Dao接口
@Mapper
public interface BrandDao extends BaseMapper<Brand> {
}
@Repository和@Mapper的区别
- @Repository和@Mapper都是作用在dao层的接口,使其生成代理对象Bean。
- @Repository:需要在添加配置地址@MapperScannerConfigurer来使用,单独使用会报错
- @Mapper:只需要在dao类上使用就行,若有多个Mapper在启动类上加MapperScan(“mapper所在的包”)就行
这里继承了MybatisPlus的BaseMapper接口,泛型内填实体类的类名
使用Mybatis进行数据层开发(CRUD)
增加数据
int insert(T t) //新增方法
编写测试案例
@SpringBootTest
class MybatisPlustTestApplicationTests {
@Autowired
private BrandDao brandDao;
/**
* 增加对象测试
*/
@Test
public void testInsert(){
Brand brand = new Brand();
brand.setBrandName("菠萝");
brand.setCompanyName("菠萝公司");
brand.setDescription("菠萝很好吃");
brand.setOrdered(100);
brand.setStatus(1);
int insert = brandDao.insert(brand);
if (insert > 0){
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}
}
}
执行成功后数据库会生成一条数据。这个测试案例中没有给id赋值,是因为MybatisPlus的id生成策略。在yml文件中配置了auto策略。意识是跟随主键id自增。需要表中有主键并设置了主键id自生成。
从以上案例可以发现,对比Mybatis,Mybatisplus简化了许多,例如无需在Dao中编写方法,也无需创建对应的xml文件。但是需知道的是,MybatisPlus是用来强化Mybati,而非替代,因此,在遇到一些MybatisPlus无法解决的场景时,也可以使用Mybatis的方法来解决。
修改数据
- 根据id修改
int updateById(T entity);
编写测试案例
/**
*根据id删除数据测试
*/
@Test
public void testDelete(){
int i = 7;
int i1 = brandDao.deleteById(i);
if(i1 > 0){
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
}
该方法是通过id来修改的,因此对象需要有id的值。
删除数据
- 根据id删除
int deleteById(Serializable id);
编写案例测试
/**
*根据id删除数据测试
*/
@Test
public void testDelete(){
int i = 7;
int i1 = brandDao.deleteById(i);
if(i1 > 0){
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
}
此方法是根据id删除,所传参数为对象的id
- 批量删除
int deleteBatchIds(Collection<? extends Serializable> idList);
编写测试案例
/**
* 批量删除,根据集合
*/
@Test
public void testDeleteArr(){
List list = new ArrayList();
list.add(13);
list.add(14);
list.add(15);
int i = brandDao.deleteBatchIds(list);
if(i > 0){
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
}
此方法根据集合删除,集合中可传入多个id值
查询数据
接下来,是最常用的查询数据
普通查询
- 查询所有
List<T> selectList(Wrapper<T> queryWrapper);
编写测试案例
/**
* 查询所有测试
*/
@Test
public void testGetALl(){
List<Brand> brands = brandDao.selectList(null);
System.out.println(brands);
}
该方法所传参数为查询条件,条件的设置后面再讲,现在先传null
- 根据id查询
T selectById(Serializable id);
编写测试案例
/**
* 根据id查询数据
*/
@Test
public void selectById(){
int i = 1;
Brand brand = brandDao.selectById(i);
System.out.println(brand);
}
分页查询
分页查询方法:
IPage<T> selectPage(IPage<T> page, Wrapper<T> queryWrapper)
- IPage:用来构建分页查询条件
- Wrapper:用来构建条件查询的条件,目前我们没有可直接传为Null
- IPage:返回值,你会发现构建分页条件和方法的返回值都是IPage
IPage是一个接口,我们需要找到它的实现类来构建它,具体的实现类,可以进入到IPage类中按ctrl+h,会找到其有一个实现类为Page
。
- 调用方法传参获取返回值
/**
* 分页查询
*/
// selectPage(IPage<T> page, Wrapper<T> queryWrapper)
@Test
public void testSelectPage(){
//1.创建IPage对象,1为当前页码,3为每页记录数
IPage<Brand> iPage = new Page<>(1,3);
//2.执行分页查询
IPage<Brand> brandIPage = brandDao.selectPage(iPage, null);
//3 获取分页结果
System.out.println("当前页码值:"+iPage.getCurrent());
System.out.println("每页显示数:"+iPage.getSize());
System.out.println("一共多少页:"+iPage.getPages());
System.out.println("一共多少条数据:"+iPage.getTotal());
System.out.println("数据:"+iPage.getRecords());
}
- 设置分页拦截器
这个拦截器MP已经为我们提供好了,我们只需要将其配置成Spring管理的bean对象即可。
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
//1 创建MybatisPlusInterceptor拦截器对象
MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
//2 添加分页拦截器
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mpInterceptor;
}
}
条件查询
MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合。
这个我们在前面都有见过,比如查询所有和分页查询的时候,都有看到过一个Wrapper
类,这个类就是用来构建查询条件的
在进行查询的时候,我们的入口是在Wrapper这个类上,因为它是一个接口,所以我们需要去找它对应的实现类,关于实现类也有很多,说明我们有多种构建查询条件对象的方式
先来看第一中:QueryWrapper
/**
* 条件查询数据(QueryWrapper)
*/
@Test
public void testGetAll(){
//1.创建QueryWrapper对象
QueryWrapper<Brand> qw = new QueryWrapper<Brand>();
//2.设置查询条件,lt()代表小于号,column:字段名称,val:值
qw.lt("ordered",100);
List list = brandDao.selectList(qw);
System.out.println(list);
}
从上面案例看,通过QueryWrapper实体类设置好查询条件后即可进行查询,这里是通过书写字段的形式来设置条件字段的,为了避免开发时字段书写错误,可以通过Lambda的方法来进行字段的条件设置。
/**
* 条件查询数据(QueryWrapper)
*/
@Test
public void testGetAll(){
//1.创建QueryWrapper对象
QueryWrapper<Brand> qw = new QueryWrapper<Brand>();
//2.设置查询条件,lt()代表小于号,column:字段名称,val:值
// qw.lt("ordered",100);
//2.2使用Lambda表达式方法设置查询条件
qw.lambda().lt(Brand::getOrdered,100);
List list = brandDao.selectList(qw);
System.out.println(list);
}
这种方法可以很好的避免字段字母输错的问题,但多加了个lambda()方法。
第二种方法:LambdaQueryWrapper()
/**
* 条件查询数据(LambdaQueryWrapper)
*/
@Test
public void testGetAll2(){
//1.创建LambdaQueryWrapper对象
LambdaQueryWrapper<Brand> lqw = new LambdaQueryWrapper<>();
//2.设置插叙条件
lqw.lt(Brand::getOrdered,100);
//3.执行查询方法
List<Brand> brands = brandDao.selectList(lqw);
System.out.println(brands);
}
LambdaQueryWrapper()支持使用Lambda格式的书写方式
注意:构建LambdaQueryWrapper的时候泛型不能省。
/**
* 各种条件查询
* * eq():等于
* * gt():大于(>)
* * ge():大于等于(>=)
* * lt():小于(<)
* * le():小于等于(<=)
* * between():between ? and ?
*/
以上都是单个条件查询,若是有多个条件该如何构建查询条件呢?
例如:我们要查询字段status大于0并且ordered字段小于100的数据
/**
* 多条件查询数据(LambdaQueryWrapper)
*/
@Test
public void testGetAll3(){
//1.创建LambdaQueryWrapper对象
LambdaQueryWrapper<Brand> lqw = new LambdaQueryWrapper<>();
//2.设置查询条件(2个条件)
lqw.gt(Brand::getStatus,0);
lqw.lt(Brand::getOrdered,100);
//3.执行查询方法
List<Brand> brands = brandDao.selectList(lqw);
System.out.println(brands);
}
可以看到,只需要编写两个条件就默认是&&的关系
多条件查询也支持链式编程
查找字段status大于0或ordered字段小于100的数据
/**
* 多条件查询数据(LambdaQueryWrapper)
* 链式编程
*/
@Test
public void testGetAll3(){
//1.创建LambdaQueryWrapper对象
LambdaQueryWrapper<Brand> lqw = new LambdaQueryWrapper<>();
//2.设置查询条件(2个条件)
lqw.gt(Brand::getStatus,0).or().lt(Brand::getOrdered,100);
//3.执行查询方法
List<Brand> brands = brandDao.selectList(lqw);
System.out.println(brands);
}
当我们在不确定前端传来的条件为几个时,可以用以下方法拼接查询条件
/**
* 动态多条件查询数据(LambdaQueryWrapper)
*/
@Test
public void testGetAll4(){
//模拟前端传来的数据
BrandDto brandDto = new BrandDto();
brandDto.setOrdered(100);
brandDto.setOrdered2(5);
LambdaQueryWrapper<Brand> lqw = new LambdaQueryWrapper<>();
//设置查询条件
if(brandDto.getOrdered() != null){
lqw.lt(Brand::getOrdered,brandDto.getOrdered());
}
if(brandDto.getOrdered2() != null){
lqw.gt(Brand::getOrdered,brandDto.getOrdered2());
}
//执行查询方法
List<Brand> brands = brandDao.selectList(lqw);
System.out.println(brands);
}
上面的案例用if判空来确定查询条件,MybatisPlus还为我们提供了一种更为简便的方法
lt(boolean condition, R column, Object val);
/**
* 动态多条件查询数据简化版(LambdaQueryWrapper)
* condition属性
*/
@Test
public void testGetAll5(){
//模拟前端传来的数据
BrandDto brandDto = new BrandDto();
brandDto.setOrdered(100);
brandDto.setOrdered2(5);
LambdaQueryWrapper<Brand> lqw = new LambdaQueryWrapper<>();
//设置查询条件
lqw.lt(brandDto.getOrdered() != null,Brand::getOrdered,brandDto.getOrdered());
lqw.gt(brandDto.getOrdered2() != null,Brand::getOrdered,brandDto.getOrdered2());
//执行查询方法
List<Brand> brands = brandDao.selectList(lqw);
System.out.println(brands);
}
此方法是原来方法的重载方法,在设置查询条件前加了一个表达式属性,当conition为true时则添加条件,为false时则不添加。
模糊查询
MybatisPlus也提供了一组模糊查询的方法
/**
* * like():前后加百分号,如 %J%
* * likeLeft():前面加百分号,如 %J
* * likeRight():后面加百分号,如 J%
*/
@Test
public void testGetAll5(){
LambdaQueryWrapper<Brand> lqw = new LambdaQueryWrapper<>();
//like()
//lqw.like(Brand::getCompanyName,"科技有限");
//likeLeft()
lqw.likeLeft(Brand::getBrandName,"米");
//likeRight()
lqw.likeRight(Brand::getCompanyName,"小");
List<Brand> brands = brandDao.selectList(lqw);
System.out.println(brands);
}
查询投影
刚才的数据,我们都是查询所有字段相当于
select * from table;
当想查询某一些字段时该使用select()
方法
/**
* 查询投影,利用select()来指定查询的字段
*/
@Test
public void testGetAll(){
LambdaQueryWrapper<Brand> lqw = new LambdaQueryWrapper<>();
lqw.select(Brand::getBrandName,Brand::getCompanyName,Brand::getDescription);
lqw.gt(Brand::getOrdered,0);
List<Brand> brands = brandDao.selectList(lqw);
System.out.println(brands);
}
-
select(…)方法用来设置查询的字段列,可以设置多个,最终的sql语句为:
SELECT id,name,age FROM teble
-
如果使用的不是lambda,就需要手动指定字段,这里要使用QueryWrapper和表中的字段名
/**
* 查询投影,利用select()来指定查询的字段
*/
@Test
public void testGetAll(){
QueryWrapper<Brand> lqw = new QueryWrapper<>();
lqw.select("brand_name","company_name","description");
lqw.gt("ordered",0);
List<Brand> brands = brandDao.selectList(lqw);
System.out.println(brands);
}
聚合函数
需求:聚合函数查询,完成count、max、min、avg、sum的使用
count:总记录数
max:最大值
min:最小值
avg:平均值
sum:求和
/**
* 聚合函数
*/
@Test
public void testGetAll2(){
QueryWrapper<Brand> lqw = new QueryWrapper<>();
lqw.select("count(*) as count");
List<Map<String, Object>> maps = brandDao.selectMaps(lqw);
System.out.println(maps);
}
分组查询
/**
* 分组查询
*/
@Test
public void testGetAll3(){
QueryWrapper<Brand> qw = new QueryWrapper<>();
qw.select("count(*) as count,ordered");
qw.groupBy("ordered");
List<Map<String, Object>> maps = brandDao.selectMaps(qw);
System.out.println(maps);
}
这个查询方法相当于sql语句:
select count(*) as count,ordered from tb_brand group by ordered;
排序查询
orderBy(boolean condition, boolean isAsc, R... columns)
/**
* condition :条件,返回boolean,
当condition为true,进行排序,如果为false,则不排序
* isAsc:是否为升序,true为升序,false为降序
* columns:需要操作的列
*/
/**
* 根据id排序查询
*/
@Test
public void testOrder(){
LambdaQueryWrapper<Brand> lqw = new LambdaQueryWrapper<>();
lqw.orderBy(true,false,Brand::getId);
List<Brand> brands = brandDao.selectList(lqw);
System.out.println(brands);
}
MybatisPlus中的Service开发
刚刚都是编写Dao层的开发,在一边的service层开发,都是写好service接口和serviceImpl实现类,再再serviceImpl实现类中注入Dao实体类,实现service接口的方法。如下:
/**
*service接口
*/
public interface UserService{
}
/**
*service实现类
*/
@Service
public class UserServiceImpl implements UserService{
}
接口和实现类有了以后,需要在接口和实现类中声明方法
public interface UserService{
public List<User> findAll();
}
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserDao userDao;
public List<User> findAll(){
return userDao.selectList(null);
}
}
MybatisPlus提供了一个Service接口和实现类,分别是:IService
和ServiceImpl
,后者是对前者的一个具体实现。具体使用方式如下:
- service接口
public interface BrandService extends IService<Brand> {
}
- serviceImpl实现类
@Service
public class BrandServiceImpl extends ServiceImpl<BrandDao, Brand> implements BrandService {
}
- 测试案例
@SpringBootTest
public class MybatisPlustTest3 {
@Autowired
private BrandService brandService;
/**
* 查询所有
*/
@Test
public void testService(){
List<Brand> list = brandService.list(null);
System.out.println(list);
}
}
修改以后的好处是,MP已经帮我们把业务层的一些基础的增删改查都已经实现了,可以直接进行使用。至于更复杂的方法,可以通过在service接口写方法并实现的办法实现。MP封装的Service层都有哪些方法可以用可以在MybatisPlus官网查看
https://www.baomidou.com/
最后再提示:MybatisPlus不是替代Mybatis,它旨在加强mybatis,使开发者在开发一些基础的方法时可以更为便捷的实现,在MP环境下也可以使用mybatis的各种方法
体使用方式如下:
- service接口
public interface BrandService extends IService<Brand> {
}
- serviceImpl实现类
@Service
public class BrandServiceImpl extends ServiceImpl<BrandDao, Brand> implements BrandService {
}
- 测试案例
@SpringBootTest
public class MybatisPlustTest3 {
@Autowired
private BrandService brandService;
/**
* 查询所有
*/
@Test
public void testService(){
List<Brand> list = brandService.list(null);
System.out.println(list);
}
}
修改以后的好处是,MP已经帮我们把业务层的一些基础的增删改查都已经实现了,可以直接进行使用。至于更复杂的方法,可以通过在service接口写方法并实现的办法实现。MP封装的Service层都有哪些方法可以用可以在MybatisPlus官网查看
https://www.baomidou.com/
最后再提示:MybatisPlus不是替代Mybatis,它旨在加强mybatis,使开发者在开发一些基础的方法时可以更为便捷的实现,在MP环境下也可以使用mybatis的各种方法