规则引擎 Drools--决策表(Decision Table)使用简介

参考:规则引擎 Drools:决策表_jueyinga的博客-CSDN博客_drools 决策表

一、规则引擎 Drools:决策表

Drools除了支持drl形式的文件外还支持xls格式的文件(即Excel文件)。这种xls格式的文件通常称为决策表(decision table)。

决策表(decision table)是一个“精确而紧凑的”表示条件逻辑的方式,非常适合商业级别的规则。决策表与现有的drl文件可以无缝替换。Drools提供了相应的API可以将xls文件编译为drl格式的字符串。

1.1 决策表:

在这里插入图片描述
决策表语法:

关键字说明是否必须
RuleSet相当于drl文件中的package必须,只能有一个。如果没有设置RuleSet对应的值则使用默认值rule_table
Sequential取值为Boolean类型。true表示规则按照表格自上到下的顺序执行,false表示乱序可选
Import相当于drl文件中的import,如果引入多个类则类之间用逗号分隔可选
Variables相当于drl文件中的global,用于定义全局变量,如果有多个全局变量则中间用逗号分隔可选
RuleTable它指示了后面将会有一批rule,RuleTable的名称将会作为以后生成rule的前缀必须
CONDITION规则条件关键字,相当于drl文件中的when。下面两行则表示 LHS 部分,第三行则为注释行,不计为规则部分,从第四行开始,每一行表示一条规则每个规则表至少有一个
ACTION规则结果关键字,相当于drl文件中的then每个规则表至少有一个
NO-LOOP相当于drl文件中的no-loop可选
AGENDA-GROUP相当于drl文件中的agenda-group可选

在决策表中还经常使用到占位符,语法为$后面加数字,用于替换每条规则中设置的具体值。

上面的决策表例子转换为drl格式的规则文件内容如下:

package rules.excels;
//generated from Decision Table
import com.ppl.demo.entity.Account;
import java.util.List;
import java.util.ArrayList;
global List<String> list;
// rule values at B11, header at B6
rule "ExcelTable_11"
	salience 65535
	agenda-group "rule-group-001"
	when
		$account: Account(sex != "女")
	then
		list.add("性别不对");
end

// rule values at B12, header at B6
rule "ExcelTable_12"
	salience 65534
	agenda-group "rule-group-001"
	when
		$account: Account(age < 22 || age> 28)
	then
		list.add("年龄不合适");
end

// rule values at B13, header at B6
rule "ExcelTable_13"
	salience 65533
	agenda-group "rule-group-002"
	when
		$account: Account(balance < 1000)
	then
		list.add("工资太低");
end

Drools提供的将xls文件编译为drl格式字符串的API如下:

	@DisplayName("Excel To Rule")
	@Test
	public void testExceltoRule() {
		String realPath = "D:\\IDEA_Work\\excelrule001.xls";//指定决策表xls文件的磁盘路径
		File file = new File(realPath);
		InputStream is = null;
		try {
			is = new FileInputStream(file);
		} catch (FileNotFoundException e) {
			throw new RuntimeException(e);
		}
		SpreadsheetCompiler compiler = new SpreadsheetCompiler();
		String drl = compiler.compile(is, InputType.XLS);
		System.out.println(drl);
	}

Drools还提供了基于drl格式字符串创建KieSession的API:

KieHelper kieHelper = new KieHelper();
kieHelper.addContent(drl, ResourceType.DRL);
KieSession kieSession = kieHelper.build().newKieSession();

1.2 相关代码

package com.ppl.demo.entity;

public class Account {
    private long accountno;
    private double balance;
    private String sex;
    private int age;
    public Account(long accountno, double balance) {
        super();
        this.accountno = accountno;
        this.balance = balance;
    }

    public Account() {
        super();
        // TODO Auto-generated constructor stub
    }

    public long getAccountno() {
        return accountno;
    }

    public void setAccountno(long accountno) {
        this.accountno = accountno;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Account{" +
                "accountno=" + accountno +
                ", balance=" + balance +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                '}';
    }
}

测试类:

package com.ppl.demo;

import com.ppl.demo.entity.Account;
import com.ppl.demo.utils.KnowledgeSessionHelper;
import org.drools.decisiontable.InputType;
import org.drools.decisiontable.SpreadsheetCompiler;
import org.junit.jupiter.api.*;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.StatelessKieSession;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

@SpringBootTest
@DisplayName("junit5功能测试")
class RuleExcelsTests {

	static KieContainer kieContainer;
	StatelessKieSession sessionStateless = null;
	KieSession sessionStatefull = null;

	@BeforeEach
	void testBeforeEach() {
		kieContainer = KnowledgeSessionHelper.createRuleBase();
		System.out.println("测试就要开始。。。");
	}

	@AfterEach
	void testAfterEach() {
		System.out.println("测试就要结束。。。");
	}

	@BeforeAll
	static void testBeforeAll() {
		System.out.println("所有测试就要开始。。。");
	}

	@AfterAll
	static void testAfterAll() {
		System.out.println("所有测试已经结束。。。");
	}

	@DisplayName("Excel Rule")
	@Test
	public void testExcelRule() {
		sessionStatefull = KnowledgeSessionHelper
				.getStatefulKnowledgeSession(kieContainer, "lesson5-session");

		//设置全局变量,名称和类型必须和规则文件中定义的全局变量名称对应
		List<String> list = new ArrayList();
		sessionStatefull.setGlobal("list",list);

		Account account = new Account();
		account.setBalance(300D);
		account.setAge(35);
		account.setSex("男");
		sessionStatefull.insert(account);

		sessionStatefull.getAgenda().getAgendaGroup("rule-group-001").setFocus();
		int count=sessionStatefull.fireAllRules();

		//激活规则引擎,如果规则匹配成功则执行规则
		System.out.println("用户balance:"+account.getBalance());
		System.out.println("list:"+list);

		//关闭会话
		System.out.println("总共执行了: "+count+" 条规则");
		sessionStatefull.dispose();
	}

	@DisplayName("Excel To Rule")
	@Test
	public void testExceltoRule() {
		String realPath = "D:\\IDEA_Work\\excelrule001.xls";//指定决策表xls文件的磁盘路径
		File file = new File(realPath);
		InputStream is = null;
		try {
			is = new FileInputStream(file);
		} catch (FileNotFoundException e) {
			throw new RuntimeException(e);
		}
		SpreadsheetCompiler compiler = new SpreadsheetCompiler();
		String drl = compiler.compile(is, InputType.XLS);
		System.out.println(drl);
	}
}

POM文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.6.7</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.ppl</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
		<drools.version>7.73.0.Final</drools.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>2.6</version>
		</dependency>
		<!--drools规则引擎-->
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-core</artifactId>
			<version>${drools.version}</version>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-compiler</artifactId>
			<version>${drools.version}</version>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-templates</artifactId>
			<version>${drools.version}</version>
		</dependency>
		<dependency>
			<groupId>org.kie</groupId>
			<artifactId>kie-api</artifactId>
			<version>${drools.version}</version>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-decisiontables</artifactId>
			<version>${drools.version}</version>
		</dependency>
		<dependency>
			<groupId>org.kie</groupId>
			<artifactId>kie-spring</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-tx</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-beans</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-core</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-context</artifactId>
				</exclusion>
			</exclusions>
			<version>${drools.version}</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.24</version>
			<scope>compile</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

agenda-group关键字说明

  • 没有被focus的规则分组会运行规则条件,不运行规则结果。

  • 详细说明可能遇到情况,如果一个kieSession有多个规则,每个规则都包含agenda-group关键字,且值不完全一样会出现以下的情况,auto-focus没有配置的情况。

  • 直接fireAllRules,所有规则会运行一遍条件,也就是when到then中间的部分,不会运行结果,then后的部分。

  • focus某一个agenda-group,所有规则会运行一遍条件,也就是when到then中间的部分,只运行focus规则的结果部分。

  • 我的处理方案:将不同agenda-group的规则放在不同的KieBase中,保证每一个KieSession中只有一个agenda-group的数据。因为我的条件部分调用了很多工具类,如果都运行一遍会影响效率。

其他示例:
在这里插入图片描述
决策表转换成drl文件代码:

package rules.decision.tables;
//generated from Decision Table
import java.lang.StringBuilder;
import com.ppl.demo.entity.Student;
global java.lang.StringBuilder resultsInfo;



// rule values at B15, header at B10
rule "student-score-name-1"
/* 1、姓名为张三的特殊处理
2、自定义规则的名字 */
	salience 65535
	activation-group "score"
	when
		$stu: Student(name == "张三")
	then
		resultsInfo.append("张三特殊处理:");
System.out.println("规则:" + drools.getRule().getName() + " 执行了.");
		resultsInfo.append("优");
System.out.println("规则:" + drools.getRule().getName() + " 执行了.");
end

// rule values at B16, header at B10
rule "student-score_16"
	salience 65534
	activation-group "score"
	when
		$stu: Student(name == "李四", score > 0 && score < 60)
	then
		resultsInfo.append("李四部分特殊处理:");
System.out.println("规则:" + drools.getRule().getName() + " 执行了.");
		resultsInfo.append("一般");
System.out.println("规则:" + drools.getRule().getName() + " 执行了.");
end

// rule values at B17, header at B10
rule "student-score_17"
	salience 65533
	activation-group "score"
	when
		$stu: Student(score > 0 && score < 60)
	then
		resultsInfo.append("不及格");
System.out.println("规则:" + drools.getRule().getName() + " 执行了.");
end

// rule values at B18, header at B10
rule "student-score_18"
	salience 65532
	activation-group "score"
	when
		$stu: Student(score > 60 && score < 70)
	then
		resultsInfo.append("一般");
System.out.println("规则:" + drools.getRule().getName() + " 执行了.");
end

// rule values at B19, header at B10
rule "student-score_19"
	salience 65531
	activation-group "score"
	when
		$stu: Student(score > 70 && score < 90)
	then
		resultsInfo.append("良好");
System.out.println("规则:" + drools.getRule().getName() + " 执行了.");
end

// rule values at B20, header at B10
rule "student-score_20"
	salience 65530
	activation-group "score"
	when
		$stu: Student(score > 90 && score < 100)
	then
		resultsInfo.append("优");
System.out.println("规则:" + drools.getRule().getName() + " 执行了.");
end

测试代码


	@DisplayName("Excel decision.tables")
	@Test
	public void testExcelDecisionTables() {

		// 张三虽然只得20分,但是根据规则判断,结果应该是  优
		invokedDecisionTable(new Student("张三", 20));
		// 李四虽然只得20分,但是根据规则判断,结果应该是  一般
		invokedDecisionTable( new Student("李四", 20));
		// 李四得75分,但是根据规则判断,结果应该是  良好
		invokedDecisionTable( new Student("李四", 75));
		// 王五得59分,但是根据规则判断,结果应该是  不及格
		invokedDecisionTable( new Student("王五", 59));
		// 赵六得20分,但是根据规则判断,结果应该是  一般
		invokedDecisionTable( new Student("赵六", 65));
		// 钱七得20分,但是根据规则判断,结果应该是  良好
		invokedDecisionTable( new Student("钱七", 75));
		// 李八得20分,但是根据规则判断,结果应该是  优
		invokedDecisionTable(new Student("李八", 95));
	}

	public void invokedDecisionTable(Student student) {
		System.out.println("\r");
		sessionStatefull = KnowledgeSessionHelper
				.getStatefulKnowledgeSession(kieContainer, "lesson6-session");
		StringBuilder result = new StringBuilder();
		sessionStatefull.setGlobal("resultsInfo", result);
		sessionStatefull.insert(student);
		int count=sessionStatefull.fireAllRules();
		//激活规则引擎,如果规则匹配成功则执行规则
		System.out.println("总共执行了: "+count+" 条规则");
		sessionStatefull.dispose();
		System.out.println("规则执行结果:" + result);
	}

在这里插入图片描述
打印日志如下:

规则:student-score-name-1 执行了.
规则:student-score-name-1 执行了.
总共执行了: 1 条规则
规则执行结果:张三特殊处理:优

规则:student-score_16 执行了.
规则:student-score_16 执行了.
总共执行了: 1 条规则
规则执行结果:李四部分特殊处理:一般

规则:student-score_19 执行了.
总共执行了: 1 条规则
规则执行结果:良好

规则:student-score_17 执行了.
总共执行了: 1 条规则
规则执行结果:不及格

规则:student-score_18 执行了.
总共执行了: 1 条规则
规则执行结果:一般

规则:student-score_19 执行了.
总共执行了: 1 条规则
规则执行结果:良好

规则:student-score_20 执行了.
总共执行了: 1 条规则
规则执行结果:优
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值