需要在DRL文件中建立数据库连接 [英] Need to establish Database connectivity in DRL file

查看:0
本文介绍了需要在DRL文件中建立数据库连接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

需要在Drools中建立Oracle数据库连接,以便在执行规则时根据需要获取一些数据。我该怎么做?

推荐答案

您不应该这样做。相反,您应该首先从数据库中查询数据,然后将其作为工作内存中的事实传递到规则中。

我试图写一份关于您不应该这样做的所有原因的详细答案,但结果发现StackOverflow有字符限制。因此,我将向您介绍高级原因。

  1. 延迟
  2. 数据一致性
  3. 缺乏数据库访问强化
  4. 规则的极端设计约束
  5. 维护负担大
  6. 潜在的安全问题

按顺序进行...

延迟。数据库查询不是免费的。无论您的连接管理有多好,每次进行数据库调用时都会产生开销。如果您非常了解Drools执行生命周期及其如何执行规则,将您的规则设计为显式地仅以将调用数量和数量降至最少的方式查询数据库,您可以认为这是一个可以接受的风险。一个好的缓存层不会有问题。请注意,以这种方式正确设计您的规则绝非易事,而且必须确保您的所有规则都兼容,这将带来永久的开销。

(提示:这意味着您绝不能永远从‘WHEN’子句调用数据库。)

数据一致性。数据库是共享资源。如果您在两个不同的‘When’子句中执行相同的查询,不能保证您将得到相同的结果。同样,您可以通过深入了解Drools如何计算和执行规则并适当地设计您的规则来解决这个问题。但同样的"延迟"问题在这里也会影响到你--即永久维护的负担。此外,规则设计限制--非常严格--可能也会降低您的其他规则和用例的效率,因为您需要进行扭曲以保持与数据库相关的规则的兼容性。

缺乏强化。可以在DRL函数中编写的Java代码与可以在Java类中编写的Java代码不同。DRL文件被解析为字符串,然后进行解释和编译;许多语言功能根本不可用。(一些示例:尝试使用资源、注释等)这使得正确加强数据库访问变得极其复杂,在某些情况下甚至是不可能的。依赖于诸如Spring数据等注释的库不能用于您的DRL函数。您将需要使用大致相当于Java 5的Java语言的子集手动管理连接池、事务管理、连接管理(关闭一切!)、错误处理等。

当然,这是专门针对编写将数据库作为DRL中的函数访问的代码的。如果您在充当数据库访问层的服务中实现数据库访问,则可以在该外部服务中利用完整的JDK及其特性和功能,然后将其作为输入传递到规则中。但就DRL功能而言,这一点仍然是一个主要问题。

规则设计约束。正如我前面提到的,您需要深入了解Drools如何计算和执行规则,以便编写与数据库交互的有效规则。如果您不知道所有左侧(";When";子句)都是先执行的,则";匹配";按重要性排序,然后按顺序执行右侧(";然后";子句)...嗯,你绝对不应该试图从规则上这样做。不仅作为初始实现者的您需要了解规则执行生命周期,而且在您之后将要维护您的规则的每个人也需要了解这一点,并且继续基于这些限制来实现规则。这是您的高维护负担。

举个例子,这里有两条规则。让我们假设";DataService";是一个正确实现的数据访问层,具有所有必要的连接和事务管理,并且它被作为事实传递到工作内存中。

rule "Record Student Tardiness"
when
  $svc: DataService() // data access layer
  Tardy( $id: studentId )
  $student: Student($tardy: tardyCount) from $svc.getStudentById($id)
then
  $student.setTardyCount($tardy + 1)
  $svc.save($student)
end

rule "Issue Demerit for Excessive Tardiness"
when
  $svc: DataService() // data access layer
  Tardy( $id: studentId )
  $student: Student(tardyCount > 3) from $svc.getStudentById($id)
then
  AdminUtils.issueDemerit($student, "excessive tardiness")
end

如果您了解Drools如何执行规则,您很快就会意识到这些规则的问题。即:

  • 我们调用getStudentById两次(延迟、一致性)
  • 第二条规则看不到对学生迟到次数的更改

因此,如果我们的学生Alice在数据库中记录了3个迟到时间,并且我们为她传递了一个新的Tardy实例,则第一个规则将被击中,她的迟到次数将增加并被保存(Alice将在数据库中有4个迟到时间)。但第二条规则不会命中!因为在计算匹配项时,Alice只有3个迟到,而发出记过规则仅触发超过3个。因此,尽管Alice现在有4个迟到,但她当时没有

第二个问题的解决方案当然是调用update,让Drool知道重新评估与工作内存中的新数据的所有匹配。这当然加剧了第一个问题--现在我们将调用getStudentById四次

最后一个问题是潜在的安全问题。这实际上取决于您实现查询的方式,但您需要加倍确保不会意外地暴露DRL中的任何连接配置(URL、凭据),并且您已经正确清理了所有查询输入以防止SQL注入。


当然,做这件事的正确方法是根本不做。首先调用数据库,然后将其传递给您的规则。

例如,假设我们有一套规则,旨在通过将客户购买与前3个月购买量的趋势进行比较来确定客户购买是否可疑。

// Assume this class serves as our data access layer and does proper connection, 
// transaction management. It might be something like a Spring Data JPA repository,
// or something from another library; the specifics are not relevant.
private PurchaseService purchaseService; 

public boolean isSuspiciousPurchase(Purchase purchase) {
    List<Purchase> previous = purchaseService.getPurchasesForCustomerAfterDate(
        purchase.getCustomerId(),
        LocalDate.now().minusMonths(3));

    KieBase kBase = ...;
    KieSession session = kBase.newKieSession();
    session.insert(purchase);
    session.insert(previous);
    // insert other facts as needed
    session.fireAllRules();

    // ...
}

如您所见,我们调用数据库并将结果传递到工作内存中。然后,我们可以编写规则,使它们在现有列表上工作,而根本不需要与数据库交互。

如果我们的用例需要修改数据库--例如保存更新--我们可以将这些命令传递回调用者,并在fireAllRules完成后调用它们。这不仅将使我们不必与规则中的数据库交互,还将使我们能够更好地控制事务管理(您可能可以将更新分组到单个事务中,即使最初的更新来自多个规则)。而且,因为我们不需要了解Drools如何计算和执行规则,所以如果带有数据库的规则被触发两次,它会更健壮一些。

这篇关于需要在DRL文件中建立数据库连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆