Mybatis中拦截器的使用场景和技巧分享

 更新时间:2023年10月23日 11:40:09   作者:襄垣  
Mybatis提供了一些机制,可以允许我们在做数据库操作的时候进行我们额外的一些程序,当然,这看起来并没有JPA的EntityListener好用,本文小编将给大家详细的介绍了Mybatis中拦截器的使用场景和技巧,需要的朋友可以参考下

场景描述

中小业务系统中,有时候会面临一些比较相似的业务场景。

  • XXX创建成功后,需要给XXX推送一条业务消息;
  • XXX更新的时候,需要给XXX推送一条业务提醒;
  • XXX创建OR更新的时候,需要将最新的数据推送到搜索引擎中,以方便其他业务系统在搜索引擎中能查询到;
  • XXX更新的时候,需要更新分布式缓存的XXX数据;
  • ……

这些场景都有相似的特征,如下

特征描述
异步实际上与主流程关系不大,可以作为附属流程异步执行
事务大多在事务结束之后执行
并行可以串行执行,也可以并行执行,对最终结果影响不大

Mybatis拦截器

Mybatis提供了一些机制,可以允许我们在做数据库操作的时候进行我们额外的一些程序。当然,这看起来并没有JPA的EntityListener好用。

但是通过这些机制,我们可以达成我们主要的目的,解耦合。(解耦合的好处显而易见,我们在关注主流程业务的时候,附属流程的业务也更容易扩展)

下面是我们即将用到的一些简单概念,如下

概念描述
解耦解耦的本质就是将类之间的直接关系转换成间接关系
观察者模式一种行为设计模式,允许你定义一种订阅机制,可在对象事件发生时通知多个“观察”该对象的其他对象。
EventBus基于事件驱动,观察者们监听自己感兴趣的特定事件,进行相应的处理。

锚定事件发生

首先,我们要找到事件发生的地方,通常来说我们更愿意在业务代码里写

Company company = new Company();
company.setCompanyName(companyName);
companyMapper.insert(company);
// 创建XXX成功后,触发了一些操作;
doSomething(company);

但是,这种方式是耦合的,我们在doSomething() 里发生的一些异常会影响到主流程,而每一次增加附属流程的改动,都会产生影响范围的蔓延。

这里,我们使用Mybatis提供的一种方式来锚定事件发生

@Component
@Intercepts( {
    @Signature(method = "update", type = Executor.class, args = {
        MappedStatement.class,
        Object.class
    })
})
public class EntityInterceptor implements Interceptor {
    private static final Logger LOGGER = LoggerFactory.getLogger(EntityInterceptor.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object proceed = invocation.proceed();
        try {
            Object[] args = invocation.getArgs();
            MappedStatement ms = (MappedStatement) args[0];
            Object params = args[1];
            if (SqlCommandType.INSERT.equals(ms.getSqlCommandType())) {
                insertCommond(params);
            }
            if (SqlCommandType.UPDATE.equals(ms.getSqlCommandType())) {
                updateCommond(params);
            }
        } catch (Exception e) {
            LOGGER.warn("entity change warning: {}", e.getMessage());
        } finally {
            return proceed;
        }
    }
    
        private void insertCommond(Object params) {
        if (Objects.isNull(params)) {
            return;
        }
        // 插入Company的事件
        if (params instanceof Company) {
            Company company = (Company) params;
            CompanyEvent companyEvent =
                new CompanyEvent(CompanyEvent.TOPIC_ADD, WebUtils.getOpenId(), CompanyVo.from(company));
            SpringUtil.getApplicationContext().publishEvent(companyEvent);
            LOGGER.info("[发布事件:{}] - [事件内容:{}]", CompanyEvent.TOPIC_ADD, JSON.toJSONString(companyEvent));
        }
    }
}

在这里,我们监听了MybatisExecutor对象的update方法,来监听对象的新增和修改。

package org.apache.ibatis.executor;

public interface Executor {

  int update(MappedStatement ms, Object parameter) throws SQLException;
}

当 Company 的 insert 事件发生时,我们发布了一条 CompanyEvent 的事件。

订阅事件

我们使用发布-订阅模型实现了解耦合,针对刚才发布的 CompanyEvent 事件,我们来写一个事件消费者。

@Component
public class CompanyListener {

    /**
     * 监听事件 - 公司.
     *
     * @param companyEvent 事件.
     */
    @Lazy
    @Async
    @TransactionalEventListener(
        fallbackExecution = true,
        phase = TransactionPhase.AFTER_COMPLETION,
        classes = CompanyEvent.class)
    public void doAsync(CompanyEvent companyEvent) {
        LOGGER.info("[Company 订阅:{}] - [开始执行:{}]",
            companyEvent.getTopic(), JSON.toJSONString(companyVo));
    }
}

代码中涉及到一些注解,简单介绍下

注解介绍
@Component会注册为Spring的一个Bean
@Lazy该方法会异步执行
@TransactionalEventListener在事务的不同阶段去触发执行该监听

我们标注了该事件是在TransactionPhase.AFTER_COMPLETION(事务提交完成)这个事务节点进行事件处理。

观察者模式

上面使用了SpringEventListener来实现的事件驱动,除此之外,还可以使用GuavaEventBusVert.xEventBus等方式实现。

很多工具类提供了关于EventBus的实现,但是使用逻辑和方式上大多大同小异。

总结

观察者模式监听模式都有利于解耦合,根据业务诉求合理的进行开发设计,能为以后的扩展打下坚实的基础。

以上就是Mybatis中拦截器的使用场景和技巧分享的详细内容,更多关于Mybatis拦截器使用的资料请关注脚本之家其它相关文章!

相关文章

  • 浅谈Java中的Filter过滤器

    浅谈Java中的Filter过滤器

    本篇文章主要介绍了浅谈Java中的Filter过滤器,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • Java Stream流零基础教程

    Java Stream流零基础教程

    Java8的另一大亮点Stream,它与java.io包里的InputStream和OutputStream是完全不同的概念,下面这篇文章主要给大家介绍了关于Java8中Stream详细使用方法的相关资料,需要的朋友可以参考下
    2022-11-11
  • Java中字符串转int数据类型的三种方式

    Java中字符串转int数据类型的三种方式

    这篇文章主要介绍了Java中字符串转int数据类型的三种方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • 使用Spring注入Hibernate验证框架

    使用Spring注入Hibernate验证框架

    这篇文章主要介绍了使用Spring注入Hibernate验证框架方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • SpringBoot配置SwaggerUI访问404错误的解决方法

    SpringBoot配置SwaggerUI访问404错误的解决方法

    这篇文章主要为大家详细介绍了SpringBoot配置SwaggerUI访问404错误的解决方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • Java DOM4J方式生成XML的方法

    Java DOM4J方式生成XML的方法

    今天小编就为大家分享一篇Java DOM4J方式生成XML的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • java中Sources目录Resources目录的区别解读

    java中Sources目录Resources目录的区别解读

    这篇文章主要介绍了java中Sources目录Resources目录的区别解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • IDEA整合jeesite4.x及安装教程

    IDEA整合jeesite4.x及安装教程

    本文给大家介绍IDEA整合jeesite4.x及安装教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-07-07
  • hibernate属性级别注解实例代码

    hibernate属性级别注解实例代码

    这篇文章主要介绍了hibernate属性级别注解实例代码,涉及属性级别注释添加方式及种类,具有一定参考价值,需要的朋友可以了解下。
    2017-10-10
  • java中 == 与 equal 的区别讲解

    java中 == 与 equal 的区别讲解

    这篇文章介绍了java中 == 与 equal 的区别,有需要的朋友可以参考一下
    2013-10-10

最新评论