在事务结束时发送事件

2024-04-18

我有一个服务对象的接口,如下所示(为简洁起见进行了简化):

public interface ItemService {

  public Item getItemById(String itemId, int version);

  public void create(Item item, User user);

  public void update(Item item, User user);

  public void delete(Item item, User user);

}

ItemService单个实现,并作为 Spring bean 连接起来。我们项目的 UI 部分以及处理 Ajax 请求的代码使用它来创建和修改Item我们的数据存储中的对象。

在底层,每个方法在被调用时都会发出一系列事件。其他模块接收事件以执行诸如保持 Lucene 索引最新或向管理员发送消息以让他们知道某些内容已更改之类的操作。每个方法调用在 Spring 中构成一个事务(使用org.springframework.orm.hibernate3.HibernateTransactionManager and org.springframework.transaction.interceptor.TransactionProxyFactoryBean).

最近有需要compose在单个事务中一起调用多个方法。有时有多个服务。例如,我们可能想做类似的事情:

*Begin transaction*
Get Items created by User Bill using ItemService
 for each Item in Items 
   Update field on Item
   Link Item to User Bill with LinkService 
   Update Item using ItemService
*Finish transaction* 

我们通过创建另一个服务来实现此目的,该服务允许您在父服务中的单个方法中编写来自服务的调用。我们称之为ComposingService. ComposingService与所有其他事务一样,也由 Spring 管理,并且由于事务是可重入的,因此这一切都应该有效。

然而,存在一个问题:如果事务内的任何这些操作失败,就会导致事务回滚我们不想发送任何事件.

按照目前的情况,如果交易中途失败,一半的事件将由ItemService在事务回滚之前,这意味着某些模块将收到一堆尚未发生的事件的事件。

我们正在尝试找到某种方法来解决这个问题,但我们一直想不出任何优雅的方法。到目前为止,我们想出的最好的办法是这样的(而且很丑):

public interface ItemService {

  public Item getItemById(String itemId, int version);

  public void create(Item item, User user, List<Event> events);

  public void update(Item item, User user, List<Event> events);

  public void delete(Item item, User user, List<Event> events);

}

在此修改后的 ItemService 中,事件不是立即发送,而是添加到作为参数传入的事件列表中。该列表由以下人员维护ComposingService,事件由以下方式发送ComposingService一旦所有的电话ItemService且其他服务已成功退出。

显然,问题在于我们以一种丑陋的方式更改了 ItemService 上的契约。调用类,即使它们是服务,也不应该担心管理事件。但我一直无法想出解决这个问题的方法,因此这个问题。

这看起来像是以前可能已经解决的问题。有人遇到过类似的问题吗?如果有,你是如何解决的?


总结您的问题:您正在寻找一种交易安全的方式来发送消息。

选项 1:JMS

事务安全消息传递正是 JMS 的用途。 Spring 中也有很好的 JMS 集成,请参阅JMS章节 http://static.springsource.org/spring/docs/3.0.x/reference/jms.html在 Spring 文档中。

这将确保消息被发送当且仅当事务已提交。 它还有助于处理这些事件的侦听器中的错误。

与当前设置的不同之处在于,这些事件将异步处理:您的服务将在处理这些事件之前返回。 (JMS 将确保它们最终得到处理,它可以配置为尝试多次以及如何处理错误,...)。根据您的需求,这可能是好事也可能是坏事。

选项2:事务同步

或者,如果 JMS 太重量级的对于您的情况,您可以使用事务同步:发送事件时,不要直接使用 Spring 的发送事件TransactionSynchronizationManager.registerSynchronization,并将消息发送到afterCommit()你的TransactionSynchronization。 您可以为每个要发送的事件添加一个新的同步,或者添加一个同步并通过使用以下命令将包含该列表的对象绑定到事务来跟踪要发送的事件:TransactionSynchronizationManager.bindResource.

我建议不要尝试使用你自己的ThreadLocal为此,因为在某些情况下这会出错;例如,如果在您的交易中您将开始一个新交易(RequiresNew).

与当前设置的差异:

  • 如果在这种情况下处理事件时引发异常,您的服务将引发异常,但更改已在数据库中提交。
  • 如果您的侦听器之一也写入数据库,则必须在新事务中执行此操作。

或者,您可以使用beforeCommit代替afterCommit,但是即使稍后对数据库的实际提交失败,您的事件也会被处理(发送邮件,...)。

这不太稳健(较少交易),比使用 JMS 更轻且更容易设置,并且通常够好了.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在事务结束时发送事件 的相关文章

随机推荐

  • 调试变量不适用于 gulp sourcemaps + uglify

    我有以下用于捆绑 javascript 的 gulp 任务 gulp task js function return gulp src paths js pipe sourcemaps init pipe uglify pipe conca
  • 在 MATLAB 中调整 3D 矩阵(图像)的大小

    我有一个 3D 矩阵 MxNxK 想将其大小调整为 M xN xK 就像 matlab 中的 imresize 一样 我正在使用图像金字塔 但其结果不是很准确 需要一个更好的 有什么解决办法吗 你可以使用interp3 因为您想要插入 3D
  • 从 Dataflow 写入 BigQuery - 作业完成时不会删除 JSON 文件

    我们的 Dataflow 作业之一将其输出写入 BigQuery 我对其幕后实现方式的理解是 Dataflow 实际上将 JSON 格式的结果 分片 写入 GCS 然后启动 BigQuery 加载作业以导入该数据 但是 我们注意到 无论作业
  • CSS 中的鼠标按下选择器是什么?

    我注意到按钮和其他元素具有默认样式 并按 3 个步骤运行 普通视图 悬停 焦点视图和鼠标按下 单击视图 在 CSS 中我可以更改普通视图和悬停视图的样式 如下所示 button background 333 color FFF button
  • ngResource 将 POST 参数附加到 url

    我有一个看起来像这样的角度服务 我在这里发出 POST 请求 factory Apples function resource HOST return resource HOST apples create method POST para
  • 使用 XPath 排序 - 不是 XSL

    XPath DOM 编程中有什么方法可以使用System Xml to run selectNodes XPATH 带有排序参数 例如 对于以下 XML 程序按照与文档相同的顺序 降序 写入值 有没有办法使用 XPath 来按升序获取值 笔
  • 是否可以使用计时器为 JButton 创建淡入效果?

    我想在我的程序按钮上添加一些效果 当我按下button the button应该淡入 Using a timer改变的值Opaque in new Color 是一种方式 但是可以应用于JButton还有 因为JButton有边界 所以我想
  • 分配后列表被意外清除[重复]

    这个问题在这里已经有答案了 class ListHolder public List
  • 应用程序中所有国家/地区的城市和邮政编码

    有没有一种方法可以将所有国家 州 城市的邮政编码存储在一个数据库中 我一直在到处寻找 我发现地名网 http www geonames org 我猜其中包含了所有内容 但无法直接获取该数据 我正在使用导轨 如果有任何助手或插件就太好了 如果
  • 更改 iOS 中的应用程序语言设置而不是整个设备

    我希望在我的应用程序中可以选择更改我的应用程序语言 只是应用程序语言而不是整个系统 请给我一些提示好吗 提前致谢 我知道本地化 您可以使用从 Xcode 设置的自定义构建标志来完成此操作 这样您就可以在本地化下运行应用程序 而无需更改设备的
  • 当我单击数据表中的下一页时,我的 jquery 选择器不再工作

    我在用着数据表 http www datatables net jquery 插件在页面上显示我的数据 当有人单击一行时 我有这个选择器 myTable tr class tableHeader click function alert c
  • 多线程程序中的 std::string

    鉴于 1 C 03标准没有以任何方式解决线程的存在 2 C 03 标准将其留给实现来决定是否std string应该在其复制构造函数中使用 Copy on Write 语义 3 写时复制语义通常会导致多线程程序中不可预测的行为 我得出以下看
  • 为什么要使用Promise.resolve().then()?

    在阅读 Angular 指令的代码时路由器链路激活 https github com angular angular blob master packages router src directives router link active
  • 释放 GCD 调度队列属性的正确方法是什么?

    我正在使用一个dispatch queue 它是通过其所有者的属性访问的 如下所示 property nonatomic assign dispatch queue t queue 请注意assign关键词 队列在对象的整个生命周期中使用
  • 嵌套属性未以简单形式显示

    鉴于以下情况 Models class Location lt ActiveRecord Base has many games end class Game lt ActiveRecord Base validates presence
  • 如何获取 BTreeSet 中元素的下界和上限?

    正在阅读BTreeSet文档 我似乎无法弄清楚如何从 a 中获取大于元素的最小值或小于元素的最大值BTreeSet在对数时间内 我看到有一个range https doc rust lang org std collections stru
  • 将 Elmah 与 WebApi 结合使用时,错误会记录两次

    我正在尝试使用 elmah 记录我的 asp net web api 项目中的异常 我遇到的问题是每个错误都会记录两次 我正在使用 Elmah Contrib Web Api 我的应用程序类如下 public class WebApiApp
  • 用 R 计算年龄

    我在 R 中有两个数据帧 其中一个数据帧包含一个人的出生年份 YEAR 1931 1924 然后另一列显示最近的时间 RECENT 09 08 2005 11 08 2005 我想做的是减去年份 以便我可以计算他们的年龄 但我不知道如何处理
  • Rails respond_with:它是如何工作的?

    我到处读到关于它有多酷的内容respond with方法在 Rails 3 中 但我什至无法在 Rails API 中或通过搜索源代码找到对它的引用 任何人都可以向我解释它是如何工作的 您可以使用哪些选项等 或者向我指出它实际实现的位置 以
  • 在事务结束时发送事件

    我有一个服务对象的接口 如下所示 为简洁起见进行了简化 public interface ItemService public Item getItemById String itemId int version public void c