首页 > 其他分享 >mybatis学习第⼋部分:Mybatis插件

mybatis学习第⼋部分:Mybatis插件

时间:2022-11-21 23:13:15浏览次数:45  
标签:插件 target Object public Mybatis user mybatis class

8.1 插件简介

⼀般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者⾃⾏拓展。这样的好处是显⽽易⻅ 的,⼀是增加了框架的灵活性。⼆是开发者可以结合实际需求,对框架进⾏拓展,使其能够更好的⼯ 作。以MyBatis为例,我们可基于MyBati s插件机制实现分⻚、分表,监控等功能。由于插件和业务 ⽆ 关,业务也⽆法感知插件的存在。因此可以⽆感植⼊插件,在⽆形中增强功能

 

 

8.2   Mybatis插件介绍

Mybati s作为⼀个应⽤⼴泛的优秀的ORM开源框架,这个框架具有强⼤的灵活性,在四⼤组件

(Executor、StatementHandler、ParameterHandler、ResultSetHandler)处提供了简单易⽤的插 件扩 展机制。Mybatis对持久层的操作就是借助于四⼤核⼼对象。MyBatis⽀持⽤插件对四⼤核⼼对象进 ⾏ 拦截,对mybatis来说插件就是拦截器,⽤来增强核⼼对象的功能,增强功能本质上是借助于底层的 动 态代理实现的,换句话说,MyBatis中的四⼤对象都是代理对象

MyBatis所允许拦截的⽅法如下:
执⾏器Executor (update、query、commit、rollback等⽅法);
SQL语法构建器StatementHandler (prepare、parameterize、batch、updates query等⽅ 法);
参数处理器ParameterHandler (getParameterObject、setParameters⽅法);
结果集处理器ResultSetHandler (handleResultSets、handleOutputParameters等⽅法);

8.2   Mybatis插件原理

在四⼤对象创建的时候

1、每个创建出来的对象不是直接返回的,⽽是interceptorChain.pluginAll(parameterHandler); 2、获取到所有的Interceptor (拦截器)(插件需要实现的接⼝);调⽤ interceptor.plugin(target);返 回 target 包装后的对象

3、插件机制,我们可以使⽤插件为⽬标对象创建⼀个代理对象;AOP (⾯向切⾯)我们的插件可 以 为四⼤对象创建出代理对象,代理对象就可以拦截到四⼤对象的每⼀个执⾏;

拦截

插件具体是如何拦截并附加额外的功能的呢?以ParameterHandler来说

public ParameterHandler newParameterHandler(MappedStatement mappedStatement,
Object object, BoundSql sql, InterceptorChain interceptorChain){
ParameterHandler parameterHandler =
mappedStatement.getLang().createParameterHandler(mappedStatement,object,sql);
parameterHandler = (ParameterHandler)
interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}

nterceptorChain保存了所有的拦截器(interceptors),是mybatis初始化的时候创建的。调⽤拦截器链
中的拦截器依次的对⽬标进⾏拦截或增强。interceptor.plugin(target)中的target就可以理解为mybatis
中的四⼤对象。返回的target是被重重代理后的对象
如果我们想要拦截Executor的query⽅法,那么可以这样定义插件:

@Intercepts({
@Signature(
type = Executor.class, method = "query", args=
{MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class}
)
})
public class ExeunplePlugin implements Interceptor {
//省略逻辑
}

除此之外,我们还需将插件配置到sqlMapConfig.xm l中。

<plugins>
<plugin interceptor="com.lagou.plugin.ExamplePlugin">
</plugin>
</plugins>

这样MyBatis在启动时可以加载插件,并保存插件实例到相关对象(InterceptorChain,拦截器链) 中。
待准备⼯作做完后,MyBatis处于就绪状态。我们在执⾏SQL时,需要先通过DefaultSqlSessionFactory
创建 SqlSession。Executor 实例会在创建 SqlSession 的过程中被创建, Executor实例创建完毕后,
MyBatis会通过JDK动态代理为实例⽣成代理类。这样,插件逻辑即可在 Executor相关⽅法被调⽤前执
⾏。
以上就是MyBatis插件机制的基本原理

8.4 ⾃定义插件

8.4.1 插件接⼝

Mybatis 插件接⼝-Interceptor

• Intercept⽅法,插件的核⼼⽅法

  • plugin⽅法,⽣成target的代理对象
  • setProperties⽅法,传递插件所需参数

8.4.2⾃定义插件 设计实现⼀个⾃定义插件

Intercepts ({//注意看这个⼤花括号,也就这说这⾥可以定义多个@Signature对多个地⽅拦截,都⽤
这个拦截器
@Signature (type = StatementHandler .class , //这是指拦截哪个接⼝
method = "prepare",//这个接⼝内的哪个⽅法名,不要拼错了
args = { Connection.class, Integer .class}),//// 这是拦截的⽅法的⼊参,按顺序写到这,不要多也不要少,如果⽅法重载,可是要通过⽅法名和⼊参来确定唯⼀的
})
public class MyPlugin implements Interceptor {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
// //这⾥是每次执⾏操作的时候,都会进⾏这个拦截器的⽅法内
Override
public Object intercept(Invocation invocation) throws Throwable {
//增强逻辑
System.out.println("对⽅法进⾏了增强....");
return invocation.proceed(); //执⾏原⽅法
}
/**
* //主要是为了把这个拦截器⽣成⼀个代理放到拦截器链中
* ^Description包装⽬标对象 为⽬标对象创建代理对象
* @Param target为要拦截的对象
* @Return代理对象
*/
Override
public Object plugin(Object target) {
System.out.println("将要包装的⽬标对象:"+target);
return Plugin.wrap(target,this);
}
/**获取配置⽂件的属性**/
//插件初始化的时候调⽤,也只调⽤⼀次,插件配置的属性从这⾥设置进来
Override
public void setProperties(Properties properties) {
System.out.println("插件配置的初始化参数:"+properties );
}
}

sqlMapConfig.xml

<plugins>
<plugin interceptor="com.lagou.plugin.MySqlPagingPlugin">
<!--配置参数-->
<property name="name" value="Bob"/>
</plugin>
</plugins>

mapper接⼝

public interface UserMapper {
List<User> selectUser();
}

mapper.xml

<mapper namespace="com.lagou.mapper.UserMapper">
<select id="selectUser" resultType="com.lagou.pojo.User">
SELECT
id,username
FROM
user
</select>
</mapper>

测试类

public class PluginTest {
@Test
public void test() throws IOException {
InputStream resourceAsStream =
Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> byPaging = userMapper.selectUser();
for (User user : byPaging) {
System.out.println(user);
}
}
}

8.5 源码分析
执⾏插件逻辑
Plugin实现了 InvocationHandler接⼝,因此它的invoke⽅法会拦截所有的⽅法调⽤。invoke⽅法会 对
所拦截的⽅法进⾏检测,以决定是否执⾏插件逻辑。该⽅法的逻辑如下:

// -Plugin
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
try {
/*
*获取被拦截⽅法列表,⽐如:
* signatureMap.get(Executor.class), 可能返回 [query, update,
commit]
*/
Set<Method> methods =
signatureMap.get(method.getDeclaringClass());
//检测⽅法列表是否包含被拦截的⽅法
if (methods != null && methods.contains(method)) {
//执⾏插件逻辑
return interceptor.intercept(new Invocation(target, method,
args));
//执⾏被拦截的⽅法
return method.invoke(target, args);
} catch(Exception e){
}
}

nvoke⽅法的代码⽐较少,逻辑不难理解。⾸先,invoke⽅法会检测被拦截⽅法是否配置在插件的
@Signature注解中,若是,则执⾏插件逻辑,否则执⾏被拦截⽅法。插件逻辑封装在intercept中,该
⽅法的参数类型为Invocationo Invocation主要⽤于存储⽬标类,⽅法以及⽅法参数列表。下⾯简单看
⼀下该类的定义

public class Invocation {
private final Object target;
private final Method method;
private final Object[] args;
public Invocation(Object targetf Method method, Object[] args) {
this.target = target;
this.method = method;
//省略部分代码
public Object proceed() throws InvocationTargetException,
IllegalAccessException { //调⽤被拦截的⽅法
>> —

8.6   pageHelper分⻚插件

MyBati s可以使⽤第三⽅的插件来对功能进⾏扩展,分⻚助⼿PageHelper是将分⻚的复杂操作进⾏封 装,使⽤简单的⽅式即可获得分⻚的相关数据

开发步骤:

①  导⼊通⽤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>

② 在mybatis核⼼配置⽂件中配置PageHelper插件

<!--注意:分⻚助⼿的插件 配置在通⽤馆mapper之前*-->*
<plugin interceptor="com.github.pagehelper.PageHelper">
<!—指定⽅⾔ —>
<property name="dialect" value="mysql"/>
</plugin>

③ 测试分⻚代码实现

@Test
public void testPageHelper() {
//设置分⻚参数
PageHelper.startPage(1, 2);
List<User> select = userMapper2.select(null);
for (User user : select) {
System.out.println(user);
}
}
}

获得分⻚相关的其他参数

/其他分⻚的数据
PageInfo<User> pageInfo = new PageInfo<User>(select);
System.out.println("总条数:"+pageInfo.getTotal());
System.out.println("总⻚数:"+pageInfo. getPages ());
System.out.println("当前⻚:"+pageInfo. getPageNum());
System.out.println("每⻚显万⻓度:"+pageInfo.getPageSize());
System.out.println("是否第⼀⻚:"+pageInfo.isIsFirstPage());
System.out.println("是否最后⼀⻚:"+pageInfo.isIsLastPage());

8.6   通⽤ mapper

什么是通⽤Mapper

通⽤Mapper就是为了解决单表增删改查,基于Mybatis的插件机制。开发⼈员不需要编写SQL,不需要 在DAO中增加⽅法,只要写好实体类,就能⽀持相应的增删改查⽅法

如何使⽤

  1. ⾸先在maven项⽬,在pom.xml中引⼊mapper的依赖
    <dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper</artifactId>
    <version>3.1.2</version>
    </dependency>

    2. Mybatis配置⽂件中完成配置

    <plugins>
    <!--分⻚插件:如果有分⻚插件,要排在通⽤mapper之前-->
    <plugin interceptor="com.github.pagehelper.PageHelper">
    <property name="dialect" value="mysql"/>
    </plugin>
    <plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
    <!-- 通⽤Mapper接⼝,多个通⽤接⼝⽤逗号隔开 -->
    <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
    </plugin>
    </plugins>

    3. 实体类设置主键

    @Table(name = "t_user")
    public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String username;
    }

    4. 定义通⽤mapper

    import com.lagou.domain.User;
    import tk.mybatis.mapper.common.Mapper;
    public interface UserMapper extends Mapper<User> {
    }

    5. 测试

    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);
    }
    }



标签:插件,target,Object,public,Mybatis,user,mybatis,class
From: https://www.cnblogs.com/eagle888/p/16913742.html

相关文章