drools的型別宣告(Type declarations)

huan1993發表於2022-05-17

一、背景

在我們編寫drl規則的時候,有些時候需要自己宣告一些類,用於輔助之後的規則執行,如果需要用到的類還需要在java中預先宣告出來,這樣就不靈活了,那麼是否可以在drl檔案中宣告一個類呢?可以使用drools的 Type declaration來實現。

二、前置知識

1、Type declaration語法結構

語法結構

2、java程式碼中獲取drl宣告的型別

1、非列舉型別

KieBase kieBase = kieContainer.getKieBase("type-kabse");
                                                // 規則檔案的包名  宣告的型別名
FactType productOrderFactType = kieBase.getFactType("rules", "ProductOrder");
Object instance = productOrderFactType.newInstance();
productOrderFactType.set(instance, "orderId", 20220517121212001L);

2、獲取列舉型別的值

需要通過反射來獲取到。

KieBase kieBase = kieContainer.getKieBase("type-kabse");
// 此處的FactType的真實型別是EnumClassDefinition,可以獲取到列舉中構造方法的引數的值
FactType orderStatusFactType = kieBase.getFactType("rules", "OrderStatus");

// 獲取drools中列舉的值
Class<?> factClass = orderStatusFactType.getFactClass();
Method method = factClass.getMethod("valueOf", String.class);
Object pay = method.invoke(null, "PAY");

注意:
如果上方的程式碼看不懂,則接著往下看。

三、需求

1、在drl檔案中宣告一個列舉型別。
2、在drl檔案中宣告一個類。
3、在drl檔案中宣告一個類並完成繼承操作。
4、編寫rule並使用我們自定義的type。
5、java中給在drl檔案中宣告的type賦值,包括類和列舉型別。

四、實現

1、在drl檔案中宣告一個列舉型別

// 宣告一個列舉型別
declare enum OrderStatus
    CREATED(0, "新建立"),
    PAY(1, "已支付"),
    RECEIVED(2, "已接收");

    status: Integer;
    desc: String;
end

語法結構: declare enum 列舉名字 end

2、在drl檔案中宣告一個類

// 宣告一個類
declare BaseOrder
    orderId: Long  // 訂單id
    createdTime: Date // 時間
    item: ProductItem // java中定義的類
    orderStatus: OrderStatus // 上方定義的列舉型別
end

這個類中的每個屬性都有一個型別,這個型別可以是已經存在的fact,也可以是任何有效的Java型別。

3、在drl檔案中宣告一個類並完成繼承操作

// 實現繼承操作
declare ProductOrder extends BaseOrder
    userId: Long // 下單使用者的id
end

使用extends來完成繼承操作。

4、編寫rule並使用我們自定義的type

// 定義一個規則,規則記憶體中存在ProductOrder並且 orderStatus是已支付userId==1001
rule "rule_type"
    no-loop true
    when
        $order: ProductOrder(userId == 1001 && orderStatus == OrderStatus.PAY)
    then
        String createdTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                                .format($order.getCreatedTime());
        System.out.println("使用者["+ $order.getUserId() +"]在["+ createdTime +"]購買的["+ $order.getItem().getItemName() +"]已完成付款");
        modify($order){
            setOrderStatus(OrderStatus.RECEIVED)
        }
end

解釋:
1、如果規則記憶體中存在ProductOrder物件,並且userId的值是1001orderStatus的值是PAY則該規則被啟用了。
2、當該規則啟用時,修改訂單的狀態為RECEIVED,在java程式碼中獲取修改後的值。

5、java中給在drl檔案中宣告的type賦值

@Slf4j
public class DroolsTypeDeclareApplication {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        KieServices kieServices = KieServices.get();
        KieContainer kieContainer = kieServices.getKieClasspathContainer();
        KieSession kieSession = kieContainer.newKieSession("type-ksession");
        kieSession.addEventListener(new DebugRuleRuntimeEventListener());
        kieSession.addEventListener(new DebugAgendaEventListener());
        kieSession.addEventListener(new DebugProcessEventListener());

        KieBase kieBase = kieContainer.getKieBase("type-kabse");
        FactType productOrderFactType = kieBase.getFactType("rules", "ProductOrder");
        FactType orderStatusFactType = kieBase.getFactType("rules", "OrderStatus");

        // 獲取drools中列舉的值
        Class<?> factClass = orderStatusFactType.getFactClass();
        Method method = factClass.getMethod("valueOf", String.class);
        Object pay = method.invoke(null, "PAY");

        Object instance = productOrderFactType.newInstance();

        ProductItem item = new ProductItem();
        item.setItemName("iphone 13");

        productOrderFactType.set(instance, "orderId", 20220517121212001L);
        productOrderFactType.set(instance, "createdTime", new Date());
        productOrderFactType.set(instance, "item", item);
        productOrderFactType.set(instance, "orderStatus", pay);
        productOrderFactType.set(instance, "userId", 1001L);

        kieSession.insert(instance);
        kieSession.fireAllRules();

        Object orderStatus = productOrderFactType.get(instance, "orderStatus");
        log.info("獲取rule中修改之後的列舉欄位的值:[{}]", orderStatus);

        kieSession.dispose();
    }
}

注意:
1、在java中獲取到drl檔案中宣告的型別,需要使用 kieBase.getFactType來獲取。
2、如果需要獲取到drl檔案中申明的列舉型別的值,可以通過反射來獲取。

6、執行上方的程式碼

使用者[1001]在[2022-05-17 11:42:27]購買的[iphone 13]已完成付款
11:42:27.724 [main] INFO com.huan.drools.querys.DroolsTypeDeclareApplication - 獲取rule中修改之後的列舉欄位的值:[RECEIVED]

可以看到規則執行了,並且java中也獲取到了工作記憶體中修改後的值。

五、完整程式碼

https://gitee.com/huan1993/spring-cloud-parent/tree/master/drools/drools-drl-type-declarations

六、參考連結

1、https://docs.drools.org/7.69.0.Final/drools-docs/html_single/index.html#drl-declarations-con_drl-rules

相關文章