当前位置: 首页 > 知识库问答 >
问题:

持久感知 KieSession 在事务期间不使用悲观锁

盖斌
2023-03-14

我在Spring Boot 2.3中使用Drools,并且实现了持久感知的会话,其中MySQL用于存储会话。我已经成功地将Spring Boot的默认< code > entitymanager factory 与Drools集成在一起,但是我的问题是事务。默认情况下,Drools在事务期间使用乐观锁,但是它也允许我们使用悲观锁,这正是我想要的。现在,在触发规则时,Drools使用以下查询持久化/更新MySQL中的KieSession:

update SessionInfo set lastModificationDate=?, rulesByteArray=?, startDate=?, OPTLOCK=? where id=? and OPTLOCK=?

现在,如果我不使用方法中的@Transactional注释的事务,则上述语句将执行两次,如果使用@Transactional,则在触发规则后仅执行一次上述语句。

现在,如果我手动更改OPTLOCK字段的值,Drools会抛出异常

javax.persistence.OptimisticLockException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [org.drools.persistence.info.SessionInfo#1]

然后:

Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [org.drools.persistence.info.SessionInfo#1]

由于此处的文本长度限制,我无法发布整个Stacktrace。可以在此GitHub项目中查看整个堆栈跟踪。

我不确定Drools是否使用了环境中定义的悲观锁。关于我的会话实现,我希望有一个< code>KieSession,因为我将KieSession用作< code>Bean。

以下是我的实现:

配置类:

@Configuration
public class DynamicDroolsConfig {

    private KieServices kieServices;
    private KieFileSystem kieFileSystem;

    @Autowired
    private PersistentSessionDAO persistentSessionDAO;
    @PersistenceUnit
    private EntityManagerFactory entityManagerFactory;
    @Autowired
    private PlatformTransactionManager platformTransactionManager;


    @PostConstruct
    private void init() {
        this.kieServices = KieServices.Factory.get();
        this.kieFileSystem = kieServices.newKieFileSystem();
    }

    @Bean
    public KieServices getKieServices() {
        return this.kieServices;
    }

    @Bean
    public KieContainer getKieContainer() {
        kieFileSystem.write(ResourceFactory.newClassPathResource("rules/rules.drl"));
        final KieRepository kieRepository = kieServices.getRepository();
        kieRepository.addKieModule(kieRepository::getDefaultReleaseId);
        KieBuilder kb = kieServices.newKieBuilder(kieFileSystem).buildAll();
        KieModule kieModule = kb.getKieModule();
        return kieServices.newKieContainer(kieModule.getReleaseId());
    }

    @Bean
    public KieFileSystem getFileSystem() {
        return kieFileSystem;
    }

    @Bean
    public KieSession kieSession() {
        List<SessionInfo> sessionDetails = persistentSessionDAO.getSessionDetails();

        if (sessionDetails.size() == 0) {
            return kieServices.getStoreServices().newKieSession(getKieContainer().getKieBase(), null, getEnv());
        } else {
            return kieServices.getStoreServices().loadKieSession(sessionDetails.get(0).getId(), getKieContainer().getKieBase(), null, getEnv());
        }
    }

    private Environment getEnv() {
        Environment env = kieServices.newEnvironment();
        env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, entityManagerFactory);
        env.set(EnvironmentName.TRANSACTION_MANAGER, platformTransactionManager);
        env.set(EnvironmentName.USE_PESSIMISTIC_LOCKING, true);
        env.set(EnvironmentName.USE_PESSIMISTIC_LOCKING_MODE, LockModeType.PESSIMISTIC_FORCE_INCREMENT.name());
        return env;
    }
}

控制器类:

@RestController
public class MyController {

    @Autowired
    private KieSession kieSession;

    @Transactional
    @GetMapping("fire-person")
    public void firePerson() {
        Person person = new Person();
        person.setName("Christy");
        kieSession.insert(person);
        kieSession.fireAllRules();
    }
}

事实类

public class Person implements Serializable {

    private String name;
    private int age;
    private String gender;
    private String toCompareName;
    private String toCompareGender;

    // getters and setters
}

存储库界面:

public interface DroolsSessionRepository extends JpaRepository<SessionInfo, Long> {
}

服务类别:

@Service
public class PersistentSessionDAO {

    @Autowired
    private DroolsSessionRepository droolsSessionRepository;

    public List<SessionInfo> getSessionDetails() {
        return droolsSessionRepository.findAll();
    }
}

跑步者等级:

@EntityScan(basePackages = {"com.sam.springdroolspersistence.entity", "org.drools.persistence.info"})
@EnableJpaRepositories
@SpringBootApplication
public class SpringDroolsPersistenceApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringDroolsPersistenceApplication.class, args);
    }
}

使用的Drools依赖项:

        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-persistence-jpa</artifactId>
            <version>${drools-version}</version>
        </dependency>

        <dependency>
            <groupId>org.kie</groupId>
            <artifactId>kie-spring</artifactId>
            <version>${drools-version}</version>
        </dependency>

        <dependency>
            <groupId>org.jbpm</groupId>
            <artifactId>jbpm-persistence-jpa</artifactId>
            <version>${drools-version}</version>
        </dependency>

代码实现也可以在这个GitHub项目中找到。任何帮助/建议都将不胜感激。谢谢你。

共有1个答案

杨慎之
2023-03-14

悲观锁定仅在JBPM中实现

Drools持久性中没有这种功能,< code>SessionInfo将始终使用基于JPA的< code>@Version注释的OptimisticLocking。

如果您需要此类功能,请在Drools的Jira上提交功能请求

 类似资料:
  • 我需要使用悲观锁执行批处理操作,以便在此操作期间没有其他人可以读取或写入此行。但是我想在每批之后增加页码,以便如果事务失败或实例死亡,我将从最后一页继续。但是使用下面的代码,它不会更新页面,直到所有批处理完成,所以当重新启动作业时,它会从页面=0开始处理。 有没有办法在每批启用悲观_写后更新页面? 提前谢谢。

  • 我在一个游戏服务器上使用Hibernate for ORM,刚刚从每个工作单元的会话/事务切换到每个请求的会话/事务。 由于多个事务可能相互冲突,因此我使用悲观锁定。问题是我经常遇到僵局。现在我的问题是,是否可以在锁定特定实体实例的事务中显式定义范围,或者在指定锁定模式后是否锁定实例直到我提交事务? 如果后者成立,如果我不能任意调度锁定数据库中资源的方法的方法调用,我如何避免每个请求环境中的会话事

  • 我正在使用Spring Boot、JPA、Oracle 12C和下面的类型化查询来选择要处理的“新”项目。一旦我选择了“新”项目,我就会更新其状态,使其不再符合选择条件,但我看到一个并发问题,相同的项目被选中。 我在这里读到,我需要设置一个'LockModeType.PESSIMISTIC_WRITE'的查询,以防止其他线程选择相同的行,但它似乎不起作用。 我是否遗漏了下面的内容,或者我是否需要另

  • 我使用Spring缓存抽象使用Ehache作为缓存提供程序。我试图将缓存操作附加到Spring JPA事务,但无法这样做。 即使事务失败/回滚缓存放发生。 配置, springcache-abs-ehcache。xml, 雇员安置处, 事务方法, 测试用例(调用者), 测试应该成功,也就是说,如果事务在该方法中回滚,Spring不应该将数据放入方法中的缓存中。但是,即使事务失败,Spring也会将

  • 我知道悲观锁定与锁定数据库记录,并在获得锁的“交易”结束时释放它。但这是否意味着悲观锁定是在物理交易中,例如 开始交易 提交事务? 对于一个网页,当用户选择要编辑的记录时,当他按下编辑按钮时,我想悲观地锁定此记录,以便其他人无法更改它,然后在编辑按钮onpress()事件中,我开始物理事务? 似乎是不可能的,因为编辑过程可能很长......它在整个编辑过程中保持数据库事务(按编辑按钮,在网页中编辑

  • 我创建了一个扩展服务并作为前台服务运行的类。我希望我的服务通知是持久的(即不通过刷卡删除)。但是,我的通知可以通过刷卡来撤销。 服务留档指出:...前台服务必须为状态栏提供通知,状态栏放在正在进行的标题下。这意味着除非服务停止或从前台删除,否则通知不能被驳回... 我确实设置了断点来检查是否命中了onDestroy()或stop Self(),但事实并非如此。该服务正在前台模式下运行,但我可以通过