一、背景
我們知道在drools
中是存在工作記憶體
的,我們的Fact
物件會加入到工作記憶體中,同時我們自己也可以在drl
檔案中使用insert/modify/update/delete
等方法,修改工作記憶體中物件的,那麼我們怎麼查詢修改之後的工作記憶體的值呢?而drools
的query
可以幫助我們實現這個功能。
二、需求
1、無引數query的使用
2、有引數query的使用
3、java程式碼中openLiveQuery
的使用
4、rule中使用query
三、前置需求
1、query的語法結構
query queryName(引數列表)
end
注意事項:
query的名字
在同一個KIE base的所有包中必須要唯一
,一般情況下我們全域性唯一即可。query
沒有when
和then
的部分
2、java中如何獲取query的結果
1、通過getQueryResults獲取
QueryResults queryResults = kieSession.getQueryResults("query的名字",可選引數類表);
通過這種方式getQueryResults
獲取到的結果只會獲取一次,如果工作記憶體中的資料發生了變化,則不會自動感知到。
2、通過openLiveQuery獲取
kieSession.openLiveQuery("query的名字", new Object[]{可選引數}, new ViewChangedEventListener() {
@Override
public void rowInserted(Row row) {}
@Override
public void rowDeleted(Row row) { }
@Override
public void rowUpdated(Row row) {}
});
通過這種方式openLiveQuery
是可以實時獲取到結果的,當工作記憶體中的資料發生變化,這個地方是可以感知到的。
四、實現
此處只列出部分核心程式碼,一些無關的程式碼不列出。
1、無引數query的使用
1、drl檔案編寫
// 不帶引數的查詢
query "query01"
// 注意這個地方的 $p,java程式碼中需要用到
$p: Person(age < 18)
end
2、java檔案編寫
// 不帶引數的query查詢
QueryResults queryResults = kieSession.getQueryResults("query01");
queryResults.iterator().forEachRemaining(row -> {
// 那麼這個地方的 $p 是怎麼來的呢?其實是我們自己編寫的drl query中寫的
Person person = (Person) row.get("$p");
log.info("query01從工作記憶體中獲取的query: {}", person);
});
2、有引數query的使用
1、drl檔案編寫
// 帶引數的查詢
query query02(Integer $age)
$p: Person(age < $age)
end
2、java檔案編寫
// 不帶引數的query查詢
// 帶引數的query查詢
queryResults = kieSession.getQueryResults("query02", 20);
queryResults.iterator().forEachRemaining(row -> {
Person person = (Person) row.get("$p");
log.info("query02從工作記憶體中獲取的query: {}", person);
});
3、java程式碼中openLiveQuery的使用
1、drl檔案編寫
// 帶引數的查詢-查詢工作記憶體Person物件的age的值小於外部傳遞進來的$age值
query query02(Integer $age)
$p: Person(age < $age)
end
// 定義一個規則,當規則記憶體中的Person的age小於18時,直接年齡+1
rule "rule_test_live_query_in_java"
no-loop true
when
$p: Person($age:age < 18)
then
modify($p){
// 此處修改了工作記憶體中age物件的值
setAge($p.getAge() + 1)
}
System.out.println("更新來規則記憶體中Person["+$p.getName()+"]的age:["+$p.getAge()+"]值");
end
解釋:
1、定義查詢query02
查詢工作記憶體中的物件。
2、rule_test_live_query_in_java
裡面存在一個 modify($p)
這個操作會導致更新工作記憶體中物件的值。
3、no-loop true
表達的是當前規則是否可以多次執行,就我們定義的這個規則,如果修改後的age<18
那麼可能還會導致規則的重新出發,加了no-loop true
則只會觸發一次。
2、java檔案編寫
public static void main(String[] args) {
KieServices kieServices = KieServices.get();
KieContainer kieContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession("query-ksession");
kieSession.addEventListener(new DebugRuleRuntimeEventListener());
kieSession.addEventListener(new DebugAgendaEventListener());
kieSession.addEventListener(new DebugProcessEventListener());
// 實時查詢
kieSession.openLiveQuery("query02", new Object[]{20}, new ViewChangedEventListener() {
@Override
public void rowInserted(Row row) {
Person person = (Person) row.get("$p");
log.info("實時查詢-query02向工作記憶體中插入Person: {}", person);
}
@Override
public void rowDeleted(Row row) {
Person person = (Person) row.get("$p");
log.info("實時查詢-query02向工作記憶體中刪除Person: {}", person);
}
@Override
public void rowUpdated(Row row) {
Person person = (Person) row.get("$p");
log.info("實時查詢-query02向工作記憶體中更新Person: {}", person);
}
});
Person person1 = new Person("張三", 16);
kieSession.insert(person1);
kieSession.fireAllRules();
kieSession.dispose();
}
解釋:
1、此處先使用了openLiveQuery
查詢。
2、讓後向工作記憶體中insert(person1)
,並且觸發了所有的規則fireAllRules
。
3、輸出結果
10:08:54.415 [main] INFO com.huan.drools.querys.DroolsLiveQueryApplication - 實時查詢-query02向工作記憶體中插入Person: Person(name=張三, age=16)
更新來規則記憶體中Person[張三]的age:[17]值
10:08:54.420 [main] INFO com.huan.drools.querys.DroolsLiveQueryApplication - 實時查詢-query02向工作記憶體中更新Person: Person(name=張三, age=17)
可以看到,openLiveQuery
實時查詢到了工作記憶體中變更的物件。
### 4、rule中使用query
drl
檔案編寫
// 定義一個查詢,Person#name 需要以$prefix開頭
query personNameStartsWith(String $prefix)
Person(name.startsWith($prefix))
end
rule "rule_person_name_starts_with"
when
$p: Person($age:age < 18)
personNameStartsWith("張";) // 此處多個引數使用 , 分割,並且最後必須以 ; 結尾
then
System.out.println("在rule中使用query");
end
如果出現瞭如下異常`Query's must use positional or bindings, not field constraints:
"張" : [Rule name='rule_person_name_starts_with'],這個是因為我們在
rule中呼叫
query時,引數沒有以
;結尾。正確用法
personNameStartsWith("張";)`
?personNameStartsWith("張";) 和 personNameStartsWith("張";)
是不一樣的。The ?
symbol means the query is pull only, once the results are returned you will not receive further results as the underlying data changes
五、完整程式碼
https://gitee.com/huan1993/spring-cloud-parent/tree/master/drools/drools-drl-query
六、參考連結
1、https://docs.drools.org/7.69.0.Final/drools-docs/html_single/index.html#drl-queries-con_drl-rules