自动填充
时间、分页插件、逻辑删除、代码生成器
MybatisPlus MybatisPlus 是在mybatis的基础上增强的一个技术
使用 导入依赖
1 2 3 4 5 6 <!--mybatis plus依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency>
连接配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mp_demo?useUnicode=true&IntegerEncoding=utf8&useSSL=false&servertimeZone=Asia/Shanghai username: root password: root mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
ORM O是对象实体类 R是数据库表 M是把表和实体类建立映射关系 有了数据库和实体类后,我们创建一个Mapper接口:在接口上继承
1 extends BaseMapper<实体类名>
直接导入使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 @Autowired private ProductMapper productMapper; @Test public void test01 () throws Exception{ Product product = productMapper.selectById(1L ); System.out.println(product); List<Product> list = productMapper.selectList(null ); list.forEach(System.out::println); } @Test public void test02 () throws Exception{ Product product = new Product (); product.setId(13L ); product.setPname("iphone14" ); product.setPrice(2999D ); product.setNum(99 ); product.setBrand("苹果" ); productMapper.insert(product); } @Test public void test03 () throws Exception{ Product product = new Product (); product.setId(13L ); product.setPrice(1999D ); productMapper.updateById(product); } @Test public void test04 () throws Exception{ productMapper.deleteById(13L ); }
如果类名和数据库名不一致的话,需要添加注解,如果一样的话,就不用写这个注解了 1 @TableName("tbl_product")
问题:如果多个表的前缀都一样,我们需要都在实体类上写注解,有什么好的办法吗? 如果好多个表名前缀一样,都是tb_开头的,我们可以配置配置文件,这样的话,我们类名只用写后面的就可以,MybatisPlus会根据配置的前缀自动识别表名
1 2 3 4 mybatis-plus: global-config: db-config: table-prefix: tbl_
问题:每次我们都是根据表的主键id寻找数据,如果表的id和实体类的id不一致会怎么样? 会报错,找不到对应的id。假如实体类主键id名字叫id,表的主键id叫pid,那么我们就需要在实体类的主键id上添加@TableId(value=”pid”)注解来识别数据表的主键id
问题:如果我们用微服务,会有多个数据库,我们通过主键id添加查询的时候会有冲突,有什么办法解决? 正常我们是用数据库id自增,如果单体项目可以使用,但要是多个项目的话就会冲突,所以我们需要给他设置一个唯一的id 我们可以在注解中指定主键类型 @TableId(value = “id”,type = IdType.AUTO) type就是设置主键类型,默认就是使用雪花算法,生成一个唯一的id
当然,如果大部分表都要设置的话,我们也可以在配置文件中设置
1 2 3 4 5 mybatis-plus: global-config: db-config: id-type: auto table-prefix: tbl_
问题:上面是主键id不一样,要是还有其他名字不一样,有解决办法吗? 如果实体类跟数据表字段名不一致的话,我们可以添加@TableField(value=”数据表的名称”),来修改
问题:当实体类中有些特殊字段,在数据库表中没有,我们不排除会怎么样? 如果不排除,会映射不到,所以我们可以通过在特殊字段上添加@Tablefield(exist=false)注解来排除,这样的话,增删改查就不会包含该字段了
问题:每次创建用户时,都需要设置创建时间,修改,比较麻烦,有什么办法解决? 我们可以设置自动默认填充,这样就不用每次都手动填充。在需要配置的时间上面添加@TableField(fill = FieldFill.INSERT) 设置好之后我们需要指定要填充什么时间,所以这里我们需要一个配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Slf4j @Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill (MetaObject metaObject) { log.info("start insert fill ...." ); this .strictInsertFill(metaObject, "createTime" , Date.class, new Date ()); this .strictInsertFill(metaObject, "lastUpdateTime" , Date.class, new Date ()); } @Override public void updateFill (MetaObject metaObject) { log.info("start update fill ...." ); this .strictUpdateFill(metaObject, "lastUpdateTime" , Date.class, new Date ()); } }
条件查询 比如:查询 price > xx 并且 pname包含华为的产品信息
用SQL查:
1 select * from tbl_product where price> 500 and pname like '%华为%'
用MybatisPlus查:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Autowired private ProductMapper productMapper; String pname = “华为”;Double price = 500 ;QueryWrapper<实体类名> queryWrapper = new QueryWrapper <>(); queryWrapper.gt("price" ,price); queryWrapper.like("pname" ,pname); List<接收的实体类名> list = productMapper.selectList(queryWrapper); list.forEach(System.out::println);
如果前端传了空值,我们这边就会查询失败,所以要加个判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Autowired private ProductMapper productMapper; String pname = “华为”;Double price = 500 ;QueryWrapper<实体类名> queryWrapper = new QueryWrapper <>(); if (price!=null ){ queryWrapper.gt("price" ,price); } if (StrUtil.isNotEmpty(pname)){ queryWrapper.like("pname" ,pname); } List<接收的实体类名> list = productMapper.selectList(queryWrapper); list.forEach(System.out::println);
问题:一两个参数判断还行,如果参数多了,有什么校验的好办法吗? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Autowired private ProductMapper productMapper; String pname = “华为”;Double price = 500 ;QueryWrapper<实体类名> queryWrapper = new QueryWrapper <>(); queryWrapper.gt(price!=null , "price" ,price); queryWrapper.like(StrUtil.isNotEmpty(pname),"pname" ,pname); List<接收的实体类名> list = productMapper.selectList(queryWrapper); list.forEach(System.out::println);
问题:如果中间我输入数据库的名字输入错了,或者以后修改了数据库的字段,这边就识别不到了,有什么办法解决? 我们之前是用QueryWrapper来创建对象,这边还支持LambdaQueryWrapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void test03 () throws Exception { String pname = "华为" ; Double price = 500d ; LambdaQueryWrapper<Product> lambdaQueryWrapper = new LambdaQueryWrapper <>(); lambdaQueryWrapper.gt(price!=null ,Product::getPrice,price); lambdaQueryWrapper.like(StrUtil.isNotEmpty(pname),Product::getPname,pname); List<Product> list = productMapper.selectList(lambdaQueryWrapper); list.forEach(System.out::println); }
问题:怎么优化提升? 我们可以使用链式编程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public void test04 () throws Exception { String pname = "华为" ; Double price = 500d ; LambdaQueryWrapper<Product> lambdaQueryWrapper = new LambdaQueryWrapper <>(); lambdaQueryWrapper .gt(price!=null ,Product::getPrice,price) .like(StrUtil.isNotEmpty(pname),Product::getPname,pname); List<Product> list = productMapper.selectList(lambdaQueryWrapper); list.forEach(System.out::println); }
链式编程查询案例: 查询商品信息,查询条件库存 num > 25 或者
price > 3000 ,只展示 id、pname、price、num 字段
Mysql语法:
1 / / 设置查询字段 select id,pname,price,num from tbl_product where num > 25 or price > 3000
用MybatisPlus查:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void test05 () throws Exception { LambdaQueryWrapper<Product> lambdaQueryWrapper = new LambdaQueryWrapper <>(); lambdaQueryWrapper.select(Product::getId,Product::getPname,Product::getPrice,Product::getNum); lambdaQueryWrapper.gt(Product::getNum,25 ) .or() .gt(Product::getPrice,3000 ); List<Product> list = productMapper.selectList(lambdaQueryWrapper); list.forEach(System.out::println); }
问题:什么情况下用lambdaQueryWrapper,什么情况下用QueryWrapper? 如果有实体类来接收,就可以用lambdaQueryWrapper,如果没有实体类接收,就需要用到QueryWrapper
案例:查询价格 > 3000 的商品信息,展示每个品牌以及对应的库存,根据库存降序展示 mysql查询
1 2 3 4 select brand, sum (num) as num from tbl_product where price > 3000 group by brand order by num desc
用MybatisPlus查询:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void test06 () throws Exception{ QueryWrapper<Product> queryWrapper = new QueryWrapper <>(); queryWrapper .select("brand" ,"sum(num) as num" ) .gt("price" ,3000d ) .groupBy("brand" ) .orderByDesc("num" ); List<Map<String, Object>> list = productMapper.selectMaps(queryWrapper); list.forEach(System.out::println); }
条件修改 需求:将小米10S手机价格设置为3999
1 update tbl_product set price = 3999 where pname = "小米10S"
用MybatisPlus修改:
1 2 3 4 5 6 7 8 9 10 11 public void test07 () throws Exception{ UpdateWrapper<Product> updateWrapper = new UpdateWrapper <>(); updateWrapper.eq("pname" ,"小米10S" ); Product product = new Product (); product.setPrice(3999d ); productMapper.update(product,updateWrapper); }
条件删除 删除OPPO手机 用mysql删除:
1 DELETE FROM tbl_product WHERE pname = "OPPO K9";
用MybatisPlus删除:
1 2 3 4 5 6 7 @Test public void test08 () throws Exception{ QueryWrapper<Product> queryWrapper = new QueryWrapper <>(); queryWrapper.eq("pname" ,"OPPO K9" ); productMapper.delete(queryWrapper); }
问题:之前都是用PageHelper分页,MybatisPlus有分页插件吗? MybatisPlus内置了专门用于分页的插件 首先添加配置类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor (); interceptor.addInnerInterceptor(new PaginationInnerInterceptor (DbType.MYSQL)); return interceptor; } }
普通分页查询:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Autowired private ProductService productService; @Test public void test01 () throws Exception { Page<Product> page = new Page <>(1 , 5 ); productService.page(page); System.out.println(page.getTotal()); System.out.println(page.getRecords()); }
条件分页查询:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Test public void test02 () throws Exception { Page<Product> page = new Page <>(1 , 5 ); LambdaQueryWrapper<Product> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.gt(Product::getPrice,1500d ); productService.page(page,queryWrapper); System.out.println(page.getTotal()); System.out.println(page.getRecords()); }
问题:数据删除后就没有了,如果年底要统计数据,怎么办? 我们可以逻辑删除,就是把数据设置一个状态,状态为1就存在,状态为0就是删除了,
先在数据表中添加属性deleted,然后在实体类中设置deleted,然后在这个属性上面标记为逻辑删除字段,通过 @TableLogic
注解标识这是逻辑删除字段,
1 2 @TableLogic private Integer deleted;
全局配置逻辑删除 @TableLogic 只是单个表设置逻辑删除字段,如果多张表都需要配置逻辑删除,则可以做全局配置
这样只要有deleted属性都被认为是做逻辑删除
1 2 3 4 5 6 mybatis-plus: global-config: db-config: logic-delete-field: deleted # 全局逻辑删除的实体字段名 logic-delete-value: 1 # 逻辑已删除值(默认为 1 ) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0 )
Service接口 为了简化service代码编写,mybatisPlus提供了通用 Service CRUD 封装IService 接口 进一步封装 CRUD 采用 get 查询单行
remove 删除
list 查询集合
page 分页
前缀命名方式区分 Mapper
层避免混淆
分类
方法
描述
新增
boolean save(T entity)
新增,entity 实体对象
boolean saveOrUpdate(T entity)
id存在则更新记录,否插入一条记录
boolean saveBatch(Collection entityList)
插入(批量),默认一次可以保存1000条数据
修改
boolean updateById(T entity)
根据 ID 修改
boolean update(T entity,Wrapper updateWrapper)
根据 条件 修改
查询
T getById(Serializable id)
根据 ID 查询
List listByIds(Collection idList)
查询(根据ID 批量查询)
List list()
查询所有
List list(Wrapper queryWrapper)
条件查询
删除
boolean removeById(Serializable id)
根据 ID 删除
boolean removeByIds(Collection idList)
删除(根据ID 批量删除)
boolean remove(Wrapper queryWrapper)
根据条件删除
改造Service 使用Service 接口使用
接口继承 IService
实现类继承 ServiceImpl<M,T>
Service 接口 继承 IService 接口,泛型 T 是实体类类型,M是DAO接口
每次创建用户,都要设置创建时间,修改时间,怎么才能不用每次设置?
值
描述
DEFAULT
默认不处理
INSERT
插入时填充字段
UPDATE
更新时填充字段
INSERT_UPDATE
插入和更新时填充字段
在实体类中字段上面设置
1 2 3 4 5 @TableField(fill = FieldFill.INSERT) private Date createTime;@TableField(fill = FieldFill.INSERT_UPDATE) private Date lastUpdateTime;****
配置类设置填充值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Slf4j @Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill (MetaObject metaObject) { log.info("start insert fill ...." ); this .strictInsertFill(metaObject, "createTime" , Date.class, new Date ()); this .strictInsertFill(metaObject, "lastUpdateTime" , Date.class, new Date ()); } @Override public void updateFill (MetaObject metaObject) { log.info("start update fill ...." ); this .strictUpdateFill(metaObject, "lastUpdateTime" , Date.class, new Date ()); } }
分页插件 添加分页插件拦截器 1 2 3 4 5 6 7 8 9 10 11 12 13 @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor (); interceptor.addInnerInterceptor(new PaginationInnerInterceptor (DbType.MYSQL)); return interceptor; } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @SpringBootTest public class PageTest { @Autowired private ProductService productService; @Test public void test01 () throws Exception { Page<Product> page = new Page <>(1 , 5 ); productService.page(page); System.out.println(page.getTotal()); System.out.println(page.getRecords()); } @Test public void test02 () throws Exception { Page<Product> page = new Page <>(1 , 5 ); LambdaQueryWrapper<Product> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.gt(Product::getPrice,1500d ); productService.page(page,queryWrapper); System.out.println(page.getTotal()); System.out.println(page.getRecords()); } }
代码生成器 安装插件 配置数据源 代码生成