RuleEngine -- 一款使用簡單,入門方便的數(shù)據(jù)庫規(guī)則引擎

規(guī)則引擎是嵌入在應(yīng)用程序中的組件遇八,實現(xiàn)了決策邏輯和業(yè)務(wù)系統(tǒng)的分離功能是掰。在現(xiàn)實業(yè)務(wù)場景中虑鼎,決策邏輯的復(fù)雜性和可變性,使得決策引擎的應(yīng)用越來越多,把決策邏輯單獨分離出來也顯得越來越重要了炫彩。

目前市場上常用的規(guī)則引擎有Ilog JRules匾七,Drools,Jess媒楼,Visual Rules等乐尊。Ilog JRules 是最有名的商用BRMS戚丸;Drools 是最活躍的開源規(guī)則引擎划址;Jess 是Clips的java實現(xiàn),就如JRuby之于Ruby限府,是AI系的代表夺颤; Visual Rules(旗正規(guī)則引擎)國內(nèi)商業(yè)規(guī)則引擎品牌。但是這些規(guī)則引擎都需要生成大量的bean類和Judgment類胁勺,在實現(xiàn)規(guī)則判斷的時候世澜,需要編寫大量的java代碼,或者使用rete規(guī)范署穗,另外編寫腳本寥裂。然后我們實際編程中,這些bean的數(shù)據(jù)大多數(shù)存于數(shù)據(jù)庫中案疲,規(guī)則引擎的判斷實際上是SQL腳本運行的一部分封恰。

因此在這里介紹一款可以使用SQL腳本來定義規(guī)則的中間件 -- RuleEngine。RuleEngine已經(jīng)登記在Maven中的中央庫中了褐啡,我們可以直接在POM.xml文件中包含就可以了诺舔。

<dependency>

? ? ? ? <groupId>com.github.hale-lee</groupId>

? ? ? ? <artifactId>RuleEngine</artifactId>

? ? ? ? <version>0.1.0</version>

? ? </dependency>

? 使用前,需要先配RuleEngine的配置文件ruleEngine.properties备畦,RuleEngine支持3種規(guī)則定義方式低飒,分別是1,數(shù)據(jù)庫table配置懂盐;2褥赊,xml文件配置;3莉恼,Drools的drl文件方式拌喉。

在ruleEngine.properties中設(shè)置rule.reader字段即可,下面分別說明:

1类垫, 數(shù)據(jù)庫table配置方式司光,在ruleEngine.properties中,設(shè)置rule.reader = database

此時需要配置下面的信息

a)? ? db.rule.table字段悉患,設(shè)置db.rule.table=tl_rule_define(表名)残家。此表結(jié)構(gòu)的定義在https://github.com/Hale-Lee/RuleEngine/tree/dev/referenc中有定義(有oralce和mysql的2種方式)。

b)? ? db.accesser數(shù)據(jù)連接方式售躁,RuleEngine提供了直接的jdbc連接坞淮,Druid連接池茴晋,Spring框架連接3種方式。 如果使用jdbc鏈接或者是Druid鏈接回窘,那么需要設(shè)置db.accesser=tech.kiwa.engine.utility.DirectDBAccesser(設(shè)置DirectDBAccesser的UseDruid可以區(qū)別是否使用Druid連接池诺擅,默認是true使用)。如果直接使用Spring框架的連接啡直,那么需要設(shè)置db.accesser= tech.kiwa.engine.utility. SpringDBAccesser烁涌。

RuleEngine也提供DBAccesser的接口,我們可以通過實現(xiàn)DBAccessor的接口的方法來自己獲得自己的連接酒觅。

c)? ? 如果使用jdbc鏈接或者是Druid鏈接撮执,那么需要配置jdbc屬性,或者是Druid的連接池參數(shù)舷丹,RuleEngine可以獨立配置連接池參數(shù)抒钱,也可以直接使用項目中現(xiàn)有的連接參數(shù)。

典型的配置文件的方式如下(使用Druid配置):

#數(shù)據(jù)驅(qū)動

jdbc.driver=com.mysql.cj.jdbc.Driver

#jdbc.driver=oracle.jdbc.driver.OracleDriver

#數(shù)據(jù)庫連接

jdbc.url=jdbc:mysql://127.0.0.1:3306/hosp?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=true

#數(shù)據(jù)庫用戶名

jdbc.username=oracle

jdbc.password=user

#規(guī)則定義的表

db.rule.table=TL_RULE_DEFINE

db.accesser=tech.kiwa.engine.utility.DirectDBAccesser

rule.reader=database

數(shù)據(jù)庫表結(jié)構(gòu)的定義如下:

-- Table structure for TL_RULE_DEFINE

?-- ----------------------------

?DROP TABLE "TL_RULE_DEFINE";

?CREATE TABLE "TL_RULE_DEFINE" (

?"ITEM_NO" NVARCHAR2(32) NOT NULL ,

?"CONTENT" NVARCHAR2(256) NULL ,

?"EXE_SQL" NVARCHAR2(512) NULL ,

?"EXE_CLASS" NVARCHAR2(128) NULL ,

?"PARAM_NAME" NVARCHAR2(128) NULL ,

?"PARAM_TYPE" NVARCHAR2(128) NULL ,

?"COMPARISON_CODE" NVARCHAR2(32) NULL ,

?"COMPARISON_VALUE" NVARCHAR2(64) NULL ,

?"BASELINE" NVARCHAR2(64) NULL ,

?"RESULT" NVARCHAR2(6) NULL ,

?"PRIORITY" NVARCHAR2(32) NULL ,

?"CONTINUE_FLAG" NVARCHAR2(2) NULL ,

?"PARENT_ITEM_NO" NVARCHAR2(2) NULL ,

?"PARENT_EXPRESS" NVARCHAR2(256) NULL ,

?"EXECUTOR" NVARCHAR2(64) NULL ,

?"REMARK" NVARCHAR2(64) NULL ,

?"COMMENTS" NVARCHAR2(64) NULL ,

?"ENABLE_FLAG" NVARCHAR2(2) NULL ,

?"CREATE_TIME" TIMESTAMP(6)? NULL ,

?"UPDATE_TIME" TIMESTAMP(6)? NULL

?)

?LOGGING

?NOCOMPRESS

?NOCACHE;



?COMMENT ON TABLE "TL_RULE_DEFINE" IS '規(guī)則引擎定義表';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."ITEM_NO" IS '主key';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."CONTENT" IS '中文的內(nèi)容說明';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."EXE_SQL" IS '執(zhí)行的SQL語句';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."EXE_CLASS" IS '執(zhí)行檢查的java類名颜凯, 與exe_sql二者只填寫一項';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."PARAM_NAME" IS 'SQL語句的參數(shù),多個參數(shù)用,分割谋币,讀值時需要完成和繼承DefaultCustomerCheck類。';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."PARAM_TYPE" IS 'exe_sql或者exe_class的參數(shù)類型症概,多個類型用逗號(,)分割蕾额,與param_name需一一對應(yīng)。';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."COMPARISON_CODE" IS '01: = 穴豫,? 02: > 凡简, 03 : < , 04 != 精肃, 05 >= 秤涩, 06: <= , 07 include 司抱, 08 exclude 筐眷, 09: included by 10: excluded by? 11: equal , 12 : not equal 13: euqalIngoreCase 15: matches 16: NOT MATCHES';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."COMPARISON_VALUE" IS '=,>,<,>=,<=, !=, include, exclude等內(nèi)容。';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."BASELINE" IS '參數(shù)值习柠,比較目標值';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."RESULT" IS '1 - 通過? 2 - 關(guān)注 3 -拒絕? 邏輯運算滿足目標值的時候讀取改內(nèi)容匀谣。';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."PRIORITY" IS '執(zhí)行的優(yōu)先順序,值大的優(yōu)先執(zhí)行.';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."CONTINUE_FLAG" IS '是否繼續(xù)執(zhí)行下一條资溃,如果某條規(guī)則滿足中斷的話武翎,那么就設(shè)置為 2. 1 -- 繼續(xù)? 2 -- 中斷';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."PARENT_ITEM_NO" IS '如果是子規(guī)則,那么需要填寫父規(guī)則的item_no';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."PARENT_EXPRESS" IS '同一PARENT_ITEM的各個ITEM的運算表達式溶锭。 ( A AND B OR C)';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."EXECUTOR" IS '結(jié)果執(zhí)行后的被執(zhí)行體宝恶,從AbstractCommand中繼承下來。';

?COMMENT ON COLUMN "TL_RULE_DEFINE"."ENABLE_FLAG" IS '是否使用 1 - 有效? 2 - 失效';


?-- ----------------------------

?-- Checks structure for table TL_RULE_DEFINE

?-- ----------------------------

?ALTER TABLE "TL_RULE_DEFINE" ADD CHECK ("ITEM_NO" IS NOT NULL);


其說明可以參考:


2, xml文件配置垫毙,xml文件配置的方式也需要在ruleEngine.properties中配置引擎的讀寫方式霹疫。僅僅配置兩項內(nèi)容即可。

rule.reader=xml

#指定規(guī)則文件的文件名综芥,RuleEngine會從classpath中搜索該文件丽蝎。

xml.rule.filename=ruleconfig.xml

XML文件的寫法與table類似,典型的xml文件格式為:

<?xml version="1.0" encoding="UTF-8"?>

<rules >

? <organization>

? ? ? <url>www.keaitupian.com</url>

? </organization>

? ? <description>

? ? ? ? Configuration for the rule list which stores the rule information in-memory and executed by rule engine service.

? ? </description>

? ? <rule id="totallist" exe_class="" method="" parent="">

? ? ? ? <property name="content" value="客戶身份證號碼規(guī)則"/>

? ? ? ? <property name="result" value="RESULT.REJECTED" desc="拒絕"/>

? ? ? ? <property name="continue_flag" value="1"/>

? ? ? ? <property name="group_express" value="(blacklist || graylist)"/>

? ? ? <property name="priority" value="10"/>

? ? </rule>

? ? <rule id="blacklist" parent="totallist">

? ? ? ? <property name="content" value="客戶身份證號碼命中內(nèi)部黑名單"/>

? ? ? ? <property name="exe_sql" value="select count(1) from customer_black_list where certificate_type >=1 and customer_no = ? and is_black = 1"/>

? ? ? ? <property name="param" value="CUSTOMER_NO" type="java.lang.String" desc="客戶編號"/>

? ? ? ? <property name="comparison_code" value="02"/>

? ? ? ? <property name="comparison_value" value="&gt;"/>

? ? ? ? <property name="baseline" value="0"/>

? ? ? ? <property name="baseline_desc" value="客戶的身份證號碼在黑名單表個數(shù)中大于0"/>

? ? </rule>

? ? <rule id="graylist" exe_class="" method="" parent="totallist">

? ? ? ? <property name="content" value="客戶身份證號碼命中內(nèi)部灰名單"/>

? ? ? ? <property name="exe_sql" value="select count(1) from customer_black_list where certificate_type =1 and customer_no = ? and is_gray = 1"/>

? ? ? ? <property name="param" value="CUSTOMER_NO" type="java.lang.String" desc="客戶編號"/>

? ? ? ? <property name="comparison_code" value="02"/>

? ? ? ? <property name="comparison_value" value="&gt;"/>

? ? ? ? <property name="baseline" value="0"/>

? ? ? ? <property name="baseline_desc" value="客戶的身份證號碼在黑名單表個數(shù)中大于0"/>

? ? </rule>

</rules>

3膀藐, Drools文件方式屠阻,RuleEngine同樣支持讀取Drools的drl文件中的規(guī)則,并且可以直接執(zhí)行其規(guī)則體消请。此時需要在ruleEngine.properties中配置引擎的讀寫方式栏笆。僅僅配置兩項內(nèi)容即可类腮。

rule.reader=drools

drools.rule.filename=sample.drl

Drools的文件樣式請參考具體Drools的文檔臊泰,典型的樣式為:

#this is a test

package tech.kiwa.engine.entity;

globals java.util.List myGlobalList

import tech.kiwa.engine.sample.Student;

function void callOver(Student $student){

? if($student != null){

? System.out.println("student [" +? $student.name + "] is called.");

? }

}

function void ageUp(Student $student, int age ){

? if($student != null){

? $student.setAge( $student.getAge() + age);

? }

}

declare teacher

? age : int

? name : String

? sex : int

end

query "juniorBoy"

? $student: Student( age <=14 && (age >10 || age !=12 , sex? ==1 || sex == 2 ), name =="tony")

end

query "querymale"(int $gender)

? $student: Student(sex == $gender)

end

rule "ageUp12"

salience 400

when

? $student: Student(age < 8)

? /* antoher rule */

then

? System.out.println("I was called, my name is : " + $student.name);

? ageUp($student,12);

? //callOver($student);

end

rule "isTom"

salience 30

date-expires "2018-12-01"

dialet "java"

when

? $student: Student(name == tom)

then

? $student.sex = 4;

? ? callOver($student);

end

啟動RuleEngine,RuleEngine是針對一個具體的目標對象(Bean)進行檢測的蚜枢,該目標對象(Bean)必須是可讀的缸逃,必須提供對應(yīng)的get方法,或者其成員變量是public的厂抽。比如學(xué)生對象需频,我們需要檢測該學(xué)生對象是否符合我們定義的規(guī)則。因此筷凤,在調(diào)用規(guī)則引擎前昭殉,必須存在該對象,如果有多個對象藐守,那請在循環(huán)體中執(zhí)行該條件挪丢。

示例代碼如下:

? ? ? ? EngineService service = new EngineService();

? ? ? ? try {

? ? ? ? ? ? Student st = new Student();? //建立學(xué)生對象

? ? ? ? ? ? st.setAge(5);

? ? ? ? ? ? st.name = "tom";

? ? ? ? ? ? st.sex = 1;

? ? ? ? ? ? EngineRunResult result = service.start(st);

? ? ? ? ? ? System.out.println(result.getResult().getName());

? ? ? ? ? ? System.out.println(st.getAge());

? ? ? ? } catch (RuleEngineException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

上述代碼中,Sutdent對象存在getAge方法卢厂,因此是可以被訪問的乾蓬,其規(guī)則也是可以執(zhí)行的,否則會拋出RuleEngineException異常慎恒,在對應(yīng)的規(guī)則中任内,執(zhí)行了ageUp()的Command操作。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末融柬,一起剝皮案震驚了整個濱河市死嗦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌粒氧,老刑警劉巖越除,帶你破解...
    沈念sama閱讀 223,126評論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡廊敌,警方通過查閱死者的電腦和手機铜跑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評論 3 400
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骡澈,“玉大人锅纺,你說我怎么就攤上這事±吲梗” “怎么了囤锉?”我有些...
    開封第一講書人閱讀 169,941評論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長护锤。 經(jīng)常有香客問我官地,道長,這世上最難降的妖魔是什么烙懦? 我笑而不...
    開封第一講書人閱讀 60,294評論 1 300
  • 正文 為了忘掉前任驱入,我火速辦了婚禮,結(jié)果婚禮上氯析,老公的妹妹穿的比我還像新娘亏较。我一直安慰自己,他們只是感情好掩缓,可當我...
    茶點故事閱讀 69,295評論 6 398
  • 文/花漫 我一把揭開白布雪情。 她就那樣靜靜地躺著,像睡著了一般你辣。 火紅的嫁衣襯著肌膚如雪巡通。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,874評論 1 314
  • 那天舍哄,我揣著相機與錄音宴凉,去河邊找鬼。 笑死蠢熄,一個胖子當著我的面吹牛跪解,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播签孔,決...
    沈念sama閱讀 41,285評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼叉讥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了饥追?” 一聲冷哼從身側(cè)響起图仓,我...
    開封第一講書人閱讀 40,249評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎但绕,沒想到半個月后救崔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惶看,經(jīng)...
    沈念sama閱讀 46,760評論 1 321
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,840評論 3 343
  • 正文 我和宋清朗相戀三年六孵,在試婚紗的時候發(fā)現(xiàn)自己被綠了纬黎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,973評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡劫窒,死狀恐怖本今,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情主巍,我是刑警寧澤冠息,帶...
    沈念sama閱讀 36,631評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站孕索,受9級特大地震影響逛艰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜搞旭,卻給世界環(huán)境...
    茶點故事閱讀 42,315評論 3 336
  • 文/蒙蒙 一散怖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧选脊,春花似錦杭抠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丹诀。三九已至钝的,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間铆遭,已是汗流浹背硝桩。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評論 1 275
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留枚荣,地道東北人碗脊。 一個月前我還...
    沈念sama閱讀 49,431評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像橄妆,于是被迫代替她去往敵國和親衙伶。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,982評論 2 361

推薦閱讀更多精彩內(nèi)容