Drools + Hibernate:需要在规则中用 OR 条件模拟 Left Outer Join

Drools + Hibernate: Need to simulate Left Outer Join with OR condition in rule

使用下面的规则,我尝试使用具有 OR 逻辑条件的规则来匹配帐户。在这种情况下,我有一个 table 的账户和一个 table 这些账户的保险记录。并非所有帐户都有保险记录。 Hibernate DAO 对象存在并且存在从帐户到保险的关联。我没有观察到这条规则的预期行为。在这种情况下,我希望帐户 1、2、3 和 4 匹配此规则,因为该规则应匹配状态为 "Inactive" 的任何帐户或保险 CURRENT_IND 值为 [= 的任何帐户21=]。但是,只有帐户 2 和 4 符合规则。仅当帐户具有保险记录时,该帐户才会匹配规则。我想要状态为 'Inactive' 的所有帐户记录,无论其保险记录是否存在。

我目前正在使用 Drools 5.6.10.FINAL 和 Hibernate 3.6.0.

进行测试

是否可以使用带 Drools 的 Hibernate 创建这样的规则?是什么导致规则过滤保险记录的存在?

package com.app.Testing
import com.app.abilities.RuleBuilder.EvalObject;
import com.app.dao.Insurance;
import com.app.dao.Account;

rule "Null Test"
when
 $V1 : Account( )
 $V2 : Insurance( ) from $V1.getInsurance()
 $V3 : EvalObject(
    $V2.getCurrentInd == "N" || 
    $V1.getStatus == "Inactive"
 )
then
  reply.getReplyList().add("Null Test");
end

示例数据:

Account:
ACCT_ID    STATUS
1          Inactive
2          Inactive
3          Inactive
4          Active
5          Active


Insurance:
ID    CURRENT_IND
2     N
4     N
5     Y

完全没有必要使用 "from" 访问某些事实的现成可用字段 - 您可以通过绑定变量或 getter 访问。这就是导致问题的原因:空值不能 "frommed" 到模式中,因此任何具有保险 == null 的帐户都将阻止进一步评估。

此外,我发现使用完全事实 (EvalObject) 来编写布尔值而不是巴洛克式的。即使您必须使用 oh-so-terrible Java 语法,一个简单的 eval 也应该做得很好。

总而言之,以下规则对您的所有四个帐户都有效:

rule "Better Test"
when
    $V1: Account( $sta: status, $ins: insurance )
    eval( $sta.equals( "Inactive" ) ||
          $ins != null && $ins.getCurrentInd().equals( "N" ) )
then
    System.out.println("Better Test " + $V1.getId() );
end

当然,逻辑是一件非常美妙的事情,如果应用得当,它会随着皮带的拉动而变化。因此,您还可以使用一些规则:

rule "Third Test A"
when
    $V1: Account( $sta: status == "Inactive" )
then
    System.out.println("Third Test A " + $V1.getId() );
end
rule "Third Test B"
when
    $V1: Account( $sta: status == "Active" )
    Insurance( currentInd == "N" ) from $V1.getInsurance()
then
    System.out.println("Third Test B " + $V1.getId() );
end

"Ahh",我听到你说,"he's making me use two rules for what can be done with one? Duplicate the RHS code? Phooey!"

不,不是我:这只是通往决赛的桥梁。这个怎么样:

rule "Fourth Test"
when
    $V1: Account( $sta: status == "Inactive" )
    or
    ($V1: Account( $sta: status == "Active" )
    and
    Insurance( currentInd == "N" ) from $V1.getInsurance()
    )
then
    System.out.println("Fourth Test " + $V1.getId() );
end

编辑 如果一个帐户可能与任意数量的保险事实相关联(即 getInsurance returns a Collection<Insurance>),最后三个规则仅适用一样好。但是,他们将为 currentInd == "N" 处的每个关联保险开火。要将其减少为单个激活,请在 Insurance 模式前加上 exists(或 not 以获得稍微不同的效果)。

考虑将受抚养人 objects(保险)也作为事实考虑总是值得的。此外,从 child 到 parent(帐户)的 link 可能会改善问题。