I have a configuration entity defined as follows:
@Entity
class ConfigEntity() : PanacheEntity() {
companion object : PanacheCompanion<ConfigEntity> {
fun findByKey(key: String): ConfigEntity? {
return find("stringKey", key).firstResult()
}
}
lateinit var stringKey: String
lateinit var stringValue: String
}
I would like to be able to let users dynamically set constraints within OptaPlanner, and I have a ConstraintsProvider
defined like:
class TimeTableConstraintProvider : ConstraintProvider {
override fun defineConstraints(constraintFactory: ConstraintFactory): Array<Constraint> {
val constraints = mutableListOf<Constraint>()
if (ConfigEntity.findByKey("room")?.stringValue == "ENABLED") constraints.add(roomConflict(constraintFactory))
if (ConfigEntity.findByKey("teacher")?.stringValue == "ENABLED") constraints.add(teacherConflict(constraintFactory))
return constraints.toTypedArray()
}
//... definitions of roomConflict and teacherConflict below
}
Receiving this error when trying to access entities using Hibernate Panache ORM
(Quarkus Main Thread) Failed to start application (with profile dev): javax.enterprise.context.ContextNotActiveException: Cannot use the EntityManager/Session because neither a transaction nor a CDI request context is active. Consider adding @Transactional to your method to automatically activate a transaction, or @ActivateRequestContext if you have valid reasons not to use transactions.
I have tried the recommended solution of adding @Transactional around the defineConstriants
function, but it hasn't had any effect.
CodePudding user response:
It looks like you are trying to use EntityManager or Session outside the context of a transaction or CDI request. To use these features, you need to make sure that you have an active transaction or an active ICD request context.
One way to solve this problem is to add the @Transactional annotation to the defineConstraints method. This will automatically activate a transaction when the method is called, and make it possible to use EntityManager or Session within the method.
Alternatively, you can use the @ActivateRequestContext annotation to activate the DCI request context for the duration of the method. This will allow you to use EntityManager or Session within the method, but will not automatically start a transaction.
Both of these annotations are provided by the Quarkus framework, which it appears you are using based on the error message you provided.
CodePudding user response:
Dynamically turning constraints off/on like this is error-prone, because ConstraintProvider.defineConstraints()
can be called once at bootstrap and cached forever, or can be called in parallel all the time. Giving it a dynamic output like this will cause race conditions, sooner or later.
To dynamically turn constraints off/on, use penalizeConfigurable()
and a @ConstraintConfiguration
with @ConstraintWeight
s of zero/non-zero. Those weights are part of the dataset (the @PlanningSolution
), and therefore one dataset might activate constraint A and another dataset might not - and still they can solve in parallel. This approach is thread-safe and doesn't need a CDI request context. See OptaPlanner docs.
That being said, there's an open RFE to allow defaulting constraints weights on application.properties
with no need for a bunch of boilerplate code.