14.5. DRL 中的类型声明和元数据
DRL 文件中的声明定义了 DRL 文件中规则使用的事实类型或元数据:
-
新事实类型: Red Hat Process Automation Manager 的
java.lang软件包中的默认事实类型是Object,但您可以根据需要在 DRL 文件中声明其他类型。在 DRL 文件中声明事实类型可让您直接在决策引擎中定义新的事实模型,而无需像 Java 一样使用较低语言创建模型。您还可以在已经构建域模型时声明新类型,并且您希望通过在原因过程中主要使用的额外实体来补充此模型。 -
事实类型的元数据: 您可以将
@key (value)格式的元数据与新的或现有事实相关联。元数据可以是任何不是由事实属性表示的数据,并在该事实类型的所有实例中保持一致。元数据可通过决策引擎在运行时查询,并在原因过程中使用。
14.5.1. 在 DRL 中没有元数据的类型声明 复制链接链接已复制到粘贴板!
新事实的声明不需要任何元数据,但必须包含属性或字段的列表。如果 type 声明不包括识别属性,则决策引擎会在 classpath 中搜索现有的事实类,并在缺少类时引发错误。
以下示例是一个新的事实类型 Person 的声明,在 DRL 文件中没有元数据:
带有规则的新事实类型的声明示例
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,以及 地址。每个属性都有一个 type,可以是任何有效的 Java 类型,包括您创建的另一个类或您之前声明的事实类型。dateOfBirth 属性具有来自 Java API 的 type java.util.Date,address 属性具有之前定义的事实类型 Address。
为了避免在每次声明类时写入类的完全限定名称,您可以将完整类名称定义为 import 子句的一部分:
带有导入中完全限定类名称的 type 声明示例
import java.util.Date
declare Person
name : String
dateOfBirth : Date
address : Address
end
当您声明新的事实类型时,决策引擎会在编译时生成代表事实类型的 Java 类。生成的 Java 类是类型定义的一对一映射。
例如,以下 Java 类是从示例 Person type 声明生成的:
为 Person 事实类型声明生成 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
14.5.2. DRL 中的枚举类型声明 复制链接链接已复制到粘贴板!
DRL 支持以声明 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
14.5.3. DRL 中的扩展类型声明 复制链接链接已复制到粘贴板!
DRL 支持以声明 <factType1& gt; 扩展 <factType2> 格式的类型声明继承。要根据 DRL 中声明的子类型扩展 Java 声明的类型,您可以在声明声明中重复父类型,而无需任何字段。
例如,以下 type 声明从顶级 Person 类型中扩展 CRUD 类型,以及来自 Tailoring 子类型的 LongTermStudent 类型:
扩展类型声明示例
import org.people.Person
declare Person end
declare Student extends Person
school : String
end
declare LongTermStudent extends Student
years : int
course : String
end
14.5.4. 使用 DRL 中的元数据类型声明 复制链接链接已复制到粘贴板!
您可以将格式 @key (value) (值是可选的)与事实类型或事实属性关联。元数据可以是任何不是由事实属性表示的数据,并在该事实类型的所有实例中保持一致。元数据可通过决策引擎在运行时查询,并在原因过程中使用。您在事实类型的属性之前声明的任何元数据都会被分配给事实类型,而您在属性后声明的元数据会被分配给该特定属性。
在以下示例中,为 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 子句的一部分,或作为单个 声明 条款的一部分:
导入类型的元数据声明示例
import org.drools.examples.Person
declare Person
@author( Bob )
@dateOfCreation( 01-Feb-2009 )
end
声明类型的 metadata 声明示例
declare org.drools.examples.Person
@author( Bob )
@dateOfCreation( 01-Feb-2009 )
end
14.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
此标签决定了给定事实类型是否作为常规事实处理,还是在复杂的事件处理期间在决策引擎中处理事件。
默认参数:
fact支持的参数:
fact、event@role( fact | event )示例: Declare VoiceCall 作为事件类型
declare VoiceCall @role( event ) end- @timestamp
此标签会自动分配给决策引擎中的每个事件。默认情况下,时间由会话时钟提供,并在它插入到决策引擎的工作内存中时分配给事件。您可以指定自定义时间戳属性,而不是会话时钟添加的默认时间戳。
Default 参数:决策引擎会话时钟添加的时间
支持的参数: Session clock time 或自定义时间戳属性
@timestamp( <attributeName> )示例: Declare VoiceCall timestamp 属性
declare VoiceCall @role( event ) @timestamp( callDateTime ) end- @duration
此标签决定了决策引擎中事件的持续时间。事件可以是基于间隔的事件或时间事件。基于间隔的事件具有持续时间,并在决策引擎的工作内存中保留,直到其持续时间被宽松为止。点事件没有持续时间,基本上是基于间隔的事件,持续时间为零。默认情况下,决策引擎中的每个事件都为零。您可以指定自定义 duration 属性而不是默认值。
默认参数:Null (零)
支持的参数:自定义持续时间属性
@duration( <attributeName> )示例: Declare VoiceCall duration 属性
declare VoiceCall @role( event ) @timestamp( callDateTime ) @duration( callDuration ) end- @expires
此标签决定事件在决策引擎工作内存中过期前的时长。默认情况下,当事件无法再匹配并激活任何当前规则时,事件会过期。您可以定义事件应过期的时间。此标签定义还覆盖由临时约束计算的隐式过期偏移,以及 KIE 基础中的分片窗口。只有在决策引擎以流模式运行时,此标签才可用。
默认参数:Null (事件后过期时间不再匹配并激活规则)
支持的参数: 自定义
timeOffset属性,格式为[IANAd][ theh][ them][114s][[ms]]@expires( <timeOffset> )示例:VoiceCall 事件的 Declare 过期偏移
declare VoiceCall @role( event ) @timestamp( callDateTime ) @duration( callDuration ) @expires( 1h35m ) end- @typesafe
此选项卡决定给定事实类型是否使用类型 security 编译。默认情况下,所有类型声明都会在启用了安全类型的情况下编译。您可以将此行为覆盖为 type-unsafe 评估,其中所有约束都作为 MVEL 约束动态执行。这在处理没有任何通用或混合类型集合的集合时很有用。
默认参数:
true支持的参数:
true,false@typesafe( <boolean> )示例:用于 type-unsafe 评估的 Declare VoiceCall
declare VoiceCall @role( fact ) @typesafe( false ) end- @serialVersionUID
此标签在事实声明中为可序列化类定义了识别
serialVersionUID值。如果 serializable 类没有显式声明serialVersionUID,则序列化运行时间会根据类的不同方面计算该类的默认serialVersionUID值,如 Java 对象序列化规格 中所述。但是,为了实现最佳反序列化结果,并更好地与序列化 KIE 会话兼容,请在相关类或 DRL 声明中根据需要设置serialVersionUID。默认参数:Null
支持的参数:Custom
serialVersionUID整数@serialVersionUID( <integer> )示例:VoiceCall 类的 Declare serialVersionUID
declare VoiceCall @serialVersionUID( 42 ) end- @key
此标签可让事实 type 属性用作事实类型的密钥标识符。然后,生成的类可以实施
equals ()和hashCode ()方法,以确定类型的两个实例是否相互相等。决策引擎也可以使用所有密钥属性作为参数生成构造器。默认参数: None
支持的参数: None
<attributeDefinition> @key示例: Declare Person 类型属性作为键
declare Person firstName : String @key lastName : String @key age : int end在本例中,决策引擎检查
firstName和lastName属性,以确定Person的两个实例是否相互相等,但不会检查age属性。决策引擎还隐式生成三个构造器:一个没有参数,一个带有@key字段,另一个带有所有字段:key 声明中的 constructors 示例
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 是一个事实类型并覆盖声明的顺序
declare Person firstName : String @position( 1 ) lastName : String @position( 0 ) age : int @position( 2 ) occupation: String end在本例中,属性按以下顺序按位置参数排列:
-
lastName -
firstName -
年龄 -
occupation
在位置参数中,您不需要指定字段名称,因为位置映射到已知命名字段。例如,参数
Person (lastName == "Doe")与Person ("Doe";)相同,其中lastName字段在 DRL 声明中具有最高位置注解。分号;表示它之前的所有内容都是位置参数。您可以使用分号分隔它们,将位置和命名参数混合在模式上。尚未绑定的位置参数中的任何变量都绑定到映射到该位置的字段。以下示例模式演示了构建位置和命名参数的不同方法。模式有两个限制和绑定,分号将位置部分与命名参数部分区分。仅使用字面参数支持变量和字面和表达式在位置参数中被支持,而不仅仅是变量。
带有位置和命名参数的模式示例
Person( "Doe", "John", $a; ) Person( "Doe", "John"; $a : age ) Person( "Doe"; firstName == "John", $a : age ) Person( lastName == "Doe"; firstName == "John", $a : age )位置参数可以归类为 输入参数 或 输出参数。输入参数包含之前声明的绑定,并使用取消配置来针对该绑定进行约束。输出参数生成声明,并将其绑定到位置参数表示的字段(如果绑定尚不存在)。
在扩展类型声明中,在定义
@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) -
eXecut(子类型中的 0 组成) -
firstname(在父类型中保存 1) -
等级(位于子类型中的 1) -
年龄(在父类型中保存 2) -
occupation(没有位置注解的第一个字段) -
graduationDate(没有位置注解的秒字段)
-
14.5.6. 事实类型的属性更改设置和监听程序 复制链接链接已复制到粘贴板!
默认情况下,决策引擎不会在每次触发规则时重新评估事实类型的所有事实模式,而是只响应给定模式内受限制或绑定的修改属性。例如,如果规则调用 modify () 作为规则操作的一部分,但操作不会在 KIE 基础中生成新的数据,则决策引擎不会自动重新评估所有事实模式,因为没有修改任何数据。此属性重新活动行为可防止 KIE 基础中不需要的递归,并导致更有效的规则评估。这个行为还意味着,您不必使用 no-loop 规则属性来避免无限递归。
您可以使用以下 Knowledgebase BuilderConfiguration 选项修改或禁用此属性重新活动行为,然后根据需要使用 Java 类或 DRL 文件中的属性更改设置来微调属性:
-
ALWAYS: (默认)所有类型都是重新主动的属性,但您可以使用@classReactiveproperty-change 设置禁用特定类型的属性重新活动。 -
ALLOWED: 没有类型是属性重新主动,但您可以使用@propertyReactiveproperty-change 设置为特定类型启用属性重新活动。 -
DISABLED:无类型是属性重新主动。所有 property-change 侦听程序都会被忽略。
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>
决策引擎支持以下 property-change 设置和监听程序作为事实类或声明的 DRL 事实类型:
- @classReactive
如果在决策引擎中将属性重新活动设置为
ALWAYS(所有类型都是重新主动),则此标签将禁用特定 Java 类或声明的 DRL 事实类型的默认属性重新活动行为。如果您希望决策引擎在每次触发规则时重新评估指定事实类型的所有事实模式,而不是只响应给定模式内的修改属性,则可以使用此标签。示例:在 DRL 类型声明中禁用默认属性重新活动
declare Person @classReactive firstName : String lastName : String end示例:在 Java 类中禁用默认属性重新活动
@classReactive public static class Person { private String firstName; private String lastName; }- @propertyReactive
如果在决策引擎中将属性 reactivity 设置为
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 规则中的事实模式中指定的额外属性启用属性重新活动。只有在决策引擎中将属性 reactivity 设置为
ALWAYS,或者如果属性 reactivity 设为ALLOWED时,才会支持该标签,相关事实类型使用@propertyReactive标签。您可以在 DRL 规则中使用该标签,在事实属性重新活动逻辑中添加或排除特定属性。默认参数: None
支持的参数:Property name, rhncfg (all),
!(not),! the !the no properties)<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( !* )如果您在事实类型中使用
@watch标签处理使用@classReactive标签(禁用属性重新活动),或者在决策引擎中将属性重新活动时,决策引擎会生成一个编译错误,相关事实类型不使用@propertyReactive标签。如果您在监听器注解中重复的属性(如@watch (firstName, ! firstName)),也会出现编译错误。- @propertyChangeSupport
对于实施对 TTY 规范中定义的属性更改的支持的事实,此标签可让决策引擎监控事实属性中的更改。https://download.oracle.com/otndocs/jcp/7224-javabeans-1.01-fr-spec-oth-JSpec/
示例:Aronlare 属性更改对 sVirt 对象的支持
declare Person @propertyChangeSupport end
14.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 还包括其他有用的方法,如一次性设置所有属性、从 映射 集合中读取值,或者一次性读取所有属性到 映射 集合中。
虽然 API 行为与 Java 反映类似,但 API 不使用反映性,并依赖于使用生成的字节代码实现的更多执行访问器。