MybatisPlus–小结
详细内容请查看官方文档
一、简介
是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
框架结构
注解
名称 | 用途 |
---|---|
@TableName | 用在实体类上,描述表名。可以解决表名和实体类名不一致情况 |
@TableId | 描述主键字段上,可配置主键生成策略 |
@TableField | 表字段描述 |
@Version | 标记乐观锁字段 |
@EnumValue | 注解在枚举字段上【没用过 |
@TableLogin | 标记逻辑删除字段 |
@SqlParser | 租户注解,支持method上以及mapper接口上【没用过 |
@KeySequence | 序列主键策略 |
二、快速入门
1.先导入依赖
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
2.配置数据信息
# mysql8 需要配置时区信息 serverTimezone=UTC
spring:
datasource:
username: root
password: admin
url: jdbc:mysql:///test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
3.编写mapper继承BaseMapper
BaseMapper由mybatis-plus提供,里面提供了一些基本的增删改查操作
@Repository
public interface UserMapper extends BaseMapper<User> {
}
4.然后就可以直接注入使用了
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTest {
@Autowired
private UserMapper userMapper;
@Test
public void test(){
// 参数是一个Wrapper,条件构造器,使用null,表示查询所有
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);
}
}
三、 配置日志
配置文件中添加配置即可
# 配置日志-控制台输入
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志控制台 输出
四、CRUD扩展
4.0 主键生成策略
public enum IdType {
AUTO(0), // 主键自增 需要配置数据库主键自增
NONE(1), // 未设置主键
INPUT(2), // 手动输入,也可以使用自定义id生成器
ID_WORKER(3), // 雪花算法,默认使用这个,
UUID(4), // 全局唯一uuid
ID_WORKER_STR(5); // ID_WORKER的字符串表示法
}
4.1 Insert操作
// 插入一条记录,可配置id填充策略
int insert(T entity);
4.2 update操作
// 根据 ID 修改
int updateById(T entity);
// 根据 wrapper 条件,更新
int update(T entity, Wrapper<T> updateWrapper);
4.3 字段自动填充
- 数据库级别自动填充
- 代码级别自动填充
示例:以自动填充创建时间和更新时间为例
1)数据库级别自动填充
时间字段需要默认值,然后update_time
字段,需要勾选更新
2)代码级别自动填充
- 在实体类对应字段使用
@TableField注解
填充策略
public enum FieldFill {
// 不填充
DEFAULT,
// 插入时填充
INSERT,
// 更新时填充
UPDATE,
// 插入和更新时填充字段
INSERT_UPDATE
}
使用示例:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@TableId(type = IdType.INPUT) // 主键生成策略--自己输入
private Long id;
private String name;
private Integer age;
private String email;
// fill 自动填充策略
@TableField(fill = FieldFill.INSERT) // 插入时自动填充 createTime字段
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
- 编写实现
MetaObjectHandler
@Slf4j
@Component //放入容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
// 插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime",new Date(),metaObject);
// 插入的时候还有更新这个字段
this.setFieldValByName("updateTime",new Date(),metaObject);
}
// 更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
// 参数:字段名,插入数据,metaObject
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
4.4 乐观锁
简述:总是认为当前操作不会出现问题,只有数据进行提交更新的时,才会对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时,如果当前记录的version与我们携带的version相同的话。更新成功–>version+1、否则更新失败
悲观锁:认为当前操作总会出现问题,所以每次都上锁,防止并发。(行级锁
注意:
- 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 整数类型下
newVersion = oldVersion + 1
newVersion
会回写到entity
中- 仅支持
updateById(id)
与update(entity, wrapper)
方法 - 在
update(entity, wrapper)
方法下,wrapper
不能复用!!!
1.表添加version
字段
2.实体类添加version
字段,加上@Version
注解
@Version // 乐观锁Version字段
private Integer version;
3.编写配置文件,添加乐观锁组件
@MapperScan("top.roise.mapper")
@EnableTransactionManagement // 配置事务
@Configuration
public class MyBatisPlusConfig {
// 注册乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
return new OptimisticLockerInterceptor();
}
}
4.测试更新操作
/**
* 单线程
* 测试乐观锁
*/
@Test
public void testOptimisticLockerInterceptor(){
User user = userMapper.selectById(1);
user.setName("88pp88");
int i = userMapper.updateById(user);
System.out.println(i);
}
更新的时候会判断version
,如果和原来相同,则更新成功。version+1
模拟多线程
@Test
public void testOptimisticLockerInterceptor(){
User user = userMapper.selectById(1);
user.setName("yyy666");
// (模拟多个用户更新)测试插入 如果没有乐观锁,第二次更新会覆盖第一次更新的值
User user2 = userMapper.selectById(1);
user.setName("mmm55");
userMapper.updateById(user2);
int i = userMapper.updateById(user);
System.out.println(i);
}
4.5 查询操作
selectById(Serializable id) 根据 ID 查询 selectBatchIds(Collection ids) 根据ID 批量查询 selectByMap(Map map) 条件查询,把查询条件放map里面
// 条件查询--使用map
@Test
public void testSelectByMap(){
HashMap<String, Object> map = new HashMap<>();
// 查询条件 --会拼接到where
map.put("name","中心小学");
map.put("age", "122");
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
4.6 分页查询
1.配置分页插件
步骤:1.添加配置组件 2.
selectPage()
方法
// Mybatis-Plus 配置类中---
// 分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
// import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 分页查询
@Test
public void testFenYe(){
// 当前页 页面显示的行数
Page<User> page = new Page<>(2,5);
userMapper.selectPage(page,null);
}
4.7 删除操作
int deleteById(Serializable id); 根据 ID 删除 int deleteBatchIds(Collection<? extends Serializable> idList); 根据ID 批量删除 int deleteByMap(Map<String, Object> columnMap); 根据 columnMap 条件,删除记录 int delete(Wrapper queryWrapper); 根据wrapper条件删除
// 示例
@Test
public void testDeleted(){
// 删除单条
int i = userMapper.deleteById(90L);
System.out.println(i);
// 批量通过id删除
int i1 = userMapper.deleteBatchIds(Arrays.asList(4, 5));
System.out.println(i1);
// 通过map添加条件删除
HashMap<String, Object> map = new HashMap<>();
map.put("name","lisbai");
int i2 = userMapper.deleteByMap(map);
// wrapper条件删除
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("email","[email protected]");
userMapper.delete(wrapper);
}
五、逻辑删除
1.数据库添加逻辑删除字段deleted【int ,1位, 默认0】
2.pojo添加相应字段,加上注解@TableLogic
@TableLogic // 逻辑删除
private Integer deleted;
3.配置类添加逻辑删除组件
// 逻辑删除
@Bean
public ISqlInjector iSqlInjector(){
return new LogicSqlInjector();
}
4.配置文件添加
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
5.测试
@Test
public void testLogic(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("email","[email protected]");
userMapper.delete(wrapper);
}
六、条件构造器Wrapper
示例1:多条件查询
select x,x,x from user where name != null and eamil != null and age < 8
// 查询姓名不为空,邮箱不为空,并且age<8
@Test
public void test1(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.isNotNull("name")
.isNotNull("email")
.le("age",8L);
List<User> users = userMapper.selectList(wrapper);
}
示例2:只查询出一个对象
select x,x,x from user where name = "Tom"
// 查询一个
@Test
public void test2(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name","Tom");
User user = userMapper.selectOne(wrapper);
}
示例3:范围查询
select x,x,x from user where deleted = 0 and age between 20 and 30
// 查询年龄在20~30的人数
// SELECT COUNT(1) FROM user
// WHERE deleted=0 AND age BETWEEN 20 AND 30
@Test
public void test3(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age",20,30); // 字段+区间
Integer count = userMapper.selectCount(wrapper);
System.out.println(count);
}
示例4:模糊查询
// 模糊查询
// like --> %AA% likeRight--> AA% likeLeft--> %BB
@Test
public void test4(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("name","App") // %App% name包含9
.likeRight("email","122"); // 122% email中以122开头的
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
示例5:子查询
SELECT ... FROM user WHERE deleted=0 AND id IN (select id from user where id < 3)
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 建立子查询 要查询的id在子查询里面
wrapper.inSql("id","select id from user where id < 3");
更多用法参考官方文档