Mybatis-plus
1. 快速入门
1.建数据库
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. 导入依赖
<dependencies>
<!-- 数据库 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3. 连接数据库
spring.datasource.username=root
spring.datasource.password=zy20000229ymy.
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
4. 传统的 pojo-dao(连接mybatis,配置mapper.xml文件)-service-controller
4. 使用了Mybatis-plus之后
-
pojo
@Data @AllArgsConstructor @NoArgsConstructor public class User { private Long id; private String name; private Integer age; private String email; }
-
mapper接口
@Repository //持久层 public interface UserMapper extends BaseMapper<User> { //所有的CRUD操作都已编写完成 //不需要像之前一样配置很多东西 }
-
注意点:我们需要在主动类上去扫描mapper包下的所有接口
//扫描 mapper 文件夹 @MapperScan("com.zhang.mapper") @SpringBootApplication public class MybatisPlusApplication { public static void main(String[] args) { SpringApplication.run(MybatisPlusApplication.class, args); } }
-
使用
@SpringBootTest class MybatisPlusApplicationTests { @Autowired private UserMapper userMapper; @Test void contextLoads() { //查询全部用户, 参数是一个Wrapper,条件是参数构造器 List<User> userList = userMapper.selectList(null); userList.forEach(System.out::println); } }
2. 配置日志
我们的sql现在是不可见的,我们希望知道它是怎么执行的,必须看日志
# 配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
3. CRUD扩展
1. Insert
@Test
public void testInsert(){
User user = new User();
user.setName("张三");
user.setAge(20);
user.setEmail("zy12311321321@163.com");
int insert = userMapper.insert(user); //帮我们自动生成id 用了雪花算法
System.out.println(insert); // 受影响的行数
System.out.println(user); // id会自动回填
}
数据库插入的id默认值为:全局的唯一id
主键生成策略
默认ID_WORKER 全局唯一id
雪花算法
主键自增
我们要实现主键自增:
- 实体类字段上
@TableId(type = IdType.AUTO)
- 数据库字段一定要是自增
2. update
@Test
public void testUpdate(){
//通过条件自动拼接动态SQL
User user = new User();
user.setId(3L);
user.setName("真的很好呀");
//注意:updateById 参数是一个对象!!!
int i = userMapper.updateById(user);
System.out.println(i);
}
自动填充
创建时间,修改时间!!!
方式一:数据库级别
- 在表中新增字段create_time,update_time
- 再次测试插入方法,我们需要先把实体类同步
- 更新查看结果就出来了,时间会自己添上的。
方式二:代码级别
-
pojo 字段上加注解
//字段添加填充内容 @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
-
编写处理器来处理这个注解
@Slf4j @Component //一定不要忘记把处理器加到IOC容器中 public class MyMetaObjectHandler implements MetaObjectHandler { //插入时的填充策略 @Override public void insertFill(MetaObject metaObject) { log.info("Start insert fill...."); this.setFieldValByName("createTime",new Date(),metaObject); this.setFieldValByName("updateTime",new Date(),metaObject); } //更新时填充策略 @Override public void updateFill(MetaObject metaObject) { log.info("Start insert fill...."); this.setFieldValByName("updateTime",new Date(),metaObject); } }
-
测试插入
-
测试更新,观察时间即可
乐观锁
测试理解
乐观锁:总是认为不会出现问题,无论干什么都不去上锁,如果出现问题,再次更新值测试
悲观锁:总是认为会出问题,无论干什么都会去上锁,再去操作。
乐观锁的实现方式:
- 取出记录时,获取当前 version
- 更新时,带上这个 version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果 version 不对,就更新失败。
测试一下MP的乐观锁插件
-
给数据库增加version字段
-
我们实体类加对应字段
@Version private Integer version;
-
注册组件
//扫描 mapper 文件夹 @MapperScan("com.zhang.mapper") @EnableTransactionManagement @Configuration public class MyBatisPlusConfig { //注册乐观锁 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } }
-
测试
//测试乐观锁成功(单线程) @Test public void testOptimisticLocker(){ //1. 查询用户信息 User user = userMapper.selectById(1L); //2. 修改用户信息 user.setName("李四"); user.setAge(11); //3. 执行更新操作 userMapper.updateById(user); }
3. 查询操作
//测试批量查询
@Test
public void testSelectByBatchId(){
List<User> userList = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
userList.forEach(System.out::println);
}
// 按条件查询之一使用map操作(同一条数据的条件)
@Test
public void testSelectByBachIds(){
HashMap<String, Object> map = new HashMap<>();
//自定义要查询的条件
map.put("name","张三");
map.put("age",22);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
分页查询
-
分页插件
//分页插件 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor2(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2)); return interceptor; }
-
测试
//分页 @Test public void testPage(){ //参数一:当前页 参数二:页面大小 Page<User> page = new Page<>(1,2); userMapper.selectPage(page,null); page.getRecords().forEach(System.out::println); System.out.println(page.getTotal()); }
-
数据库操作
日志: Preparing: SELECT id,name,age,email,version,deleted,create_time,update_time FROM user WHERE deleted=0 LIMIT ? Parameters: 2(Long)
4. 删除操作
基本的删除操作
//测试删除
@Test
public void testDeleteById(){
userMapper.deleteById(11);
}
//通过id批量删除
@Test
public void testDeleteBatchId(){
userMapper.deleteBatchIds(Arrays.asList(8,9));
}
//通过map删除
@Test
public void testDeleteMap(){
Map<String, Object> map = new HashMap<>();
map.put("name","张三");
userMapper.deleteByMap(map);
}
逻辑删除
物理删除:从数据库中直接移除
逻辑删除:数据库中没有移除,而是通过一个变量让它失效。 deleted=0 ==>deleted=1
管理员可以查看被删除的记录,防止数据丢失,类似于回收站
测试:
-
在数据库中添加deleted字段
-
实体类中增加属性
@TableLogic //逻辑删除 private Integer deleted;
-
逻辑删除配置(application.yml/properties)
mybatis-plus: global-config: db-config: logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2) logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
-
测试删除数据
@Test public void testDeleteById(){ userMapper.deleteById(1); }
走的逻辑
UPDATE user SET deleted=1 WHERE id=? AND deleted=0
-
通过查询语句查询 发现查不出来
4. 条件构造器Wrapper
1. 查询name,邮箱不为空的用户,年龄大于等于21
@Test
public void selectWp(){
//查询name,邮箱不为空的用户,年龄大于等于21
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name")
.isNotNull("email")
.ge("age",21);
userMapper.selectList(wrapper).forEach(System.out::println);
}
日志:
Preparing:
SELECT id,name,age,email,version,deleted,create_time,update_time
FROM user
WHERE deleted=0 AND (name IS NOT NULL AND email IS NOT NULL AND age >= ?)
Parameters: 21(Integer)
2. 查询名字Sandy
@Test
public void test2(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name","Sandy");
User user = userMapper.selectOne(wrapper); //查询一个数据,出现多个结果使用List,或者Map
System.out.println(user);
}
日志:
Preparing:
SELECT id,name,age,email,version,deleted,create_time,update_time
FROM user
WHERE deleted=0 AND (name = ?)
Parameters: Sandy(String)
3. 查询年龄在20~30之间的用户数量
@Test
public void test3(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age",20,30);
Long count = userMapper.selectCount(wrapper);
System.out.println(count);
}
日志:
Preparing:
SELECT COUNT( * ) AS total
FROM user
WHERE deleted=0 AND (age BETWEEN ? AND ?)
Parameters: 20(Integer), 30(Integer)
4. 模糊查询
@Test
public void test4(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.notLike("name","a")
.likeRight("email","t");
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
日志:
Preparing:
SELECT id,name,age,email,version,deleted,create_time,update_time
FROM user
WHERE deleted=0
AND (name NOT LIKE ? AND email LIKE ?)
Parameters: %a%(String), t%(String)
5. 通过id进行排序
@Test
public void test5(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("id");
List<User> userList = userMapper.selectList(wrapper);
userList.forEach(System.out::println);
}
日志:
Preparing:
SELECT id,name,age,email,version,deleted,create_time,update_time
FROM user
WHERE deleted=0
ORDER BY id DESC
6. 子查询
@Test
public void test6(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
// id在子查询中查出来
wrapper.inSql("id","select id from user where id < 3");
List<User> userList = userMapper.selectList(wrapper);
userList.forEach(System.out::println);
}
日志:
Preparing:
SELECT id,name,age,email,version,deleted,create_time,update_time
FROM user
WHERE deleted=0
AND (id IN (select id from user where id < 3))