drools版本:7.0
語言:java8
描述:動態從數據庫中加載規則,並且加載到工作內存中
最終實現效果:從數據庫中讀取數據生成drl格式的字符串以後,可以一次性加載到工作內存中,也可以逐次加載到內存中進行build,而之前已經build好的規則不會消失。
下面是具體實現過程:
首先drools提供了常見的兩種加載規則的方式,一種是通過定義kmodule.xml的方式進行加載,並且在對應的package下寫好drl規則文件即可,xml文件如下,規則文件略。
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.drools.org/xsd/kmodule">
<kbase name="rule1KB" packages="rules">
<ksession name="rule1KS"/>
</kbase>
</kmodule>
通過這種xml加載的方式在具體java代碼中獲取session會話的方式是這樣的:
KieContainer kc =KieServices.Factory.get().getKieClasspathContainer("rule1KB");
KieSession kieSession = kc.newKieSession("rule1KS");
注意獲取KieContainer 和KieSession 的名稱和xml中的對應。
但是在實際的動態加載中,drl格式的文件極有可能不是寫好了的,而是從數據庫中取出來數據,然後通過拼接字符串的方式動態生成的(至少我們現在這個項目就要這樣做(_))。
所以採用這種方式就有問題了,好在drools官方提供了另一種加載規則文件的方式,具體代碼如下:
public KieContainer loadForRule(String drlStr) {
KieServices ks = KieServices.Factory.get();
KieRepository kr = ks.getRepository();
KieFileSystem kfs = ks.newKieFileSystem();
kfs.write("src/main/resources/rules/" + drlStr.hashCode() + ".drl", drlStr);
// 將KieFileSystem加入到KieBuilder
KieBuilder kb = ks.newKieBuilder(kfs);
// 編譯此時的builder中所有的規則
kb.buildAll();
if (kb.getResults().hasMessages(Message.Level.ERROR)) {
throw new RuntimeException("Build Errors:\n" + kb.getResults().toString());
}
return ks.newKieContainer(kr.getDefaultReleaseId());
}
通過上面這段代碼實際上已經可以通過動態加載的方式拿到了一個KieContainer 類,繼續通過KieContainer 獲取到KieSession會話即可(實際上創建一次會話代價極低),具體一行代碼如下:
KieSession kieSession = kContainer.newKieSession();
但是通過上面這段代碼,每次都是重新加載,會出現這樣的問題:現在加載了A規則,再去加載B規則,A規則已經不存在與工作內存中了,這個問題簡單思考一下應該就是每次的Kie裏面的對象都是重新new的,所以每次都是重新保存,這明顯和實際要求不合,最直接想到的應該就是緩存一個KieContainer 方法,但是這種方式沒有成功,最後採用全局緩存了kieFileSystem和kieRepository進行處理。具體代碼如下(省略了get/set方法):
public class KieUtils {
private static KieContainer kieContainer;
private static KieSession kieSession;
private static KieServices kieServices;
private static KieRepository kieRepository;
private static KieFileSystem kieFileSystem;
public static void initAndNotClear(){
if (Objects.isNull(kieServices))
kieServices = KieServices.Factory.get();
if (Objects.isNull(kieRepository))
kieRepository = kieServices.getRepository();
if (Objects.isNull(kieFileSystem))
kieFileSystem = kieServices.newKieFileSystem();
}
}
使用上面這個類進行緩存,將上面的代碼獲取對應類的地方替換回去,比如:
KieUtils.initAndNotClear();
KieFileSystem kfs = KieUtils.getKieFileSystem();
對於這種方式的思考:
這種方式實際上還是build了KieFileSystem 中的所有的規則,實際上是一種假的添加新的規則進原規則體系中編譯的方式,但實際解決了問題,並且因爲動態生成最耗時間的地方在生成字符串,這種方式字符串是不用重新生成了,一般測試編譯所有規則的時間是以秒級單位的,並且RETE算法本身就是一種以空間換取時間的算法,只要在規則觸發時速度夠快,編譯時間稍微多耗一點,還是可以接受的。
以上爲轉載:https://www.jianshu.com/p/371d4a2a5866