MyBatis插件机制
# 1. MyBatis插件介绍
MyBatis作为一个应用广泛的优秀的ORM开源框架,这个框架具有强大的灵活性,在四大组件(Executor、StatementHandler、ParameterHandler、ResultsetHandler)处提供了简单易用的插件扩展机制。Mybatis对持久层的操作就是借助于四大核心对象。Mybatis支持用插件对四大核心对象进行拦截,对于Mybatis来说插件就是拦截器,用了增强核心对象的功能,增强功能本质上是借助于底层动态代理实现的,换句话说,MyBatis中的四大对象都是代理对象。

通过官网 (opens new window)可知,Mybatis插件机制允许拦截四大对象的方法如下:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
# 2. 自定义MyBatis插件
在 MyBatis 中开发插件,需要实现 Interceptor 接口,接口的定义如下:
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- intercept(Invocation invocation)方法:它将直接覆盖你所拦截对象原有的方法,因此它是插件的核心方法。通过 invocation 参数可以反射调度原来对象的方法。
- plugin(Object target)方法:target 是被拦截对象,它的作用是给被拦截对象生成一个代理对象,并返回它。为了方便MyBatis 使用 org.apache.ibatis.plugin.Plugin 中的 wrap 静态方法提供生成代理对象。
- setProperties(Properties properties)方法:允许在 plugin 元素中配置所需参数,方法在插件初始化的时候就被调用了一次,然后把插件对象存入到配置中,以便后面再取出。
我们现在来实现这个接口,对Executor对象的query方法进行拦截:
//注意看这个大花括号,也就这说这里可以定义多个@Signature对多个地方拦截,都用这个拦截器
@Intercepts({
@Signature(type = Executor.class,//这是指拦截哪个接口
method = "query",//这个接口内的哪个方法名,不要拼错了
// 这是拦截的方法的入参,按顺序写到这,不要多也不要少,如果方法重载,可是要通过方法名和入参来确定唯一的
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class ExamplePlugin implements Interceptor {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
//这里是每次执行操作的时候,都会进行这个拦截器的方法内
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("------------------intercept方法执行开始--------------------");
System.out.println("invocation.getTarget:"+invocation.getTarget().getClass().getName());
for (Object arg : invocation.getArgs()) {
System.out.println("参数:"+arg);
}
System.out.println("invocation.getMethod:"+invocation.getMethod().getName());
return invocation.proceed();
}
//主要是为了把这个拦截器生成一个代理放到拦截器链中
@Override
public Object plugin(Object target) {
System.out.println("------------------plugin方法执行开始--------------------");
System.out.println("将要包装的目标对象:"+target);
return Plugin.wrap(target,this);
}
//插件初始化的时候调用,也只调用一次,插件配置的属性从这里设置进来
@Override
public void setProperties(Properties properties) {
System.out.println("------------------setProperties方法执行开始--------------------");
System.out.println("插件配置的初始化参数:" + properties);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
自定义MyBatis插件必须加上@Intercepts注解,在@Intercepts内部可以定义多个@Signature对多个地方拦截。
配置SqlMapConfig.xmll文件:
<plugins>
<plugin interceptor="com.hukai.demo.plugin.ExamplePlugin">
<property name="name" value="hukai"/>
</plugin>
</plugins>
1
2
3
4
5
2
3
4
5
这样Mybatis在启动时可以加载插件,并在调用相应方法时进行拦截,我们来看看效果:



# 3. 常用插件介绍
# 3.1 PageHelper分页插件
MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据。
开发步骤:
- 导入通用PageHelper的坐标
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 在mybatis核心配置文件中配置PageHelper插件
<!--注意:分页助手的插件 配置在通用馆mapper之前-->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!--指定方言 -->
<property name="dialect" value="mysql"/>
</plugin>
1
2
3
4
5
2
3
4
5
- 测试分页代码实现
@Test
public void testFindAll() {
Page pageInfo = PageHelper.startPage(1, 10);
List<Emp> empList = empMapper.findAll();
empList.forEach(System.out::println);
// System.out.println("总条数:"+pageInfo.getTotal());
// System.out.println("总页数:"+pageInfo. getPages ());
// System.out.println("当前页:"+pageInfo. getPageNum());
// System.out.println("每页显示长度:"+pageInfo.getPageSize());
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 3.2 通用Mapper
通用Mapper就是为了解决单表增删改查,基于Mybatis的插件机制。开发人员不需要编写SQL,不需要在Mapper接口中增加方法,只要写好实体类,就能支持相应的增删改查方法。
开发步骤:
- 首先在maven项目,在pom.xml中引入mapper的依赖。
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>3.1.2</version>
</dependency>
1
2
3
4
5
2
3
4
5
- Mybatis配置文件中完成配置
<plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
<!-- 通用Mapper接口,多个通用接口用逗号隔开 -->
<property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
</plugin>
1
2
3
4
2
3
4
- 实体类设置主键
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
}
1
2
3
4
5
6
7
2
3
4
5
6
7
- 定义通用mapper
import tk.mybatis.mapper.common.Mapper;
public interface UserMapper extends Mapper<User> {
}
1
2
3
2
3
- 测试代码
public class UserTest {
@Test
public void test1() throws IOException {
Inputstream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(4);
//(1)mapper基础接口
//select 接口
User user1 = userMapper.selectOne(user); //根据实体中的属性进行查询,只能有一个返回值
List<User> users = userMapper.select(null); //查询全部结果
userMapper.selectByPrimaryKey(1); //根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号
userMapper.selectCount(user); //根据实体中的属性查询总数,查询条件使用等号
// insert 接口
int insert = userMapper.insert(user); //保存一个实体,null值也会保存,不会使用数据库默认值
int i = userMapper.insertSelective(user); //保存实体,null的属性不会保存会使用数据库默认值
// update 接口
int i1 = userMapper.updateByPrimaryKey(user);//根据主键更新实体全部字段,null值会被更新
// delete 接口
int delete = userMapper.delete(user); //根据实体属性作为条件进行删除,查询条件 使用等号
userMapper.deleteByPrimaryKey(1); //根据主键字段进行删除,方法参数必须包含完 整的主键属性
//(2)example方法
Example example = new Example(User.class);
example.createCriteria().andEqualTo("id", 1);
example.createCriteria().andLike("val", "1");
//自定义查询
List<User> users1 = userMapper.selectByExample(example);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
编辑 (opens new window)
上次更新: 2022/03/20, 16:39:15