无法在 Spring Boot 中实现 Drools KieSession Persistence
Posted
技术标签:
【中文标题】无法在 Spring Boot 中实现 Drools KieSession Persistence【英文标题】:Cannot implement Drools KieSession Persistence in Spring Boot 【发布时间】:2021-01-21 02:17:00 【问题描述】:我试图在 Spring Boot Maven 项目中使用 KieSession
的持久性功能实现 Drools。关注this documentation 进行实施。能够在普通 Java 应用程序中执行此操作,但在 Spring Boot 应用程序中尝试执行此操作时遇到异常。
下面是实现。
项目结构
配置类
@Configuration
public class PersistentDroolConfig
public static Long KIE_SESSION_ID;
private final KieServices kieServices = KieServices.Factory.get();
@Bean
public KieSession kieSession()
KieSession kieSession = kieServices.getStoreServices().newKieSession(getKieBase(), null, getEnv());
PersistentDroolConfig.KIE_SESSION_ID = kieSession.getIdentifier();
return kieSession;
public KieServices getKieServices()
initDataSource();
return kieServices;
public KieBase getKieBase()
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
kieFileSystem.write(ResourceFactory.newClassPathResource("rules/rules.drl"));
final KieRepository kieRepository = kieServices.getRepository();
kieRepository.addKieModule(kieRepository::getDefaultReleaseId);
KieBuilder kb = kieServices.newKieBuilder(kieFileSystem);
kb.buildAll();
KieModule kieModule = kb.getKieModule();
KieContainer kieContainer = kieServices.newKieContainer(kieModule.getReleaseId());
return kieContainer.getKieBase();
public Environment getEnv()
Environment env = kieServices.newEnvironment();
env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, Persistence.createEntityManagerFactory("org.drools.persistence.jpa"));
env.set(EnvironmentName.TRANSACTION_MANAGER, TransactionManagerServices.getTransactionManager());
return env;
private void initDataSource()
PoolingDataSource ds = new PoolingDataSource();
ds.setUniqueName("jdbc/BitronixJTADataSource");
ds.setClassName("com.mysql.cj.jdbc.MysqlXADataSource");
ds.setMaxPoolSize(3);
ds.setAllowLocalTransactions(true);
ds.getDriverProperties().put("user", "root");
ds.getDriverProperties().put("password", "1234");
ds.getDriverProperties().put("URL", "jdbc:mysql://localhost:3306/drool_demo");
ds.init();
控制器类
@RestController
public class OfferController
@Autowired
private KieSession kieSession;
@GetMapping("/order/card-type/price")
public Order order(@PathVariable("card-type") String cardType, @PathVariable int price)
Order order = new Order(cardType, price);
kieSession.insert(order);
kieSession.fireAllRules();
return order;
事实类
public class Order implements Serializable
private String name;
private String cardType;
private int discount;
private int price;
public Order(String cardType, int price)
this.cardType = cardType;
this.price = price;
// setters and getters
// toString()
persistence.xml
<persistence version="2.0"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd
http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="org.drools.persistence.jpa"
transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/BitronixJTADataSource</jta-data-source>
<class>org.drools.persistence.info.SessionInfo</class>
<class>org.drools.persistence.info.WorkItemInfo</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.max_fetch_depth" value="3" />
<property name="hibernate.hbm2ddl.auto" value="create" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.BTMTransactionManagerLookup" />
</properties>
</persistence-unit>
</persistence>
pom.xml文件中包含的依赖如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.github.marcus-nl.btm</groupId>
<artifactId>btm</artifactId>
<version>3.0.0-mk1</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-decisiontables</artifactId>
<version>$drools-version</version>
</dependency>
<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-persistence-jpa</artifactId>
<version>$drools-version</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
错误堆栈跟踪:
Caused by: org.hibernate.engine.jndi.JndiException: Unable to lookup JNDI name [jdbc/BitronixJTADataSource]
Caused by: javax.naming.NameNotFoundException: unable to find a bound object at name 'jdbc/BitronixJTADataSource'
这个项目也可以在这里this repository找到。
更新 1:
实施@jccampanero 的答案后,我有一个更新的堆栈跟踪:
Caused by: org.hibernate.HibernateException: Unable to perform isolated work
Caused by: java.sql.SQLSyntaxErrorException: Table 'drool_demo.sessioninfo_id_seq' doesn't exist
更新 2:
在进一步挖掘之后,我发现 Drools 由于一些语法错误而没有制作必要的表格。由于 *** 有文本限制,因此仅在此处发布了重要的异常消息。是这样的:
Hibernate: drop table if exists SessionInfo
Hibernate: drop table if exists WorkItemInfo
Hibernate: create table SessionInfo (id bigint not null auto_increment, lastModificationDate datetime, rulesByteArray longblob, startDate datetime, OPTLOCK integer, primary key (id)) type=MyISAM
2020-10-09 23:49:59.554 WARN 11376 --- [ main] o.h.t.s.i.ExceptionHandlerLoggedImpl : GenerationTarget encountered exception accepting command : Error executing DDL "create table SessionInfo (id bigint not null auto_increment, lastModificationDate datetime, rulesByteArray longblob, startDate datetime, OPTLOCK integer, primary key (id)) type=MyISAM" via JDBC Statement
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "create table SessionInfo (id bigint not null auto_increment, lastModificationDate datetime, rulesByteArray longblob, startDate datetime, OPTLOCK integer, primary key (id)) type=MyISAM" via JDBC Statement
Caused by: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'type=MyISAM' at line 1
Hibernate: create table WorkItemInfo (workItemId bigint not null auto_increment, creationDate datetime, name varchar(255), processInstanceId bigint not null, state bigint not null, OPTLOCK integer, workItemByteArray longblob, primary key (workItemId)) type=MyISAM
2020-10-09 23:49:59.556 WARN 11376 --- [ main] o.h.t.s.i.ExceptionHandlerLoggedImpl : GenerationTarget encountered exception accepting command : Error executing DDL "create table WorkItemInfo (workItemId bigint not null auto_increment, creationDate datetime, name varchar(255), processInstanceId bigint not null, state bigint not null, OPTLOCK integer, workItemByteArray longblob, primary key (workItemId)) type=MyISAM" via JDBC Statement
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "create table WorkItemInfo (workItemId bigint not null auto_increment, creationDate datetime, name varchar(255), processInstanceId bigint not null, state bigint not null, OPTLOCK integer, workItemByteArray longblob, primary key (workItemId)) type=MyISAM" via JDBC Statement
Caused by: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'type=MyISAM' at line 1
【问题讨论】:
听起来您缺少 jdbc 驱动程序。我不熟悉这个特定的“bitronix”数据源,但也许可以尝试将适当的 JDBC 驱动程序依赖项添加到您的 pom 中?或者,如果您不想使用它,请将您的配置更新为不同的:<jta-data-source>jdbc/BitronixJTADataSource</jta-data-source>
你的配置类有点乱。我会使用 bean 作为数据源,以便能够轻松地将数据源注入所有需要它的服务中,如果您查看堆栈跟踪的末尾:您的数据源似乎配置不正确,因此请使用数据库客户端检查您的数据库配置。
您的“更新”提出了一个完全不同的问题。 (关于这一点,您是否考虑过创建缺失的表?看起来您正在尝试使用序列表中自动生成的 id,但说序列表不存在。)
@RoddyoftheFrozenPeas 正在使用为 Spring Boot 提供的 Mysql 驱动程序。也使用了 spring boot jdbc 驱动程序。仍然是相同的堆栈跟踪。
@RoddyoftheFrozenPeas 表格将由 Drools 通过休眠自动生成。所以我使用<property name="hibernate.hbm2ddl.auto" value="create"/>
【参考方案1】:
我认为您的配置有问题。 PersistentDroolConfig
类的 getKieServices
方法永远不会被调用,因此,初始化数据源的方法 initDataSource
也不会被调用。
也许您可以尝试修改您的PersistentDroolConfig
,如下所示:
@Configuration
public class PersistentDroolConfig
public static Long KIE_SESSION_ID;
private KieServices kieServices;
@PostContruct
private void init()
this.initDataSource();
this.kieServices = KieServices.Factory.get();
@Bean
public KieSession kieSession()
KieSession kieSession;
if (KIE_SESSION_ID == null)
kieSession = createNewKieSession();
KIE_SESSION_ID = kieSession.getIdentifier();
return kieSession;
else
kieSession = getPersistentKieSession();
KIE_SESSION_ID = kieSession.getIdentifier();
return kieSession;
public KieBase getKieBase()
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
kieFileSystem.write(ResourceFactory.newClassPathResource("rules/rules.drl"));
final KieRepository kieRepository = kieServices.getRepository();
kieRepository.addKieModule(new KieModule()
@Override
public ReleaseId getReleaseId()
return kieRepository.getDefaultReleaseId();
);
KieBuilder kb = kieServices.newKieBuilder(kieFileSystem);
kb.buildAll();
KieModule kieModule = kb.getKieModule();
KieContainer kieContainer = kieServices.newKieContainer(kieModule.getReleaseId());
KieBase kieBase = kieContainer.getKieBase();
return kieBase;
public Environment getEnv()
Environment env = kieServices.newEnvironment();
env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, Persistence.createEntityManagerFactory("org.drools.persistence.jpa"));
env.set(EnvironmentName.TRANSACTION_MANAGER, TransactionManagerServices.getTransactionManager());
return env;
private KieSession createNewKieSession()
KieSession kieSession = kieServices.getStoreServices().newKieSession(getKieBase(), null, getEnv());
PersistentDroolConfig.KIE_SESSION_ID = kieSession.getIdentifier();
return kieSession;
private KieSession getPersistentKieSession()
return kieServices.getStoreServices().loadKieSession(KIE_SESSION_ID, getKieBase(), null, getEnv());
private void initDataSource()
PoolingDataSource ds = new PoolingDataSource();
ds.setUniqueName("jdbc/BitronixJTADataSource");
ds.setClassName("com.mysql.cj.jdbc.MysqlXADataSource");
ds.setMaxPoolSize(3);
ds.setAllowLocalTransactions(true);
ds.getDriverProperties().put("user", "root");
ds.getDriverProperties().put("password", "1234");
ds.getDriverProperties().put("URL", "jdbc:mysql://localhost:3306/drool_demo");
ds.init();
更新
如不同 cmets 所述,在进行这些更改后,出现了与用于生成实体 SessionInfo
的字段 id
的值的 SESSIONINFO_ID_SEQ
序列相关的问题。
问题似乎与使用的 Hibernate 和 MySQL 的版本有关。
要解决此问题,需要对persistence.xml
文件进行几处更改。
首先,应包含以下属性:
<property name="hibernate.id.new_generator_mappings" value="false" />
它经常用于 Drools 示例和test cases。请参阅 this 伟大的 Vlad Mihalcea 文章以获得深入的解释。
包含此配置属性产生了与所使用的 MySQL 方言相关的新问题。应该改为:
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
使用此配置,应用程序应该可以顺利运行。
【讨论】:
感谢您纠正错误。我有一个更新的堆栈跟踪。你能看一下吗?我也更新了问题。 一个可能的问题是 Hibernate 无法识别定义 Drools 数据库项的目录。请在您的persistence.xml
文件中包含类似的内容:<property name="hibernate.default_schema" value="your-schema"/>
。当然,把your-schema
换成正确的。
我已经尝试过 hibernate.defualt 模式属性。我仍然得到相同的堆栈跟踪。此外,Drools 使用 <property name="hibernate.hbm2ddl.auto" value="create"/>
通过休眠创建所需的表
Hibernate 制作了两个不同的表 SessionInfo 和 WorkItemInfo。这两个是我们在persistence.xml文件中提到的Drools提供的类
拜托,您可以尝试将 Hibernate 方言更改为org.hibernate.dialect.MySQL5Dialect
。请参阅this 堆栈溢出问题。【参考方案2】:
您是否在容器中配置了 jndi 数据源?
<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
maxTotal="100" maxIdle="30" maxWaitMillis="10000"
username="javauser" password="javadude" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/javatest"/>
Tomcat jndi data source example
【讨论】:
我在 Spring Boot 中使用嵌入式 Tomcat。我应该把这个内容放在哪里 ***.com/questions/24941829/…以上是关于无法在 Spring Boot 中实现 Drools KieSession Persistence的主要内容,如果未能解决你的问题,请参考以下文章