Mybatis-Plus:ActiveRecord和插件应用
Posted 丿涛哥哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis-Plus:ActiveRecord和插件应用相关的知识,希望对你有一定的参考价值。
1、ActiveRecord
ActiveRecord(简称AR)⼀直⼴受动态语⾔( php 、 Ruby 等)的喜爱,⽽ Java 作为准静态语⾔,对于 ActiveRecord 往往只能感叹其优雅,所以 Mybatis-Plus 也在 AR 道路上进⾏了⼀定的探索。
什么是ActiveRecord?
ActiveRecord也属于ORM(对象关系映射)层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很⼤程度的快速实现模型的操作,⽽且简洁易懂。
ActiveRecord的主要思想是:
- 每⼀个数据库表对应创建⼀个类,类的每⼀个对象实例对应于数据库中表的⼀⾏记录;通常表的每个字段在类中都有相应的Field;
- ActiveRecord同时负责把⾃⼰持久化,在ActiveRecord中封装了对数据库的访问,即CRUD;
- ActiveRecord是⼀种领域模型(Domain Model),封装了部分业务逻辑;
1.1、开启AR模式
在MP中,开启AR⾮常简单,只需要将实体对象继承Model即可。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User extends Model<User>
@TableId(type = IdType.AUTO) //指定Id的生成策略为自增长
private Long id;
@TableField(select = true) //查询的时候,不返回该字段的值
private String name;
private String age;
@TableField(value = "email") //解决字段名不一致问题
private String mail;
@TableField(exist = false) //该字段在数据库表中不存在
private String address;
1.2、根据主键查询
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestAR
/*
在AR模式下,完成根据主键查询
*/
@Test
public void tesARSelectById()
User user = new User();
user.setId(12L);
User user1 = user.selectById();
System.out.println(user1);
1.3、新增数据
/*
在AR模式下,完成添加操作
*/
@Test
public void tesARInsert()
User user = new User();
user.setName("七喜");
user.setAge("18");
user.setMail("qixi@163.com");
boolean insert = user.insert();
System.out.println(insert);
结果:
1.4、更新操作
/*
在AR模式下,完成更新操作
*/
@Test
public void tesARUpdate()
User user = new User();
user.setId(13L);
user.setName("七喜");
user.setAge("48");
user.setMail("qixi48@163.com");
boolean insert = user.updateById();
System.out.println(insert);
1.5、删除操作
/*
在AR模式下,完成删除操作
*/
@Test
public void tesARDelete()
User user = new User();
//user.setId(13L);
boolean delete = user.deleteById(13L);
System.out.println(delete);
1.6、根据条件查询
/*
在AR模式下,根据条件进行查询
*/
@Test
public void tesARFindByWrapper()
User user = new User();
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.ge("age",18);
List<User> users = user.selectList(queryWrapper);
for (User user1 : users)
System.out.println(user1);
结果:
User(id=1, name=Jone, age=18, mail=test1@baomidou.com, address=null)
User(id=2, name=Jack, age=20, mail=test2@baomidou.com, address=null)
User(id=3, name=Tom, age=28, mail=test3@baomidou.com, address=null)
User(id=4, name=Sandy, age=21, mail=test4@baomidou.com, address=null)
User(id=5, name=Billie, age=24, mail=test5@baomidou.com, address=null)
User(id=6, name=小七1, age=18, mail=xiaoqi@163.com, address=null)
User(id=12, name=小七, age=20, mail=test, address=null)
2、插件
2.1、Mybatis中的插件机制
MyBatis 允许你在已映射语句执⾏过程中的某⼀点进⾏拦截调⽤。默认情况下,MyBatis 允许使⽤插件来拦截的⽅法调⽤包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
我们看到了可以拦截Executor接⼝的部分⽅法,⽐如update,query,commit,rollback等⽅法,还有其他接⼝的⼀些⽅法等。
总体概括为:
- 拦截执⾏器的⽅法
- 拦截参数的处理
- 拦截结果集的处理
- 拦截Sql语法构建的处理
拦截器示例:
@Intercepts(@Signature(
type= Executor.class,
method = "update",
args = MappedStatement.class,Object.class))
public class MyInterceptor implements Interceptor
@Override
public Object intercept(Invocation invocation) throws Throwable
//拦截⽅法,具体业务逻辑编写的位置
return invocation.proceed();
@Override
public Object plugin(Object target)
//创建target对象的代理对象,⽬的是将当前拦截器加⼊到该对象中
return Plugin.wrap(target, this);
@Override
public void setProperties(Properties properties)
//属性设置
注⼊到Spring容器:
/**
* ⾃定义拦截器
*/
@Bean
public MyInterceptor myInterceptor()
return new MyInterceptor();
或者通过xml配置,SqlMapConfig.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<plugin interceptor="com.lagou.mp.plugins.MyInterceptor"></plugin>
</plugins>
</configuration>
2.2、执⾏分析插件
在MP中提供了对SQL执⾏的分析的插件,可⽤作阻断全表更新、删除的操作,注意:该插件仅适⽤于开发环境,不适⽤于⽣产环境。
SpringBoot配置:
@Bean
public SqlExplainInterceptor sqlExplainInterceptor()
SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
List<ISqlParser> sqlParserList = new ArrayList<>();
// 攻击 SQL 阻断解析器、加⼊解析链
sqlParserList.add(new BlockAttackSqlParser());
sqlExplainInterceptor.setSqlParserList(sqlParserList);
return sqlExplainInterceptor;
测试:
@Test
public void testUpdate()
User user = new User();
user.setAge(20);
int result = this.userMapper.update(user, null);
System.out.println("result = " + result);
结果:
Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition
of table update operation
at com.baomidou.mybatisplus.core.toolkit.ExceptionUtils.mpe(ExceptionUtils.java:49)
at com.baomidou.mybatisplus.core.toolkit.Assert.isTrue(Assert.java:38)
at com.baomidou.mybatisplus.core.toolkit.Assert.notNull(Assert.java:72)
at
com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser.processUpdate(BlockAtta
ckSqlParser.java:45)
at
com.baomidou.mybatisplus.core.parser.AbstractJsqlParser.processParser(AbstractJsqlParse
r.java:92)
at
com.baomidou.mybatisplus.core.parser.AbstractJsqlParser.parser(AbstractJsqlParser.java:
67)
at
com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler.sqlParser(Abstract
SqlParserHandler.java:76)
at
com.baomidou.mybatisplus.extension.plugins.SqlExplainInterceptor.intercept(SqlExplainIn
terceptor.java:63)
at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61)
at com.sun.proxy.$Proxy70.update(Unknown Source)
at
org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:197)
... 41 more
可以看到,当执⾏全表更新时,会抛出异常,这样有效防⽌了⼀些误操作。
2.3、性能分析插件
性能分析拦截器,⽤于输出每条 SQL 语句及其执⾏时间,可以设置最⼤执⾏时间,超过时间会抛出异常。
该插件只⽤于开发环境,不建议⽣产环境使⽤。
javaconfig⽅式
/*
性能分析插件
*/
@Bean
public PerformanceInterceptor performanceInterceptor()
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
//设置sql语句的最大执行时长
performanceInterceptor.setMaxTime(100);
//设置sql是否格式化显示
performanceInterceptor.setFormat(true);
return performanceInterceptor;
xml⽅式
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<!-- SQL 执⾏性能分析,开发环境使⽤,线上不推荐。 maxTime 指的是 sql 最⼤执⾏时⻓ -->
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
<property name="maxTime" value="100" />
<!--SQL是否格式化 默认false-->
<property name="format" value="true" />
</plugin>
</plugins>
</configuration>
执⾏结果:
Time:11 ms - ID:com.tao.mapper.UserMapper.selectList
Execute SQL:
SELECT
id,
name,
age,
email AS mail
FROM
user
WHERE
age >= 18
可以看到,执⾏时间为11ms。如果将maxTime设置为1,那么,该操作会抛出异常。
Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: The SQL
execution time is too large, please optimize !
at com.baomidou.mybatisplus.core.toolkit.ExceptionUtils.mpe(ExceptionUtils.java:49)
at com.baomidou.mybatisplus.core.toolkit.Assert.isTrue(Assert.java:38)
2.4、乐观锁插件
2.4.1、主要适⽤场景
意图:
当要更新⼀条记录的时候,希望这条记录没有被别⼈更新
乐观锁实现⽅式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执⾏更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
2.4.2、插件配置
spring xml:
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>
spring boot:
/*
乐观锁插件
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor()
return new OptimisticLockerInterceptor();
2.4.3、注解实体字段
需要为实体字段添加@Version注解。
第⼀步,为表添加version字段,并且设置初始值为1:
ALTER TABLE `user` ADD COLUMN `version` int(10) NULL AFTER `email`;
UPDATE `user` SET `version`='1';
第⼆步,为User实体对象添加version字段,并且添加@Version注解:
@Version
private Integer version;
2.4.4、测试
测试⽤例:
@Test
public void tesARUpdate()
User user = new User();
User user1 = user.selectById(12L);
user.setId(12L);
user.setName("七喜");
user.setVersion(user1.getVersion());
boolean insert = user.updateById();
System.out.println(insert);
执⾏⽇志:
Time:4 ms - ID:com.tao.mapper.UserMapper.updateById
Execute SQL:
UPDATE
user
SET
name='七喜',
version=2
WHERE
id=12
AND version=1
true
可以看到,更新的条件中有version条件,并且更新的version为2。
如果再次执⾏,更新则不成功。这样就避免了多⼈同时更新时导致数据的不⼀致。
2.4.5、特别说明
- ⽀持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 整数类型下 newVersion = oldVersion + 1
- newVersion 会回写到 entity 中
- 仅⽀持 updateById(id) 与 update(entity, wrapper) ⽅法
- 在 update(entity, wrapper) ⽅法下, wrapper 不能复⽤!!!
以上是关于Mybatis-Plus:ActiveRecord和插件应用的主要内容,如果未能解决你的问题,请参考以下文章
Mybatis-Plus:ActiveRecord和插件应用
MyBatis-Plus——使用ActiveRecord(AR)实现CRUD