mybatis为我们提供了插件,并提供了其为可扩展
可以增强我们使用的灵活性,可以结合项目需求对框架进行拓展
目前我们可以基于mybatis插件实现分页、分表、监控等功能
mybatis有四大组件
mybatis对持久层的操作就是借助着四大核心对象,我们可以对着四大对象进行拦截加强处理
也就相当于四大组件的拦截器,增强功能的本质上是底层的动态代理实现的
也就是说mybatis的四大组件核心对象就是代理对象
mybatis允许我们拦截的方法如下:
在四⼤核心对象创建的时候
1、每个创建出来的对象不是直接返回的,⽽是interceptorChain.pluginAll(parameterHandler)处理后的
2、获取到所有的Interceptor (拦截器),调⽤ interceptor.plugin(target);返回 target 包装后的对象
3、插件机制,AOP (⾯向切⾯)我们的插件可以为四⼤对象创建出代理对象,代理对象就可以拦截到四⼤对象的每⼀个执⾏;
public ParameterHandler newParameterHandler(MappedStatement mappedStatement,
Object object, BoundSql sql, InterceptorChain interceptorChain){
// 此处已经创建好了ParameterHandler对象
ParameterHandler parameterHandler =
mappedStatement.getLang().createParameterHandler(mappedStatement,object,sql);
// 此处interceptorChain.pluginAll(parameterHandler)处理 生成代理对象
parameterHandler = (ParameterHandler)interceptorChain.pluginAll(parameterHandler);
// 返回的也就是代理对象
return parameterHandler;
}
public Object pluginAll(Object target) {
// 获取我们的所有拦截器
for (Interceptor interceptor : interceptors) {
// 参数传入的target就是ParameterHandler的原生对象
// plugin对原生对象进行处理 就是为当前的原生对象生成一个代理对象
target = interceptor.plugin(target);
}
return target;
}
如果我们要做一个拦截器应该怎么做呢
假设我们要拦截Executor的query方法
首先要创建一个拦截器 这个拦截器要实现Interceptor接口
// 首先加入Intercepts注解代表这是一个拦截器
@Intercepts({
// 这个拦截器具体拦截了什么
@Signature(
// 要拦截的类
type = Executor.class,
// 要拦截的方法名
method = "query",
// 因为会有方法重载 需要标识拦截方法的参数
args={
MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class})
})
public class ExecutorPlugin implements Interceptor {
// 具体拦截实现
}
然后需要将我们的ExecutorPlugin拦截器配置到核心配置文件中
<plugins>
<plugin interceptor="com.hen84.plugin.ExecuPlugin">
</plugin>
</plugins>
这样在我们启动时就可以加载到我们自定义的插件,并保存插件实例到拦截器链中,在我们执行sql时 Executor实例被创建后 mybatis就会为其生成动态代理类,这样在执行query方法前就会调用我们的拦截器里的加强逻辑
接下来我们手动演示一下
package com.hen84.plugin;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.util.Properties;
@Intercepts({
// 这个拦截器具体拦截了什么
@Signature(
// 要拦截的类
type = Executor.class,
// 要拦截的方法名
method = "query",
// 因为会有方法重载 需要标识拦截方法的参数
args={
MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class})
})
public class ExecutorPlugin implements Interceptor {
// 具体的加强操作 只要被拦截的query方法执行时,就会先调用该方法
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("开始增强.........");
return invocation.proceed();//意思就是让原方法执行
}
// 把当前拦截器放到拦截器链中
public Object plugin(Object o) {
System.out.println("把当前拦截器放到拦截器链中"+o);
Object wrap = Plugin.wrap(o, this);
return wrap;
}
// 配置文件的参数
public void setProperties(Properties properties) {
System.out.println("properties" + properties);
}
}
<plugins>
<plugin interceptor="com.hen84.plugin.ExecutorPlugin">
<property name="测试" value="123456"/>
</plugin>
</plugins>
这样每当我们执行sql语句之前都会先执行我们的加强方法
pageHelper是mybatis为我们提供的分页插件,那么他怎样使用呢
导⼊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>
在核心配置文件中配置PageHelper插件
<plugin interceptor="com.github.pagehelper.PageHelper">
<!—指定⽅⾔ —>
<property name="dialect" value="mysql"/>
</plugin>
附上pageHelper的部分代码
// 能够看到和我们的配置方法是一样的
// 也就是在每次执行Executor的query方法前都会调用intercept加强方法
@Intercepts({
@Signature(
type = Executor.class,
method = "query",
args = {
MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)})
public class PageHelper implements Interceptor {
}
演示
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
// 分页查询 startPage(要查询第几页,一页有多少条数据)
PageHelper.startPage(1,1);
List<User> users = userMapper1.selectList();
控制台打印
// PageHelper为我们做了什么事
// 1 先查询总条数
17:44:18,598 DEBUG selectList_PageHelper_Count:159 - ==> Preparing: SELECT count(*) FROM t_user
17:44:18,912 DEBUG selectList_PageHelper_Count:159 - ==> Parameters:
17:44:19,127 DEBUG selectList_PageHelper_Count:159 - <== Total: 1
// 根据我们PageHelper.startPage(1,1);直接改变我们的sql完成分页查询
17:44:19,129 DEBUG selectList_PageHelper:159 - ==> Preparing: select user_id userId,user_name userName from t_user limit ?,?
17:44:19,130 DEBUG selectList_PageHelper:159 - ==> Parameters: 0(Integer), 1(Integer)
17:44:19,135 DEBUG selectList_PageHelper:159 - <== Total: 1
通⽤Mapper就是为了解决单表增删改查,基于Mybatis的插件机制。开发⼈员不需要编写SQL,不需要
在DAO中增加⽅法,只要写好实体类,就能⽀持相应的增删改查⽅法
导入坐标
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>3.1.2</version>
</dependency>
在核心配置文件中完成配置
<plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
<!-- 通⽤Mapper接⼝,多个通⽤接⼝⽤逗号隔开 -->
<property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
</plugin>
实例演示
// 指定sql表
@Table(name = "t_user")
public class User implements Serializable {
@Id // 指定主键
@GeneratedValue(strategy = GenerationType.IDENTITY) // 指定自动增长
private Long userId;
@Column(name = "user_name") //指定sql字段名称
private String userName;
@Column(name = "user_phone") //指定sql字段名称
private String userPhone;
@Transient // 表明这个不是数据库的字段
private String noSql;
}
// 实现我们指定的通用mapper接口
// mapper接口中有为我们写好的各种单表查询方法
public interface UserMapper extends Mapper<User> {
}
如果这些写好的sql并不能满足mybatis还为我们提供了自定义查询
//example⽅法
Example example = new Example(User.class);
// 设置条件 id = 1
example.createCriteria().andEqualTo("id", 1);
//⾃定义查询
List<User> users = userMapper.selectByExample(example);
}
1、mybatis可拦截处理方法都有哪些
2、mybatis自定义插件机制
3、对平常使用的pageHelper和通用Mapper有了更深刻的印象