三、ActiveRecord
Active Record(活动记录),是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。
在MyBatisPlus中,AR模式即在实体类中封装了对数据库的访问,而不通过mapper类。
1、虽然操作数据库不通过mapper类,但需要编写mapper类并继承BaseMapper
public interface UserMapper extends BaseMapper<User> { }
2、实体类继承Model类,开启AR模式
@Data @AllArgsConstructor @TableName("tb_user") public class User extends Model<User> { @TableId(value = "id",type = IdType.AUTO) public Integer UserId; @TableField("username") public String userName; @TableField("sex") public String userSex; @TableField("address") public String userAddress; @TableField("account") public Integer userAccount; }
四、ActiveRecord增删改查
4.1 AR添加
@Test public void testAdd(){ User user = new User(null, "李四", "男", "北京市", 12000); user.insert(); }
==> Preparing: INSERT INTO tb_user ( username, sex, address, account ) VALUES ( ?, ?, ?, ? ) ==> Parameters: 李四(String), 男(String), 北京市(String), 12000(Integer) <== Updates: 1
4.2 AR根据id修改
@Test public void testUpdate(){ // 创建实体类对象 User user = new User(); // 设置需要更新的属性 user.setUserAccount(13000); // 设置需要修改的id user.setUserId(22); // 根据主键进行更新,没有设置的值忽略 user.updateById(); }
==> Preparing: UPDATE tb_user SET account=? WHERE id=? ==> Parameters: 13000(Integer), 22(Integer) <== Updates: 1
4.3 AR根据id查询
@Test public void testFindById(){ User user = new User(); user.setUserId(16); User user1 = user.selectById(); }
==> Preparing: SELECT id AS UserId,username,sex AS userSex,address AS userAddress,account AS userAccount FROM tb_user WHERE id=? ==> Parameters: 16(Integer) <== Columns: UserId, username, userSex, userAddress, userAccount <== Row: 16, 郑怜雪, 女, 重庆市, 555900 <== Total: 1
4.4 AR删除
@Test public void testDeleteById(){ User user = new User(); user.setUserId(21); user.deleteById(); }
==> Preparing: DELETE FROM tb_user WHERE id=? ==> Parameters: 21(Integer) <== Updates: 1
4.5 AR查询所有
@Test public void testFindAll(){ User user = new User(); List<User> users = user.selectAll(); }
==> Preparing: SELECT id AS UserId,username,sex AS userSex,address AS userAddress,account AS userAccount FROM tb_user ==> Parameters: <== Columns: UserId, username, userSex, userAddress, userAccount <== Row: 5, 翟玲娇, 女, 长沙市, 1000 <== Row: 6, 张晓, 女, 青岛市, 2500 <== Row: 12, 唐宛凝, 女, 石家庄市, 3334 <== Row: 16, 郑怜雪, 女, 重庆市, 555900 <== Row: 18, 梅川内酷, 男, 临沂市, 3000 <== Row: 19, 黎飒, 男, 深圳市, 9000 <== Row: 20, 黄林, 男, 昆明市, 5000 <== Row: 22, 李四, 男, 北京市, 13000 <== Total: 8
4.6 AR分页查询
@Test public void testFindPage(){ // 创建分页条件 Page page = new Page(0,2); // 查询构造器 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); // 查询名字包含"张"的学生,按照账户升序排序 queryWrapper.like("username","张").orderByAsc("account"); User user = new User(); // 分页查询 IPage iPage = user.selectPage(page, queryWrapper); // 打印分页数据 System.out.println("结果集:"+iPage.getRecords()); System.out.println("总页数:"+iPage.getPages()); System.out.println("总条数:"+iPage.getTotal()); System.out.println("当前页:"+iPage.getCurrent()); System.out.println("每页条数:"+iPage.getSize()); }
==> Preparing: SELECT COUNT(*) AS total FROM tb_user WHERE (username LIKE ?) ==> Parameters: %张%(String) <== Columns: total <== Row: 2 <== Total: 1 ==> Preparing: SELECT id AS UserId,username,sex AS userSex,address AS userAddress,account AS userAccount FROM tb_user WHERE (username LIKE ?) ORDER BY account ASC LIMIT ? ==> Parameters: %张%(String), 2(Long) <== Columns: UserId, username, userSex, userAddress, userAccount <== Row: 6, 张晓, 女, 青岛市, 2500 <== Row: 23, 张三, 男, 北京市, 12000 <== Total: 2
注意:AR分页查询也需要配置分页插件
五、MyBatisPlus插件
5.1 插件概述
MyBatis插件机制
MyBatis插件就是对Executor、StatementHandler、ParameterHandler、ResultSetHandler这四个接口上的方法进行拦截,利用JDK动态代理机制,为这些接口的实现类创建代理对象,在执行方法时,先去执行代理对象的方法,从而执行自己编写的拦截逻辑。
- Executor
MyBatis的内部执行器,它负责调用StatementHandler操作数据库,并把结果集通过 ResultSetHandler进行自动映射。 - StatementHandler
MyBatis直接让数据库执行sql脚本的对象。 - ParameterHandler
MyBatis实现Sql入参设置的对象。 - ResultSetHandler
MyBatis把ResultSet集合映射成POJO的接口对象。
MyBatisPlus常用插件
MyBatisPlus依据MyBatis插件机制,为我们提供了一些开发中常用的插件,我们在开发中使用即可。
常用插件:
- 自动分页: PaginationInnerInterceptor
- 防止全表更新与删除: BlockAttackInnerInterceptor
- 乐观锁: OptimisticLockerInnerInterceptor
这些插件都实现了InnerInterceptor接口
5.2 分页插件
在配置类或启动类配置分页插件,之前用过,不配置插件的话分页功能无法实现。
// 注册插件 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }
5.3 防止全表更新与删除插件
作用:分析删除/更新语句,防止小白或者恶意进行删除/更新全表操作。
注意:
- 该插件只支持 MySQL5.6.3 以上版本
- 该插件只建议在开发环境中使用,不建议在生产环境使用
插件使用:
1、在配置类或启动类配置防止全表更新与删除插件
// 注册插件 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 防止全表更新与删除插件 interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); return interceptor; }
2、测试全表删除
@Test public void testDeleteAll() { User user = new User(); user.delete(new QueryWrapper()); }
5.4 乐观锁插件
修改数据库中的数据时,为了避免同时被其他人修改,最好的办法就是对该数据进行加锁以防止并发。
锁的设计分为悲观锁和乐观锁:
- 悲观锁:悲观锁对数据被外界修改持保守态度。即在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现往往依靠数据库提供的锁机制。
- 乐观锁:乐观锁在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做
MyBatisPlus乐观锁插件实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
使用MyBatisPlus乐观锁插件:
1、注册乐观锁插件
// 注册插件 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 防止全表更新与删除插件 interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); // 乐观锁插件 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; }
2、修改实体类,添加version列并在该属性上面增加@Version
@Data @AllArgsConstructor @NoArgsConstructor @TableName("tb_user") public class User extends Model<User> { @TableId(value = "id",type = IdType.AUTO) public Integer UserId; @TableField("username") public String userName; @TableField("sex") public String userSex; @TableField("address") public String userAddress; @TableField("account") public Integer userAccount; @Version private Integer version; }
3、修改数据库表,添加一列整型version字段并设置默认值为0
4、测试修改功能
@Test public void testUpdate1() { User user = new User(); user.setUserId(18); user.setUserName("梅川丘酷"); //如果版本号和数据库一致更新成功,版本号+1,如果不一致更新失败 user.setVersion(0); user.updateById(); }
==> Preparing: UPDATE tb_user SET username=?, version=? WHERE id=? AND version=? ==> Parameters: 梅川丘酷(String), 1(Integer), 18(Integer), 0(Integer) <== Updates: 1