Mybatis-plus:对Mybatis做增强,简化开发
一、快速入门
1、创建user表:
DROP TABLE IF EXISTS user;
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)
);
2、加入对应数据:
DELETE FROM user;
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');
3、创建Springboot工程
4、引入相关依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--引入mybatis-plus依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
5、配置application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=UTC
username: root
password: 12341234
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl //配置日志
6、代码部分
-
创建实体类
@Data public class User { private Long id; private String name; private Integer age; private String email; }
-
创建Mapper接口,继承BaseMapper<泛型>。
在主启动类中加入@MapperScan(“全限定名”),扫描Mapper接口
7、测试
@SpringBootTest
class MpdemoApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
List<User> users = userMapper.selectList(null);
for (User user : users) {
System.out.println(user);
}
}
}
//测试结果:
User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com)
Idea在第一次启动的时候会将代码编译,启动较慢!
二、mp实现添加操作
代码操作:
@Test
public void addUser(){
User user = new User();
user.setName("Lucy"); //未设置主键值
user.setAge(18);
user.setEmail("12356@qq.com");
int insert = userMapper.insert(user);
}
通过日志查看执行结果
==> Preparing: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
==> Parameters: 1329331831785492482(Long), Lucy(String), 18(Integer), 12356@qq.com(String)
<== Updates: 1
- 未设置主键值mp自动生成19位IP值
添加操作实现主键策略
ID生成策略具体参考这篇文章:https://www.cnblogs.com/haoxinyue/p/5208136.html
-
自动增长:AUTO_INCREMENT
-
UUID:每次生成随机唯一的值
-
Redis实现
-
mp自带策略 :snowflake算法
在实体类主键ID上添加注解如下
@Data
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
}
//AUTO:自动增长
//INPUT:设置id值
//NONE:输入
//ASSGIN_UUID:随机生成值
三、mp实现修改操作
代码操作:
@Test
public void updateUser(){
User user = new User();
user.setId(2L);
user.setAge(40);
userMapper.updateById(user);
}
通过日志查看执行结果:
==> Preparing: UPDATE user SET age=? WHERE id=?
==> Parameters: 40(Integer), 2(Long)
<== Updates: 1
自动填充:
不需要set到对象里面的值,使用mp方式实现数据添加
准备工作:
1、表中添加两个字段
create_time、update_time
2、添加实体类属性
具体实现过程:
- 在需要自动填充的属性上添加注解
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
-
创建类,实现接口MetaObjectHandler,并实现方法
//注册到容器中 @Component public class MyMetaObjectHandler implements MetaObjectHandler { //使用mp实现添加方法,该方法执行 @Override public void insertFill(MetaObject metaObject) { this.setFieldValByName("createTime",new Date(),metaObject); this.setFieldValByName("updateTime",new Date(),metaObject); } //使用mp实现修改方法,该方法执行 @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("updateTime",new Date(),metaObject); } }
乐观锁:
- 乐观锁主要解决丢失更新问题!
丢失更新:多个人修改同一条数据的时候,最后一个提交事务的提交数据会覆盖前面的数据。
- 解决丢失更新主要可以用悲观锁(串行,只能单人使用)
乐观锁实现方式:
- 取出纪录时,获取当前Version
- 更新时,带上这个version
- 执行更新时,set version = new Version where version = Oldversion
- 如果version不匹配,则更新失败
案例操作:
-
在数据库中加入version字段
-
实体类添加version字段,并添加@version注解
@Version private Integer version;
-
配置乐观锁插件到spring容器
//乐观锁插件 @Bean public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor(){ return new OptimisticLockerInnerInterceptor(); }
-
用自动填充添加version初始值
this.setFieldValByName("version",1,metaObject);
测试结果:
//测试乐观锁,一定要先查再改
@Test
public void testOptimisticLocker(){
User user = userMapper.selectById(1329347964622127105L);
user.setAge(235);
userMapper.updateById(user);
}
version结果+1
四、更多查询
- 多个ID查询:List selectBatchIds(@Param(“coll”) Collection<? extends Serializable> idList);
@Test
public void testSelectDemo1(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));//传入集合
for (User user : users) {
System.out.println(user);
}
}
- 简单条件查询:
@Test
public void testSelectDemo2(){
HashMap<String,Object> map = new HashMap<>();
map.put("name","Jone");//传入键值对作为条件
map.put("age","18");
List<User> users = userMapper.selectByMap(map);
for (User user : users) {
System.out.println(user);
}
}
五、分页(重点)
- 配置分页插件
//分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor page = new PaginationInterceptor();
page.setDialectType("mysql");
return page;
}
- 编写分写代码
- 直接new page对象,传入两个参数:当前页、每页记录数
- 调用mp方法实现分页查询
@Test
public void testPage(){
//传入两个参数:当前页和每页显示记录数
Page<User> page = new Page<>(1, 3);
//调用mp分页查询方法
//调用mp分页查询过程中,底层封装
//把分页所有数据封装到page中
userMapper.selectPage(page,null);
System.out.println(page.getCurrent()); //获取当前页
System.out.println(page.getRecords()); //每页数据List集合
System.out.println(page.getSize()); //每页显示记录数
System.out.println(page.getTotal()); //总记录数
System.out.println(page.getPages()); //总页数
System.out.println(page.hasNext()); //是否有下一页
System.out.println(page.hasPrevious()); //是否有上一页
}
日志打印:
==> Preparing: SELECT COUNT(1) FROM user
==> Parameters:
<== Columns: COUNT(1)
<== Row: 7
==> Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user LIMIT ?,? //这里有limit查询
==> Parameters: 3(Long), 3(Long)
<== Columns: id, name, age, email, create_time, update_time, version
<== Row: 4, Sandy, 21, test4@baomidou.com, null, null, null
<== Row: 5, Billie, 24, test5@baomidou.com, null, null, null
<== Row: 7, Lisa, 235, 412345@qq.com, 2020-11-19 08:37:43, 2020-11-19 09:05:35, 1
<== Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@314a31b0]
2
[User{id=4, name='Sandy', age=21, email='test4@baomidou.com', createTime=null, updateTime=null, version=null}, User{id=5, name='Billie', age=24, email='test5@baomidou.com', createTime=null, updateTime=null, version=null}, User{id=7, name='Lisa', age=235, email='412345@qq.com', createTime=Thu Nov 19 16:37:43 CST 2020, updateTime=Thu Nov 19 17:05:35 CST 2020, version=1}]
3
7
3
true
true
六、逻辑删除
-
物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
//删除操作,物理删除 @Test public void testDeleteById(){ //单个删除 userMapper.deleteById(1); //批量删除 userMapper.deleteBatchIds(Arrays.asList(1,2,3)); }
-
逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态“,之后数据库能看到此条数据。
-
数据库表中添加逻辑删除字段,对应实体类添加逻辑删除属性
@TableLogic private Integer deleted;
-
配置逻辑删除插件
//逻辑删除插件,3.1.1新版本不用配置插件 @Bean public ISqlInjector sqlInjector(){ return new LogicSqlInjector(); }
-
添加配置信息
logic-delete-value: 1 logic-not-delete-value: 0 #配置不删除是0,删除是1
-
执行删除方法!
@Test public void testDeleteById(){ userMapper.deleteById(1329410275504369665L); } 日志输出: ==> Preparing: UPDATE user SET deleted=1 WHERE id=? AND deleted=0 ==> Parameters: 1329410275504369665(Long) <== Updates: 1
-
执行查询方法语句中多一个WHERE deleted=0!!!逻辑删除!!
-
七、实现复杂条件查询操作
——使用QuerryWapper构建条件查询
代码详解(注释详细)
//创建QueryWrapper对象
QueryWrapper<User> wrapper = new QueryWrapper<>();
//通过QueryWrapper设置条件
//ge(大于等于)、gt(大于)、le(小于等于)、lt(小于)
//查询大于等于30的人
wrapper.ge("age",100);//参数段名称,值
List<User> users = userMapper.selectList(wrapper);//传入wrapper
System.out.println(users);
//eq(等于)、ne(不等于)
//查询名称是Tom的人
QueryWrapper<User> eq = wrapper.eq("name", "Tom");
List<User> users = userMapper.selectList(wrapper);
System.out.println(users);
//between(查范围中的值)
//查询年龄20-100中的人
QueryWrapper<User> age = wrapper.between("age", 20, 100);
List<User> users = userMapper.selectList(wrapper);
System.out.println(users);
//like(模糊查询)
wrapper.like("name", "a" );
List<User> users = userMapper.selectList(wrapper);
System.out.println(users);
//orderByDesc(通过条件排序查询)
wrapper.orderByDesc("id");
List<User> users = userMapper.selectList(wrapper);
System.out.println(users);
//last(拼接sql语句)
wrapper.last("limit 1");
List<User> users = userMapper.selectList(wrapper);
System.out.println(users);
//select(查询指定的列)
wrapper.select("name","age");
List<User> users = userMapper.selectList(wrapper);
System.out.println(users);