繁体   English   中英

如何使drools文件中的全局变量同步?

[英]How to make a global variable in the drools file synchronous?

我正在开发一个 Spring Boot 应用程序,我在其中创建了一些 REST API。 有一个阅读实体,警报实体。 我正在通过 POST 方法发送读数,在ReadingsService类的postReadings方法中,我正在使用KIE检查读数是否符合某些标准,并将规则rules.drl文件。 我正在创建一个 Alert 对象并使用session.setGlobal方法将其设置为全局对象。 然后我检查警报对象是否为空并将其保存到警报存储库。 在 drools 文件中,我添加了一个打印语句来检查是否所有警报都已正确创建。 所有警报都通过打印语句正确打印,但是,只有其中一些被保存到警报存储库中。 有人可以帮忙吗?

这是阅读服务

@Service
public class ReadingsServiceImpl implements ReadingsService{

@Autowired
ReadingsRepository repository;

@Autowired
VehicleRepository vehicleRepo;

@Autowired
private KieSession session;

@Autowired
AlertRepository alertRepo;


@Override
public List<Readings> findAll() {
    return repository.findAll();
}

@Override
public Readings postReadings(Readings readings) {
    Alert alert = new Alert();
    session.setGlobal("alert", alert);
    session.insert(readings);
    session.fireAllRules();
    if(!Optional.ofNullable(alert).map(o -> o.getVin()).orElse("").isBlank()) {
        alertRepo.save(alert);
    }
    return repository.save(readings);
}

这是口水文件

import com.shiva.truckerapi.entity.Readings;
global com.shiva.truckerapi.entity.Alert alert;
rule "EngineCoolantOrCheckEngineLight"
when
    readingsObject: Readings(engineCoolantLow || checkEngineLightOn);
then
    alert.setVin(readingsObject.getVin());
    alert.setPriotity("LOW");
    alert.setDescription("Engine Coolant LOW Or Check Engine Light ON");
    alert.setTimestamp(readingsObject.getTimestamp());
    alert.setLatitude(readingsObject.getLatitude());
    alert.setLongitude(readingsObject.getLongitude());
    System.out.println(alert.toString());
end;

你的规矩没有错。 但是,您的保存由“if”语句保护,该语句检查是否存在非空白/非空 VIN,因此这可能会导致丢失保存的问题:

if(!Optional.ofNullable(alert).map(o -> o.getVin()).orElse("").isBlank()) {
  alertRepo.save(alert);
}

您可以轻松编写单元测试来验证此行为——如果警报存在(因为您之前在同一方法中对其进行了实例化,所以总是如此)并且存在 VIN,则它会保存; 如果警报存在并且没有不保存的 VIN。


这是一种非常老式的保存规则输出的方法。 现在我们通常在右手边的规则中使用带有副作用的动作,或者使用对象来传递信息。

您当前的设置甚至存在逻辑缺陷:您将始终有一个警报,因为您在将其添加到会话之前对其进行了实例化。

如果您想使用这样的全局变量,您需要向 Alert 类本身添加某种指示符,以指示已添加警报。 因此,例如,如果您只是使用适当的 getter 添加一个boolean added = false方法,您可以像这样更新您的规则:

rule "Engine Coolant Or Check Engine Light"
when
    readingsObject: Readings(engineCoolantLow || checkEngineLightOn);
then
    alert.setAdded(true); // Mark the alert as now being added
    alert.setVin(readingsObject.getVin());
    alert.setPriotity("LOW");
    alert.setDescription("Engine Coolant LOW Or Check Engine Light ON");
    alert.setTimestamp(readingsObject.getTimestamp());
    alert.setLatitude(readingsObject.getLatitude());
    alert.setLongitude(readingsObject.getLongitude());
    System.out.println(alert.toString());
end

然后您可以修复您的调用方法,以关闭非空对象的可空性,而是关闭您是否添加了警报:

public Readings postReadings(Readings readings) {
    Alert alert = new Alert();
    session.setGlobal("alert", alert);
    session.insert(readings);
    session.fireAllRules();
    if(alert.isAdded()) {
        alertRepo.save(alert);
    }
    return repository.save(readings);
}

(当然,我假设您在“存储库”实例中有适当的连接和事务处理。)

如果你仍然需要一个非空的 VIN,你应该在规则的左侧检查它; 像这样:

readingsObject: Readings( vin != null,
                          engineCoolantLow || checkEngineLightOn )

相关问题: 如何将drl文件的Cosequence中的值返回给java

Drools 在同一个(单个)线程中评估规则,除非你进行额外的配置。 因此postReadings()和 drools then block 将被同一个线程调用。 但是postReadings()可以在并发上下文中执行,并且它使用共享全局对象引用Alert alert (因为共享会话实例)。 Drools 与使您的ReadingsServiceImpl线程安全的顺序问题无关。 有很多方法可以实现这一点,第一个反模式是“全局变量”......不要在 drools 文件中使用共享对象,而是使用共享线程安全服务引用。 您可以使用不同的同步方法、线程局部变量使您的服务以线程安全的方式处理您的内部内容,但您真的需要共享对象吗?

import com.shiva.truckerapi.entity.Readings;
import com.shiva.truckerapi.entity.Alert;

global com.shiva.truckerapi.service.ReadingsService readingsService;

rule "EngineCoolantOrCheckEngineLight"
when
    readingsObject: Readings(engineCoolantLow || checkEngineLightOn);
then
    Alert alert = new Alert();
    alert.setVin(readingsObject.getVin());
    alert.setPriotity("LOW");
    alert.setDescription("Engine Coolant LOW Or Check Engine Light ON");
    alert.setTimestamp(readingsObject.getTimestamp());
    alert.setLatitude(readingsObject.getLatitude());
    alert.setLongitude(readingsObject.getLongitude());
    System.out.println(alert.toString());

    readingsService.onAlert(alert);
end;

服务变更

@PostConstruct
private void postConstruct() {
    session.setGlobal("readingsService", this);
}

@Override
public void postReadings(Readings readings) {
    session.insert(readings);
    session.fireAllRules();
    repository.save(readings);
}

@Override
public void onAlert(Alert alert) {
    // this looks ugly. There should not be produced 'invalid' alert, if vin is empty alert should not be generated by the rule itself
    if(!Optional.ofNullable(alert).map(o -> o.getVin()).orElse("").isBlank()) {
        alertRepo.save(alert);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM