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

Java 如何使drools文件中的全局变量同步?,java,spring-boot,rest,drools,kie,Java,Spring Boot,Rest,Drools,Kie,我正在开发一个springboot应用程序,在那里我创建了一些restapi。有一个读数实体,警报实体。我通过POST方法发送读数,在ReadingService类的postReadings方法中,我使用KIE检查读数是否符合某些标准,并将规则写入rules.drl文件中。我正在创建一个警报对象,并使用session.setGlobal方法将其设置为全局对象。然后我检查警报对象是否为null,并将其保存到警报存储库。在drools文件中,我添加了一个print语句来检查是否正确创建了所有警报。所

我正在开发一个springboot应用程序,在那里我创建了一些restapi。有一个读数实体,警报实体。我通过POST方法发送读数,在
ReadingService
类的
postReadings
方法中,我使用
KIE
检查读数是否符合某些标准,并将规则写入
rules.drl
文件中。我正在创建一个警报对象,并使用
session.setGlobal
方法将其设置为全局对象。然后我检查警报对象是否为null,并将其保存到警报存储库。在drools文件中,我添加了一个print语句来检查是否正确创建了所有警报。所有警报都通过print语句正确打印,但是,只有部分警报被保存到警报存储库中。有人能帮忙吗

这是阅读服务

@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);
}

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

if(!Optional.ofNullable(alert).map(o -> o.getVin()).orElse("").isBlank()) {
  alertRepo.save(alert);
}
您可以很容易地编写单元测试来验证这种行为——如果警报存在(因为您之前在同一方法中实例化了它,所以总是存在),并且存在VIN,那么它会保存;如果警报存在且没有VIN,则无法保存


这是一种非常老式的保存规则输出的方法。现在,我们通常在规则的右侧使用具有副作用的操作,或者使用对象传回信息

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

如果要使用这样的全局警告,则需要向警报类本身添加某种指示符,以指示已添加警报。因此,例如,如果您只需添加一个带有适当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
readingsObject: Readings( vin != null,
                          engineCoolantLow || checkEngineLightOn )
然后,您可以将调用方法修复为不关闭非null对象的可空性,而是关闭是否添加了警报:

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,则应在规则的左侧检查该VIN;大概是这样的:

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
readingsObject: Readings( vin != null,
                          engineCoolantLow || checkEngineLightOn )

相关问题:

Drools在同一(单个)线程中计算规则,除非您进行其他配置。因此,同一线程将调用
postdreads()
和droolsthen块。但是
postreads()
可以在并发上下文中执行,并且它使用共享全局对象引用
警报
(因为共享会话实例)。Drools与使您的
ReadingServiceImpl
线程安全的顺序问题无关。实现这一点有很多方法,第一个反模式是“全局变量”。。。不要在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);
    }
}

我添加了一个同步块,它成功了。然而,我想知道是否有更好的方法来做这件事。谢谢你的回复。我尝试了你的方法,但问题仍然存在。根据我的理解,在将alert对象插入会话后,下一条“保存到repo”语句不会等待drools文件中的语句执行,因为这是异步执行。我在PostReaders方法中包含了一个同步块,一切都很好。您能告诉我一种更好的方法将警报对象传回PostReaders方法,而不是像我那样使用全局变量吗?听起来问题在于您的事务管理,而不是事务部分的规则,我只是扩展了JpaRepository,没有覆盖任何方法。我只是使用默认实现。然而,当我添加一个同步块时,一切都很好。只是想知道是否有其他更好的方法来实现它。如果你有时间,我可以分享所有的代码,让你看看。这里没有线程。如果它是由同步块修复的,那么它就是代码中的其他部分。好的,我将尝试调试我的代码并再次重写它。谢谢你的回复。我不知道如何实现它,并使用了一个共享对象。我需要在drools文件中再编写三条规则,如果满足某个条件,每条规则都应该创建一个警报对象,并且所有这些警报对象都应该保存到
警报存储库
。你能告诉我如何处理这个问题吗?对于第一个问题,我现在无法投票,因为我没有足够的声誉去投票。我试着投票,收到了这样一条信息:“谢谢你的反馈!声誉低于15%的人所投的票会被记录下来,但不会改变公开显示的帖子分数。“关于第二个问题,我逐行阅读了你的答案很多次。你清楚地解释了drools部分,但我在执行时出错了。我想我在服务部分犯了一些错误,我正在努力找出原因。请参阅更新的答案。如果出现错误,请共享堆栈跟踪