欢迎关注微信公众号,需要完整项目在公众号回复MP即可
http://mp.baomidou.com
2、特点MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
3、支持数据库mysql 、mariadb 、oracle 、db2 、h2 、hsql 、sqlite 、postgresql 、sqlserver 、presto 、Gauss 、Firebird
Phoenix 、clickhouse 、Sybase ASE 、 Oceanbase 、达梦数据库 、虚谷数据库 、人大金仓数据库 、南大通用数据库
4、框架结构 二、快速入门 1、创建数据库创建数据库:mybatis_plus
CREATE TABLE user ( id BIGINT(20) NOT NULL COMMENT '主键ID', name VARCHAr(30) NULL DEFAULT NULL COMMENT '姓名', age INT(11) NULL DEFAULT NULL COMMENT '年龄', email VARCHAr(50) NULL DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (id) ); INSERT INTO user (id, name, age, email) VALUES (1, 'Jone', 18, 'test1@baomidou.com'), (2, 'Jack', 20, 'test2@baomidou.com'), (3, 'Tom', 28, 'test3@baomidou.com'), (4, 'Sandy', 21, 'test4@baomidou.com'), (5, 'Billie', 24, 'test5@baomidou.com');2、创建spring boot工程 3、引入依赖
4、修改配置文件org.springframework.boot spring-boot-starter com.baomidou mybatis-plus-boot-starter 3.4.1 mysql mysql-connector-java runtime org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine
在 application.properties 配置文件中添加 MySQL 数据库的相关配置 :
#mysql数据库连接 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=123456 #mybatis日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
**注意:**如果定义了mysql驱动的依赖的版本为5,例如
5.1.47
则数据库连接配置为
#mysql数据库连接 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=true spring.datasource.username=root spring.datasource.password=123456 #mybatis日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl5、创建实体类
创建包 entity,编写实体类 User.java,并使用 lombok 简化实体类的编写
package com.atguigu.mybatisplus.entity;
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
编译结果
创建包 mapper ,编写Mapper接口:UserMapper.java
package com.atguigu.mybatisplus.mapper; public interface UserMapper extends baseMapper7、启动类添加注解{ }
在spring boot启动类中添加@MapperScan注解,扫描Mapper文件夹,只会扫描包中的接口,不会扫描类。
package com.atguigu.mybatisplus;
@SpringBootApplication
@MapperScan("com.atguigu.mybatisplus.mapper")
public class MybatisPlusApplication {
......
}
8、测试
使用测试类,进行功能测试
package com.atguigu.mybatisplus;
@SpringBootTest
class MybatisPlusApplicationTests {
//@Autowired //默认按类型装配。是spring的注解
@Resource //默认按名称装配,找不到与名称匹配的bean,则按照类型装配。是J2EE的注解
private UserMapper userMapper;
@Test
void testSelectList() {
//selectList()方法的参数:封装了查询条件
//null:无任何查询条件
List users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
sql日志输出
#mybatis日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl三、通用Mapper
MP中的基本CRUD在内置的baseMapper中都已得到了实现
使用已经创建好的测试类
//@Autowired //spring
@SpringBootTest//J2EE
class MybatisPlusApplicationTests {
@Resource
private UserMapper userMapper;
@Test
void testInsert(){
User user = new User();
user.setName("aojiaoge");
user.setAge(18);
user.setEmail("222226@qq.com");
int result= userMapper.insert(user);
System.out.println("结果:"+result);
System.out.println(user.getId());
}
@Test
void testSelect(){
//id查询
//id列表查询
//条件查询
Map map = new HashMap<>();
map.put("name","Tom");
List users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
@Test
void testUpdate(){
User user = new User();
user.setId(1l);
user.setAge(100);
//注意:update时生成的sql是动态的
int i = userMapper.updateById(user);
System.out.println("结果:"+i);
User user1 = userMapper.selectById(1);
System.out.println(user1);
}
@Test
void testDelete(){
Map objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("name","aojiaoge");
int i = userMapper.deleteByMap(objectObjectHashMap);
System.out.println("结果:"+i);
}
}
四、通用Service
MP中有一个接口IService和其实现类ServiceImpl,封装了常见的业务逻辑
1、创建Service接口创建service包,创建UserService,继承IService
package com.atguigu.mybatisplus.service; public interface UserService extends IService2、创建Service实现类{ }
创建impl包,创建UserServiceImpl,继承ServiceImpl,实现UserService
package com.atguigu.mybatisplus.service.impl; @Service public class UserServiceImpl extends ServiceImpl3、测试类implements UserService { }
@SpringBootTest//J2EE
class MybatisPlusApplicationTests {
@Resource
private UserService userService;
@Test
void testCount(){
int count = userService.count();
System.out.println(count);
}
@Test
void testSaveBach(){
ArrayList users = new ArrayList<>();
for (int i = 0; i < 6; i++) {
User user = new User();
user.setName("aojiaoge"+i);
user.setAge(18+i);
users.add(user);
}
userService.saveBatch(users);
}
}
五、自定义Mapper
当通用Mapper无法满足我们的需求时,我们可以自定义基于Mapper接口的xml文件,并在xml文件中配置SQL语句
1、接口方法定义在UserMapper接口中定义如下方法:
List2、创建XML文件selectAllByName(String name);
在resources目录中创建mapper目录,创建UserMapper.xml
uid as id, username as name, age,email, is_deleted as deleted, create_time as updateTime, update_time as updateTime
注意:MP中mapper目录是持久层映射文件的默认目录,如果是其他目录,需要配置mapper-locations,例如:
mybatis-plus.mapper-locations=classpath:xml
@Override
public void insertFill(metaObject metaObject) {
log.info("start insert fill....");
boolean hasCreateTime = metaObject.hasSetter("createTime");
if (hasCreateTime) {
log.info("start insert createTime fill....");
this.strictInsertFill(metaObject,"createTime", LocalDateTime.class,LocalDateTime.now());
}
boolean hasUpdateTime = metaObject.hasSetter("updateTime");
if (hasUpdateTime) {
log.info("start insert updateTime fill....");
this.strictInsertFill(metaObject,"updateTime", LocalDateTime.class,LocalDateTime.now());
}
@Override
public void updateFill(metaObject metaObject) {
log.info("start update fill....");
boolean hasUpdateTime = metaObject.hasSetter("updateTime");
if (hasUpdateTime){
log.info("start update updateTime fill....");
this.strictInsertFill(metaObject,"updateTime", LocalDateTime.class,LocalDateTime.now());
}
}
}
用户明确定义了属性值,则无需自动填充,否则使用自动填充
@TableField(fill = FieldFill.INSERT) private Integer age;
Object isNull = this.getFieldValByName("age",metaObject);
if (isNull == null){
log.info("start insert age fill...");
this.strictInsertFill(metaObject,"age",Integer.class,0);
}
4、TableLogic
(1)、逻辑删除
物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录 使用场景:可以进行数据恢复
*step1:*数据库中创建逻辑删除状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tfop4qGt-1639215263654)(D:graduation_project毕业设计公众号1数据库逻辑删除.png)]
*step2:*实体类中添加逻辑删除属性
@TableLogic @TableField(value = "is_deleted") private Boolean deleted;//逻辑删除 1 # 逻辑已删除值(默认为 1/true) 0 # 逻辑未删除值(默认为 0/false)(3)、测试
测试删除:删除功能被转变为更新功能
-- 实际执行的SQL update user set is_deleted=1 where id = 1 and is_deleted=0
测试查询:被逻辑删除的数据默认不会被查询
-- 实际执行的SQL select id,name,is_deleted from user where is_deleted=0八、插件 1、分页插件
MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能
(1)、添加配置类创建config包,创建MybatisPlusConfig类
@Configuration
@MapperScan("com.rongyibao.mybatisplus.mapper")//可以将主类中的注解移到此处
public class MybatisPlusConfig {}
(2)、添加分页插件
配置类中添加@Bean配置
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//这里更换为我们使用的MySQL数据库
return interceptor;
}
(3)、测试分页
@Resource
private UserMapper userMapper;
@Test
void testConfig(){
Page userPage = new Page<>(2, 5);//页码,每页记录数
userMapper.selectPage(userPage, null);
List records = userPage.getRecords();//两行返回是同一类型所以直接直接使用
records.forEach(System.out::println);
long total = userPage.getTotal();
System.out.println("count:"+total);
boolean b1 = userPage.hasPrevious();
System.out.println("上一页?"+b1);
boolean b = userPage.hasNext();
System.out.println("下一页?"+b);
}
2、XML自定义分页
(1)、UserMapper中定义接口方法
IPage(2)、定义XMLselectPageByPage(Page> page, Integer age);
(3)、测试
@Test
public void testSelectPageVo(){
Page pageParam = new Page<>(1,5);
userMapper.selectPageByPage(pageParam, 18);
List users = pageParam.getRecords();
users.forEach(System.out::println);
}
3、乐观锁
(1)、场景
一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太高,可能会影响销量。又通知小王,你把商品价格降低30元。
此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就完全被小王的覆盖了。
现在商品价格是70元,比成本价低10元。几分钟后,这个商品很快出售了1千多件商品,老板亏1万多。
接下来将我们演示这一过程:
*step1:*数据库中增加商品表
CREATE TABLE product
(
id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
price INT(11) DEFAULT 0 COMMENT '价格',
version INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id)
);
INSERT INTO product (id, NAME, price) VALUES (1, '笔记本', 100);
*step2:*创建实体类
package com.atguigu.mybatisplus.entity;
@Data
public class Product {
private Long id;
private String name;
private Integer price;
private Integer version;
}
step3:创建Mapper及对应的xml
public interface ProductMapper extends baseMapper{}
*step4:*测试
@Test
public void testConcurrentUpdate() {
//1、小李
Product p1 = productMapper.selectById(1L);
//2、小王
Product p2 = productMapper.selectById(1L);
//3、小李将价格加了50元,存入了数据库
p1.setPrice(p1.getPrice() + 50);
int result1 = productMapper.updateById(p1);
System.out.println("小李修改结果:" + result1);
//4、小王将商品减了30元,存入了数据库
p2.setPrice(p2.getPrice() - 30);
int result2 = productMapper.updateById(p2);
System.out.println("小王修改结果:" + result2);
//最后的结果
Product p3 = productMapper.selectById(1L);
System.out.println("最后的结果:" + p3.getPrice());
}
(2)、乐观锁方案
数据库中添加version字段:取出记录时,获取当前version
SELECT id,`name`,price,`version` FROM product WHERe id=1
更新时,version + 1,如果where语句中的version版本不对,则更新失败
UPDATe product SET price=price+50, `version`=`version` + 1 WHERe id=1 AND `version`=1(3)、乐观锁实现流程
*step1:*修改实体类
添加@version注解
@Version private Integer version;
*step2:*添加乐观锁插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//这里更换为我们使用的MySQL数据库
//乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());//乐观锁
return interceptor;
}
(4)、优化流程
失败后重试
@SpringBootTest//J2EE
public class TestProduct {
@Resource
private ProductMapper productMapper;
@Test
void testMP(){
//取数据
Product xiaoli = productMapper.selectById(1l);
Product xiaowang = productMapper.selectById(1l);
//修改数据
xiaoli.setPrice(xiaoli.getPrice() + 50);
int i = productMapper.updateById(xiaoli);
System.out.println("小李 "+i);
if (i==0){
//错误重试
System.out.println("小李重新修改");
//取数据
Product xiaoli1 = productMapper.selectById(1l);
xiaoli1.setPrice(xiaoli.getPrice() + 50);
int i1 = productMapper.updateById(xiaoli);
System.out.println("小李 "+i1);
}
xiaowang.setPrice(xiaowang.getPrice() - 30);
int i1 = productMapper.updateById(xiaowang);
System.out.println("小王 "+i1);
if (i1==0){
//错误重试
System.out.println("小王重新修改");
//取数据
Product xiaowang1 = productMapper.selectById(1l);
xiaowang1.setPrice(xiaowang1.getPrice() - 30);
int i2 = productMapper.updateById(xiaowang1);
System.out.println("小王 "+i2);
}
Product product = productMapper.selectById(1l);
System.out.println(product);
}
}
九、条件结构器
1、Wapper介绍
(1)、wapper家族
在MP中我们可以使用通用Mapper(baseMapper)实现基本查询,也可以使用自定义Mapper(自定义XML)来实现更高级的查询。当然你也可以结合条件构造器来方便的实现更多的高级查询。
Wrapper : 条件构造抽象类,最顶端父类
AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
QueryWrapper : 查询条件封装
UpdateWrapper : Update 条件封装
AbstractLambdaWrapper : 使用Lambda 语法
LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
LambdaUpdateWrapper : Lambda 更新封装Wrapper
@SpringBootTest
public class TestWrapper {
@Resource
private UserMapper userMapper;
private QueryWrapper userQueryWrapper = new QueryWrapper<>();
}
2、QueryWrapper
(1)、例1:组装查询条件
@Test
void test1(){
userQueryWrapper
.like("username","a")
.between("age",10,20)
.isNotNull("email");
List users = userMapper.selectList(userQueryWrapper);
users.forEach(System.out::println);
}
(2)、例2:组装排序条件
@Test
void test2(){
userQueryWrapper
.orderByDesc("age").orderByAsc("id");
List users = userMapper.selectList(userQueryWrapper);
users.forEach(System.out::println);
}
(3)、例3:组装删除条件
@Test
void test3(){
userQueryWrapper.isNull("email");
int delete = userMapper.delete(userQueryWrapper);
System.out.println(delete);
}
(4)、例4:条件的优先级
@Test
void test4(){
userQueryWrapper
.like("username","a")
.and(i -> i.lt("age",18).or().isNull("email"));
List users = userMapper.selectList(userQueryWrapper);
users.forEach(System.out::println);
User user = new User();
user.setAge(18);
user.setEmail("user@atguigu.com");
int update = userMapper.update(user, userQueryWrapper);
System.out.println(update);
}
(5)、例5:组装select子句
@Test
void test5(){
userQueryWrapper.select("username","age");
List
(6)、例6:实现子查询
@Test
public void test6() {
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id", "select id from user where id <= 3");
//selectObjs的使用场景:只返回一列
List
但是上面方式容易发生sql注入
@Test
void test6(){
userQueryWrapper.in("uid",1,2,3);
List
3、UpdateWrapper
(1)、例7:需求同例4
@Test
void test7(){
UpdateWrapper userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.set("age",18)
.set("email","user@atguigu.com")
.like("username","n")
.and(i -> i.lt("age",18).or().isNull("email"));
User user = new User();
//这里必须要创建User对象,否则无法应用自动填充。如果没有自动填充,可以设置为null
int update = userMapper.update(user, userUpdateWrapper);
System.out.println(update);
}
4、condition
(1)、例8:动态组装查询条件
@Test
void test8(){
//模拟用户输入
String name = null;
Integer ageStart = 10;
Integer ageEnd = 20;
userQueryWrapper
.like(StringUtils.isNotBlank(name),"username",name)
.ge(ageStart != null,"age",ageStart)
.le(ageEnd != null,"age",ageEnd);
List users = userMapper.selectList(userQueryWrapper);
users.forEach(System.out::println);
}
5、LambdaXxxWrapper
(1)、例9:Query - 需求同例8
@Test
void test9(){
LambdaQueryWrapper userLambdaQueryWrapper = new LambdaQueryWrapper<>();
String name = null;
Integer ageStart = 10;
Integer ageEnd = 20;
userLambdaQueryWrapper
.like(StringUtils.isNotBlank(name),User::getName,name)
.ge(ageStart != null,User::getAge,ageStart)
.le(ageEnd != null,User::getAge,ageEnd);
List users = userMapper.selectList(userLambdaQueryWrapper);
users.forEach(System.out::println);
}
(2)、例10:Update - 需求同例4
@Test
void test10(){
LambdaQueryWrapper userLambdaQueryWrapper = new LambdaQueryWrapper<>();
userLambdaQueryWrapper
.like(User::getName,"a")
.and(i -> i.lt(User::getAge,18).or().isNull(User::getEmail));
List users = userMapper.selectList(userLambdaQueryWrapper);
users.forEach(System.out::println);
User user = new User();
user.setAge(18);
user.setEmail("user@atguigu.com");
int update = userMapper.update(user, userLambdaQueryWrapper);
System.out.println(update);
}
userLambdaQueryWrapper = new LambdaQueryWrapper<>();
String name = null;
Integer ageStart = 10;
Integer ageEnd = 20;
userLambdaQueryWrapper
.like(StringUtils.isNotBlank(name),User::getName,name)
.ge(ageStart != null,User::getAge,ageStart)
.le(ageEnd != null,User::getAge,ageEnd);
List users = userMapper.selectList(userLambdaQueryWrapper);
users.forEach(System.out::println);
}
(2)、例10:Update - 需求同例4
```java
@Test
void test10(){
LambdaQueryWrapper userLambdaQueryWrapper = new LambdaQueryWrapper<>();
userLambdaQueryWrapper
.like(User::getName,"a")
.and(i -> i.lt(User::getAge,18).or().isNull(User::getEmail));
List users = userMapper.selectList(userLambdaQueryWrapper);
users.forEach(System.out::println);
User user = new User();
user.setAge(18);
user.setEmail("user@atguigu.com");
int update = userMapper.update(user, userLambdaQueryWrapper);
System.out.println(update);
}
项目完整结构



