当前位置: 首页 > 知识库问答 >
问题:

用于数据持久性的BusinessCode强制约束

燕飞文
2023-03-14

我目前正在实现一个基于 JavaEE 的应用程序,其结构大致如下:

RestService
├── BusinessService
    ├── ServiceDAO

webservice接收请求并将它们转发给实际的业务服务,然后业务服务通过DAO执行多个操作。重要的是,BusinessService必须使用只能通过业务代码表达的约束,而不是通过SQL约束(我们不希望实现复杂的数据库触发器或存储过程)来对照现有记录检查数据。在这种情况下,约束是没有两个数据集可能具有重叠有效性。

对于最小方案,代码如下所示:

@Table
public class DataEntity {
    @Id
    public int id;

    @Column("validFrom")
    public Instant validFrom;

    @Column("validTo")
    public Instant validTo;
}
@Path("/rest-service-path")
public class RestService {
    @Inject
    private BusinessService businessService;

    @POST
    @Path("add")
    public void addData(DataEntity data) {
        businessService.addData(data);
    }
}
@Stateless
public class BusinessService {
    @Inject
    private ServiceDAO serviceDAO;

    public void addData(DataEntity data) {
        List<DataEntity> overlappingData =
            serviceDAO.findOverlapping(data.getValidFrom(), data.getValidTo());
        if (!overlappingData.isEmpty()) {
            throw new IllegalStateException("Cannot have time overlaps");
        }
        serviceDAO.save(data);
    }
}
@Stateless
public class ServiceDAO {
    @Inject
    private EntityManager entityManager;

    public List<DataEntity> findOverlapping(Instant from, Instant to) {
        // finds records that have their validity interval overlap with the supplied one
    }

    public void save(DataEntity data) {
        entityManager.persist(data);
    }
}

如果我正确理解了 EJB,则上述代码具有以下隐式行为:

  • @Stateless注释将BusinessServiceServiceDAO,转换为EJB
  • 默认情况下,BusinessServiceServiceDAO具有容器并发管理功能。
  • 默认情况下,BusinessService#addDataServiceDAO#addData的事务类型为“必需”,因为它们是公共EJB方法
  • 因为<code>BusinessService#addData

我面临的问题是,对< code >/rest-service-path/add 的两个并发调用打开了两个独立的事务,这也将并发执行验证检查。如果两个数据集都通过了检查,但两个数据集合在一起会违反约束,则它们仍会被添加到数据库中,因为由于单独的事务,它们看不到彼此。

我目前的解决方法(我不太有信心)是使业务服务成为@Singleton。这将隐式地给出添加数据锁定类型.WRITE,这意味着没有两个对addData的调用可以同时运行。这有以下问题:

  • 如果我们有两个或多个单独的java进程运行该服务,则没有帮助,因为锁定仅适用于每个EJB。
  • 它阻止了不会相互干扰的数据实体的并发处理,这是我们的大多数情况。这会导致性能更差

到目前为止,我对这一切的理解正确吗?如何安全、更优雅地实施业务代码中定义的约束,如上面所述?我同意在DAO中抛出异常,只要它保持一致性。

共有1个答案

谭京
2023-03-14

我不认为你会绕过一个单例实例,但你可以通过不将EJB设置为单例,而是引入另一个管理已经处理的数据的服务来最小化影响。

本质上,它将包含正在检查的DataEntity的集合。如果它们不重叠,可以同时检查它们,因此您可以通过首先检查新候选者和队列中的候选者之间的重叠来最大化并发。

如果要添加 DataEntity,但已经检查了一个重叠的实体,则必须等到该检查完成(毕竟,可能无法添加前一个实体,但可以添加最近的实体)。为此,您需要一种机制来稍后触发排队检查。

 类似资料:
  • 并了解了如何制作仅数据容器:http://container42.com/2014/11/18/data-only-container-madness/ 我也看到了与我类似的问题:如何在docker中处理持久存储(例如数据库 但如果我有一个灯服务器设置..我用数据容器进行了一切很好的设置,而不是将它们“直接”链接到我的源操作系统,偶尔做一次备份… 总比有人过来重新启动我的服务器..我如何重新设置我

  • 我们有一个系统(例如系统a)通过HTTP接收时间序列数据,并且该数据通过OpenTSDB的REST接口持久化在OpenTSDB中。我现在想把阿帕奇Kafka引入系统。我的想法是运行一个Kafka服务器,系统a在接收到时间序列消息后立即将该消息发布到Apache Kafka服务器。 然后,我可以让一个使用者读取主题并将这些数据写入OpenTSDB。我对这种方法有几个问题: 关于生产者和消费者的架构:

  • 问题内容: 东西没有被冲走。发生的情况的简化示例: 输出: 我不知道是否必须对Session和Transaction进行某些操作才能使数据持久化,或者这是GPar中的错误。在底层的hibernate级别发生了什么? 我希望最近创建的Person在并行闭包中可见。 问题答案: Gpars是一个多线程工具,并且在您的域类中注入的hibernate会话不是线程安全的。 尝试使用以下方法或直接调用Sess

  • 问题内容: 从这里的讨论看来,Redux reducer的状态应该保留在数据库中。 用户身份验证在这种情况下如何工作? 是否不会创建新的状态对象来替换数据库中先前创建和编辑的每个用户(及其应用程序状态)的先前状态? 在前端使用所有这些数据并不断更新数据库中的状态是否会表现出色? 编辑: 我创建了一个示例Redux auth项目,该项目也恰好示例了通用Redux,并使用Redux,Socket.io

  • 正如标题中所述,我正在使用Firebase实时数据库,并启用了持久性。

  • 问题内容: 我创建了一个这样的对象: 我想保存该对象。我怎样才能做到这一点? 问题答案: 你可以使用标准库中的模块。这是你的示例的基本应用: 你还可以定义自己的简单实用程序,如下所示,该实用程序打开文件并向其中写入单个对象: 更新资料 由于这是一个非常受欢迎的答案,因此,我想谈谈一些高级用法主题。 实际使用该cPickle模块几乎总是可取的,而不是因为该模块是用C编写的并且速度更快。它们之间有一些