如何解决需要在 DRL 文件中建立数据库连接
需要在 drools 中建立 Oracle 数据库连接,以便在执行规则时根据需要获取一些数据。我该怎么做?
解决方法
你不应该这样做。相反,您应该先从数据库中查询数据,然后将其作为工作内存中的事实传递给规则。
我试图写一篇关于你不应该这样做的所有原因的详细答案,但事实证明 StackOverflow 有字符限制。所以我要给你一些高层次的理由。
- 延迟
- 数据一致性
- 缺乏数据库访问强化
- 规则的极端设计约束
- 高维护负担
- 潜在的安全问题
按顺序...
延迟。数据库查询不是免费的。无论您的连接管理有多好,每次进行数据库调用时都会产生开销。如果您对 Drools 执行生命周期及其执行规则的方式有深入的了解,并且您将规则设计为仅以最小化调用次数和数量的方式显式查询数据库,您可以考虑这是一个OK风险。一个好的缓存层不会出错。请注意,必须以这种方式正确设计您的规则并非易事,而且您必须确保所有规则保持合规,因此会产生永久的开销。
(提示:这意味着您绝不能永远从“when”子句中调用数据库。)
数据一致性。数据库是一种共享资源。如果您在两个不同的“when”子句中进行相同的查询,不能保证您会得到相同的结果。同样,您可以通过深入了解 Drools 如何评估和执行规则并适当地设计规则来解决这个问题。但是来自“延迟”的相同问题也会影响到您——即永久维护的负担。此外,规则设计限制(非常严格)可能还会降低您的其他规则和用例的效率,因为您需要进行扭曲以保持依赖于数据库的规则兼容。
缺乏强化。您可以在 DRL 函数中编写的 Java 代码与您可以在 Java 类中编写的 Java 代码不同。 DRL 文件被解析为字符串,然后被解释,然后被编译;许多语言功能根本不可用。 (一些示例:try-with-resources、注释等)这使得正确地强化您的数据库访问极其复杂,在某些情况下是不可能的。您无法在 DRL 函数中使用依赖于注解(如 Spring Data)的库。您将需要使用 Java 语言的子集手动管理您的连接池、事务管理、连接管理(关闭所有内容!)、错误处理等,大致相当于 Java 5。
当然,这特定于编写代码以将数据库作为 DRL 中的函数访问。如果您改为在充当数据库访问层的服务中实现数据库访问,则可以在该外部服务中利用完整的 JDK 及其特性和功能,然后将其作为输入传递到规则中。但就DRL功能而言,这一点仍然是一个主要问题。
规则设计约束。 正如我之前提到的,您需要深入了解 Drools 如何评估和执行规则,以便编写与数据库交互的有效规则。如果您不知道 所有 左侧(“when”子句)首先执行,然后按显着性排序的“匹配”,然后执行右侧(“then”子句)按顺序......好吧,你绝对不应该试图从规则中做到这一点。作为最初的实施者,您不仅需要了解规则执行生命周期,而且接下来要维护规则的每个人也需要了解这一点, 并继续基于这些实施规则限制。这是您的高维护负担。
例如,这里有两条规则。让我们假设“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 个迟到数据库。)但是第二条规则不会命中!因为在计算匹配时,爱丽丝只有 3 次迟到,并且“记分”规则仅在超过 3 次时触发。所以虽然她有 4 次迟到 现在 ,她没有然后。
第二个问题的解决方案当然是调用update
,让Drools知道用工作内存中的新数据重新评估所有匹配。这当然会加剧第一个问题——现在我们将调用 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 文件中编写了函数,但建议在 java 文件中添加此类代码并从 DRL 文件中调用特定方法。
function String ConnectDB(String ConnectionClass,String url,String user,String password) {
Class.forName(ConnectionClass);
java.sql.Connection con = DriverManager.getConnection(url,user,password);
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select * from Employee where employee_id=199");
rs.first();
return rs.getString("employee_name");
}
rule "DBConnection"
when
person:PersonPojo(name == ConnectDB("com.mysql.jdbc.Driver","jdbc:mysql://localhost:3306/root","root","redhat1!"))
.. ..
then
. . ..
end
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。