16.5. DRL 中的类型声明和元数据
DRL 文件中的声明定义了新的事实类型或元数据,以便供 DRL 文件中的规则使用的事实类型:
-
新事实类型: Red Hat Process Automation Manager 的
java.lang
软件包中的默认事实类型是对象
,但您可以根据需要声明 DRL 文件中的其他类型的类型。在 DRL 文件中声明事实类型可让您直接在决策引擎中定义新的事实模型,而无需在 Java 等低级别语言中创建模型。您也可以在已经构建域模型时声明新类型,并且您希望将此模型与主要在原因过程中使用的其他实体补充。 -
事实类型的元数据: 您可以将格式
@key(value)
中的元数据与新的或现有事实相关联。元数据可以是不由事实属性表示的任何数据类型,在该事实类型的所有实例之间是一致的。可在运行时查询元数据,供决策引擎在原因过程中使用。
16.5.1. 在 DRL 中没有元数据的类型声明
新事实的声明不需要任何元数据,但必须包含属性或字段列表。如果类型声明不包含标识属性,则决策引擎会在类路径中搜索现有的事实类,并在缺少类时引发错误。
以下示例是 DRL 文件中没有元数据的新事实类型 Person
声明:
带有规则的新事实类型的声明示例
declare Person name : String dateOfBirth : java.util.Date address : Address end rule "Using a declared type" when $p : Person( name == "James" ) then // Insert Mark, who is a customer of James. Person mark = new Person(); mark.setName( "Mark" ); insert( mark ); end
在本例中,新事实类型 Person
有三个属性,即dateOfBirth
,地址为。
每个属性都有一个类型,可以是任何有效的 Java 类型,包括您创建另一个类或之前声明的事实类型。
dateOfBirth
属性类型 java.util.Date
、Java API 和 address
属性具有之前定义的事实类型 Address
。
为了避免在每次声明时写入类的完全限定名称,您可以将完整类名称定义为 导入
子项的一部分:
导入中具有完全限定类名称的类型声明示例
import java.util.Date declare Person name : String dateOfBirth : Date address : Address end
当您声明新的事实类型时,决策引擎在编译时生成代表事实类型的 Java 类。生成的 Java 类是类型定义的一对一 JavaBeans 映射。
例如,以下 Java 类由示例 Person
类型声明生成:
为 Person fact 类型声明生成的 Java 类
public class Person implements Serializable { private String name; private java.util.Date dateOfBirth; private Address address; // Empty constructor public Person() {...} // Constructor with all fields public Person( String name, Date dateOfBirth, Address address ) {...} // If keys are defined, constructor with keys public Person( ...keys... ) {...} // Getters and setters // `equals` and `hashCode` // `toString` }
然后,您可以使用规则中生成的类与其他事实一样,如上例中的 Person
类型声明所示:
使用声明的 Person 事实类型的规则示例
rule "Using a declared type" when $p : Person( name == "James" ) then // Insert Mark, who is a customer of James. Person mark = new Person(); mark.setName( "Mark" ); insert( mark ); end
16.5.2. DRL 中的枚举类型声明
DRL 支持以声明 enum <factType> 的格式 声明 enum <factType
> 的声明,后跟以分号结尾的值列表。然后,您可以使用 DRL 文件中的规则中的枚举列表。
例如,以下枚举类型声明为员工调度规则定义了一周的天数:
具有调度规则的枚举类型声明示例
declare enum DaysOfWeek SUN("Sunday"),MON("Monday"),TUE("Tuesday"),WED("Wednesday"),THU("Thursday"),FRI("Friday"),SAT("Saturday"); fullName : String end rule "Using a declared Enum" when $emp : Employee( dayOff == DaysOfWeek.MONDAY ) then ... end
16.5.3. DRL 中的扩展类型声明
DRL 支持格式类型声明继承,声明 <factType1> 扩展了 <factType2>
。要根据 DRL 中声明的子类型扩展 Java 中声明的类型,您可以在没有任何字段的声明中重复父类型。
例如,以下类型声明从顶级 Person
类型扩展 Student
类型,以及 Student
子类型 中的 LongTerm
Stude 类型:
扩展类型声明示例
import org.people.Person declare Person end declare Student extends Person school : String end declare LongTermStudent extends Student years : int course : String end
16.5.4. 带有 DRL 中元数据的类型声明
您可以将格式 @key(value)
(值是可选的)中的元数据与事实类型或事实属性相关联。元数据可以是不由事实属性表示的任何数据类型,在该事实类型的所有实例之间是一致的。可在运行时查询元数据,供决策引擎在原因过程中使用。在事实类型属性前声明的任何元数据都会被分配给 fact 类型,而您在属性后声明的元数据被分配到该特定属性。
在以下示例中,为 Person
事实类型声明两个元数据属性 @author
和 @dateOfCreation
,为 name
属性声明两个元数据项目 @key
和 @maxLength
。@key
metadata 属性没有所需的值,因此省略括号和值。
事实类型和属性的元数据声明示例
import java.util.Date declare Person @author( Bob ) @dateOfCreation( 01-Feb-2009 ) name : String @key @maxLength( 30 ) dateOfBirth : Date address : Address end
对于现有类型的元数据属性声明,您可以识别完全限定的类名称,作为所有声明条款的 导入
子项的一部分,或作为个别 声明声明
的一部分:
导入类型的元数据声明示例
import org.drools.examples.Person declare Person @author( Bob ) @dateOfCreation( 01-Feb-2009 ) end
声明类型的元数据声明示例
declare org.drools.examples.Person @author( Bob ) @dateOfCreation( 01-Feb-2009 ) end
16.5.5. DRL 中事实类型和属性声明的元数据标签
虽然您可以在 DRL 声明中定义自定义元数据属性,但决策引擎也支持以下预定义元数据标签用于事实类型或事实类型属性声明。
本节中的示例参考 VoiceCall
类假设示例应用程序域模型包括以下类详情:
示例电信域模型中的 VoiceCall 事实类
public class VoiceCall { private String originNumber; private String destinationNumber; private Date callDateTime; private long callDuration; // in milliseconds // Constructors, getters, and setters }
- @role
此标签决定,给定事实类型是作为常规事实处理,还是在复杂事件处理期间在决策引擎中的事件进行处理。
default 参数:
fact
支持的参数:
事实
、事件
@role( fact | event )
示例: Declare VoiceCall 作为事件类型
declare VoiceCall @role( event ) end
- @timestamp
此标签会自动分配给决策引擎中的每个事件。默认情况下,会话时钟提供的时间,并在事件插入决策引擎的工作内存中时分配给事件。您可以指定自定义时间戳属性,而不是会话时钟添加的默认时间戳。
default 参数:决策引擎会话时钟添加的时间
支持的参数: Session clock time 或 custom time stamp 属性
@timestamp( <attributeName> )
示例: Declare VoiceCall timestamp 属性
declare VoiceCall @role( event ) @timestamp( callDateTime ) end
- @duration
此标签决定决策引擎中事件的持续时间。事件可以是基于间隔的事件或时间点事件。基于间隔的事件的时间,并在决策引擎的工作内存中保留一段时间,直到它们的持续时间可用为止。点内事件没有持续时间,基本上是基于 interval 的事件,且持续为零。默认情况下,决策引擎中的每个事件都会有零持续时间。您可以指定自定义持续时间属性,而不是默认值。
默认参数:Null(零)
支持的参数: 自定义持续时间属性
@duration( <attributeName> )
示例: Declare VoiceCall duration 属性
declare VoiceCall @role( event ) @timestamp( callDateTime ) @duration( callDuration ) end
- @expires
此标签决定事件在决策引擎工作内存过期前的时间持续时间。默认情况下,事件过期,事件不再匹配并激活任何当前规则。您可以定义事件应过期的时间。此标签定义还覆盖从 KIE 基础中的时序限制和滑动窗口计算的隐式到期偏移。只有在决策引擎以流模式运行时,该标签才可用。
默认参数:Null(事件后的事件过期,不再匹配并激活规则)
支持的参数:自定义
timeOffset
属性,格式为[#d][#h][#m][#s][[ms]]
@expires( <timeOffset> )
示例:对 VoiceCall 事件进行禁止过期偏移
declare VoiceCall @role( event ) @timestamp( callDateTime ) @duration( callDuration ) @expires( 1h35m ) end
- @typesafe
此选项卡确定给定事实类型是使用 还是没有类型安全性的编译。默认情况下,所有类型声明都是使用启用类型 security 进行的编译。您可以覆盖此行为来类型非安全评估,其中所有限制都是以 MVEL 约束并执行动态生成的。这在处理没有通用或混合类型集合的集合时很有用。
默认参数:
true
支持的参数:
true
、false
@typesafe( <boolean> )
示例: Declare VoiceCall 用于 type-unsafe 评估
declare VoiceCall @role( fact ) @typesafe( false ) end
- @serialVersionUID
此标记在事实声明中为 serializable 类定义标识
serialVersionUID
值。如果 serializable 类没有明确声明serialVersionUID
,则序列化运行时间会根据类的不同方面计算该类的默认serialVersionUID
值,如 Java Object Serialization 规格 中所述。但是,为了实现最佳反序列化结果并更好地与序列化 KIE 会话的兼容性,请在相关类或您的 DRL 声明中根据需要设置serialVersionUID
。默认参数: Null
支持的参数: 自定义
serialVersionUID
整数@serialVersionUID( <integer> )
示例:对 VoiceCall 类的 Declare serialVersionUID
declare VoiceCall @serialVersionUID( 42 ) end
- @key
此标签启用事实类型属性,用作事实类型的键标识符。然后,生成的类可以实施
equals()
和hashCode()
方法,以确定类型的两个实例是否等于。决策引擎还可使用所有关键属性作为参数生成构造器。默认参数: None
支持的参数: 无
<attributeDefinition> @key
示例: Declare Person类型属性作为键
declare Person firstName : String @key lastName : String @key age : int end
在本例中,决策引擎检查
firstName
和lastName
属性,以确定Person
的两个实例是否相互相等,但它没有检查age
属性。决策引擎还会隐式生成三个构造器:一个没有参数,一个含有@key
字段,另一个含有所有字段:键声明中的构造器示例
Person() // Empty constructor Person( String firstName, String lastName ) Person( String firstName, String lastName, int age )
然后,您可以基于密钥构造器创建类型为 的实例,如下例所示:
使用密钥构造器的实例示例
Person person = new Person( "John", "Doe" );
- @position
此标记决定了位置参数中声明事实类型属性或字段的位置,覆盖属性的默认声明顺序。您可以使用此标签来修改模式中的位置限制,同时保持类型声明和位置参数的一致格式。此标签仅适用于 classpath 上类的字段。如果一个类中的一些字段使用此标签,则没有此标签的属性将按照声明的顺序定位。支持类继承,但不支持方法接口。
默认参数: None
支持的参数: 任何整数
<attributeDefinition> @position ( <integer> )
示例:拒绝事实类型和覆盖声明的顺序
declare Person firstName : String @position( 1 ) lastName : String @position( 0 ) age : int @position( 2 ) occupation: String end
在本例中,属性按以下顺序在位置参数中排列优先级:
-
lastName
-
firstName
-
age
-
occupation
在位置参数中,您不需要指定字段名称,因为位置映射到已知命名字段。例如,参数
Person(lastName == "Doe")
与Person("Doe";)
相同,其中lastName
字段在 DRL 声明中具有最高位置注解。分号;
表示在它是一个位置参数之前的所有内容。您可以使用分号将它们分开,在模式中混合位置和命名参数。位置参数中的任何变量都未绑定到映射到该位置的字段。以下示例模式演示了构建位置和命名参数的不同方法。该模式有两个限制和绑定,分号区分指定参数部分的 positional 部分。在位置参数中支持使用字面意义的变量和字面量,但不支持单独变量。
带有位置和指定参数的模式示例
Person( "Doe", "John", $a; ) Person( "Doe", "John"; $a : age ) Person( "Doe"; firstName == "John", $a : age ) Person( lastName == "Doe"; firstName == "John", $a : age )
位置参数可以归类为 输入参数 或 输出参数。输入参数包含之前声明的绑定并使用未验证受到该绑定的限制。当绑定尚不存在时,输出参数会生成声明并将其绑定到 positional 参数代表的字段。
在扩展类型声明中,在定义
@position
注释时要谨慎,因为属性位置在子类型中继承。这种继承可能会导致混合属性顺序在在某些情况下可能会引起混淆。两个字段可以具有相同的@position
值,并且不需要声明连续的值。如果重复位置,则使用继承的方式解决冲突,即父类型中的位置值具有优先权,然后使用第一个到最后一个声明中的声明顺序。例如,以下扩展类型声明导致混合位置优先级:
带有混合位置注解的扩展事实类型示例
declare Person firstName : String @position( 1 ) lastName : String @position( 0 ) age : int @position( 2 ) occupation: String end declare Student extends Person degree : String @position( 1 ) school : String @position( 0 ) graduationDate : Date end
在本例中,属性按以下顺序在位置参数中排列优先级:
-
lastname
(在父类型中的位置 0) -
院校
(子类型中的位置 0) -
FirstName
(在父类型中的位置 1) -
程度
(子类型中的位置 1) -
age
(在父类型中的位置 2) -
occupation
(没有位置注解的前字段) -
graduationDate
(无位置注解的第二个字段)
-
16.5.6. 为事实类型更改设置和监听程序
默认情况下,决策引擎不会在每次触发规则时重新评估所有事实类型的事实模式,而是仅响应给定模式内受限制或绑定的属性。例如,如果规则调用 modify()
作为规则操作的一部分,但该操作不会在 KIE 基础中生成新数据,则决策引擎不会自动重新评估所有事实模式,因为没有修改数据。这个属性 reactivity 行为可防止 KIE 库中不需要的递归,并导致更有效的规则评估。这个行为还意味着您不需要总是使用 no-loop
rule 属性来避免无限重复。
您可以使用以下 KnowledgeBuilderConfiguration
选项修改或禁用此属性重新活动行为,然后根据需要使用 Java 类或 DRL 文件中的属性更改设置来微调属性重新活动:
-
ALWAYS
:(默认)所有类型都是属性 reactive,但您可以使用@classReactive
属性-change 设置禁用特定类型的属性重新活动。 -
ALLOWED
:无类型是属性 reactive,但您可以使用@propertyReactive
属性-change 设置为特定类型启用属性重新活动。 -
DISABLED
: No type 是属性 reactive。所有属性更改监听程序都会被忽略。
在 KnowledgeBuilderConfiguration 中属性重新活动设置示例
KnowledgeBuilderConfiguration config = KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration(); config.setOption(PropertySpecificOption.ALLOWED); KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(config);
或者,您可以在 Red Hat Process Automation Manager 发行版的 standalone.xml
文件中更新 drools.propertySpecific
系统属性:
在系统属性中设置的属性重新活动示例
<system-properties> ... <property name="drools.propertySpecific" value="ALLOWED"/> ... </system-properties>
决策引擎支持以下属性更改设置和监听程序,用于事实类或声明的 DRL 事实类型:
- @classReactive
如果在决策引擎中将属性重新活动设置为
ALWAYS
(所有类型都是 reactive),则此标签会禁用特定 Java 类的默认属性重新活动行为或声明的 DRL 事实类型。如果您希望决策引擎在每次触发规则时重新评估指定事实类型的所有事实模式,则可使用此标签,而不是仅响应给定模式内受约束或绑定的属性。示例: 禁用 DRL 类型声明中的默认属性重新活动
declare Person @classReactive firstName : String lastName : String end
示例:禁用 Java 类中的默认属性重新活动
@classReactive public static class Person { private String firstName; private String lastName; }
- @propertyReactive
如果在决策引擎中将属性重新活动设置为
ALLOWED
(没有属性重新活跃),则此标签为特定 Java 类启用属性重新活动,或者为声明的 DRL 事实类型启用属性重新活动。如果您希望决策引擎仅响应指定事实类型内受限制或绑定的属性,则可使用此标签,而不是在每次触发规则时重新评估所有事实模式。示例:在 DRL 类型声明中启用属性重新活动(在全局禁用重新活动时)
declare Person @propertyReactive firstName : String lastName : String end
示例:在 Java 类中启用属性重新活动(当全局禁用重新活动时)
@propertyReactive public static class Person { private String firstName; private String lastName; }
- @watch
此标签为您在 DRL 规则中以实际模式指定的附加属性启用属性重新活动。只有在决策引擎中将属性重新活动设置为
ALWAYS
时,或将属性重新活动设置为ALLOWED
且相关事实类型使用@propertyReactive
标签时,才支持该标签。您可以在 DRL 规则中使用此标签在事实属性重新活动逻辑中添加或排除特定属性。默认参数: None
支持的参数:Property name,
*
(全部)、!
(不是)、!*
(无属性)<factPattern> @watch ( <property> )
示例:在事实模式下启用或禁用属性重新活动
// Listens for changes in both `firstName` (inferred) and `lastName`: Person(firstName == $expectedFirstName) @watch( lastName ) // Listens for changes in all properties of the `Person` fact: Person(firstName == $expectedFirstName) @watch( * ) // Listens for changes in `lastName` and explicitly excludes changes in `firstName`: Person(firstName == $expectedFirstName) @watch( lastName, !firstName ) // Listens for changes in all properties of the `Person` fact except `age`: Person(firstName == $expectedFirstName) @watch( *, !age ) // Excludes changes in all properties of the `Person` fact (equivalent to using `@classReactivity` tag): Person(firstName == $expectedFirstName) @watch( !* )
如果您在使用
@classReactive
标签的事实类型中使用@watch
标签(禁用属性重新活动),或在决策引擎中将属性重新活动设置为ALLOWED
,则决策引擎将@watch 标签用于属性
。如果您在监听器注解中重复属性(如@watch(firstName, ! firstName)
)时,也会发生编译错误。- @propertyChangeSupport
对于实现对 JavaBeans 规格中定义的属性更改的事实,此标签使决策引擎能够监控事实属性中的更改。
示例: Declare 属性更改在 JavaBeans 对象中支持
declare Person @propertyChangeSupport end
16.5.7. 访问应用程序代码中声明的 DRL 类型
DRL 中声明的类型通常在 DRL 文件中使用,而 Java 模型通常在规则和应用程序间共享模型时使用。因为在 KIE 基本编译时生成声明的类型,所以应用程序在应用程序运行时无法访问它们。在某些情况下,应用程序需要从声明的类型直接访问和处理事实,特别是当应用程序换行决策引擎时,为规则管理提供了更高级别的领域、特定于域的用户界面。
要直接从应用程序代码中处理声明的类型,您可以在 Red Hat Process Automation Manager 中使用 org.drools.definition.type.FactType
API。通过此 API,您可以在声明的事实类型中实例化、读取和写入字段。
以下示例代码直接从应用程序修改 Person
事实类型:
通过 FactType API 处理声明的事实类型的应用程序代码示例
import java.util.Date; import org.kie.api.definition.type.FactType; import org.kie.api.KieBase; import org.kie.api.runtime.KieSession; ... // Get a reference to a KIE base with the declared type: KieBase kbase = ... // Get the declared fact type: FactType personType = kbase.getFactType("org.drools.examples", "Person"); // Create instances: Object bob = personType.newInstance(); // Set attribute values: personType.set(bob, "name", "Bob" ); personType.set(bob, "dateOfBirth", new Date()); personType.set(bob, "address", new Address("King's Road","London","404")); // Insert the fact into a KIE session: KieSession ksession = ... ksession.insert(bob); ksession.fireAllRules(); // Read attributes: String name = (String) personType.get(bob, "name"); Date date = (Date) personType.get(bob, "dateOfBirth");
API 还包括其他有用的方法,例如一次性设置所有属性、从 map
集合中读取值,或一次性读取所有属性到 映射
集合中。
虽然 API 行为与 Java 反射相似,但 API 不使用反射,它依赖于通过生成的字节码实施的更高性能访问器。