搜索

16.5. DRL 中的类型声明和元数据

download PDF

DRL 文件中的声明定义了新的事实类型或元数据,以便供 DRL 文件中的规则使用的事实类型:

  • 新事实类型: 红帽决策管理器的默认事实 类型是 对象,但您可以根据需要声明 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

支持的参数: truefalse

@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

在本例中,决策引擎检查 firstNamelastName 属性,以确定 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

在本例中,属性按以下顺序在位置参数中排列优先级:

  1. lastName
  2. firstName
  3. age
  4. 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

在本例中,属性按以下顺序在位置参数中排列优先级:

  1. lastname (在父类型中的位置 0)
  2. 院校 (子类型中的位置 0)
  3. FirstName (在父类型中的位置 1)
  4. 程度 (子类型中的位置 1)
  5. age (在父类型中的位置 2)
  6. occupation (没有位置注解的前字段)
  7. 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 Decision 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 Decision 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 不使用反射,它依赖于通过生成的字节码实施的更高性能访问器。

Red Hat logoGithubRedditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

© 2024 Red Hat, Inc.