| Total Complexity | 131 |
| Total Lines | 704 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like DbEntityManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use DbEntityManager, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 67 | class DbEntityManager implements SessionInterface, EntityLoadListenerInterface |
||
| 68 | { |
||
| 69 | //protected static final EnginePersistenceLogger LOG = ProcessEngineLogger.PERSISTENCE_LOGGER; |
||
| 70 | public const TOGGLE_FOREIGN_KEY_STMT = "toggleForeignKey"; |
||
| 71 | public const BATCH_SIZE = 50; |
||
| 72 | |||
| 73 | protected $optimisticLockingListeners = []; |
||
| 74 | |||
| 75 | protected $idGenerator; |
||
| 76 | |||
| 77 | protected $dbEntityCache; |
||
| 78 | |||
| 79 | protected $dbOperationManager; |
||
| 80 | |||
| 81 | protected $persistenceSession; |
||
| 82 | protected $isIgnoreForeignKeysForNextFlush; |
||
| 83 | |||
| 84 | public function __construct(IdGeneratorInterface $idGenerator, PersistenceSessionInterface $persistenceSession = null) |
||
| 85 | { |
||
| 86 | $this->idGenerator = $idGenerator; |
||
| 87 | $this->persistenceSession = $persistenceSession; |
||
| 88 | if ($this->persistenceSession !== null) { |
||
| 89 | $this->persistenceSession->addEntityLoadListener($this); |
||
| 90 | } |
||
| 91 | $this->initializeEntityCache(); |
||
| 92 | $this->initializeOperationManager(); |
||
| 93 | } |
||
| 94 | |||
| 95 | protected function initializeOperationManager(): void |
||
| 96 | { |
||
| 97 | $this->dbOperationManager = new DbOperationManager(); |
||
| 98 | } |
||
| 99 | |||
| 100 | protected function initializeEntityCache(): void |
||
| 101 | { |
||
| 102 | $jobExecutorContext = Context::getJobExecutorContext(); |
||
| 103 | $processEngineConfiguration = Context::getProcessEngineConfiguration(); |
||
| 104 | |||
| 105 | if ( |
||
| 106 | $processEngineConfiguration !== null |
||
| 107 | && $processEngineConfiguration->isDbEntityCacheReuseEnabled() |
||
|
|
|||
| 108 | && $jobExecutorContext !== null |
||
| 109 | ) { |
||
| 110 | $this->dbEntityCache = $jobExecutorContext->getEntityCache(); |
||
| 111 | if ($this->dbEntityCache === null) { |
||
| 112 | $this->dbEntityCache = new DbEntityCache($processEngineConfiguration->getDbEntityCacheKeyMapping()); |
||
| 113 | $jobExecutorContext->setEntityCache($this->dbEntityCache); |
||
| 114 | } |
||
| 115 | } else { |
||
| 116 | if ($processEngineConfiguration !== null) { |
||
| 117 | $this->dbEntityCache = new DbEntityCache($processEngineConfiguration->getDbEntityCacheKeyMapping()); |
||
| 118 | } else { |
||
| 119 | $this->dbEntityCache = new DbEntityCache(); |
||
| 120 | } |
||
| 121 | } |
||
| 122 | } |
||
| 123 | |||
| 124 | // selects ///////////////////////////////////////////////// |
||
| 125 | |||
| 126 | public function selectList(string $statement, $parameter = null, $pageOrFirstResult = null, $maxResults = null): array |
||
| 127 | { |
||
| 128 | if ($parameter === null) { |
||
| 129 | return $this->selectList($statement, null, 0, PHP_INT_MAX); |
||
| 130 | } elseif ($parameter instanceof ListQueryParameterObject && $pageOrFirstResult === null) { |
||
| 131 | return $this->selectListWithRawParameter($statement, $parameter, $parameter->getFirstResult(), $parameter->getMaxResults()); |
||
| 132 | } elseif ($pageOrFirstResult === null) { |
||
| 133 | return $this->selectList($statement, $parameter, 0, PHP_INT_MAX); |
||
| 134 | } elseif ($pageOrFirstResult instanceof Page) { |
||
| 135 | if ($parameter instanceof ListQueryParameterObject) { |
||
| 136 | return $this->selectList($statement, $parameter); |
||
| 137 | } |
||
| 138 | return $this->selectList($statement, $parameter, $pageOrFirstResult->getFirstResult(), $pageOrFirstResult->getMaxResults()); |
||
| 139 | } elseif (is_int($pageOrFirstResult) && is_int($maxResults)) { |
||
| 140 | return $this->selectList($statement, new ListQueryParameterObject($parameter, $pageOrFirstResult, $maxResults)); |
||
| 141 | } |
||
| 142 | } |
||
| 143 | |||
| 144 | public function selectListWithRawParameter(string $statement, $parameter, int $firstResult, int $maxResults): array |
||
| 145 | { |
||
| 146 | if ($firstResult == -1 || $maxResults == -1) { |
||
| 147 | return []; |
||
| 148 | } |
||
| 149 | $loadedObjects = $this->persistenceSession->selectList($statement, $parameter); |
||
| 150 | return $this->filterLoadedObjects($loadedObjects); |
||
| 151 | } |
||
| 152 | |||
| 153 | public function selectOne(string $statement, $parameter) |
||
| 154 | { |
||
| 155 | $result = $this->persistenceSession->selectOne($statement, $parameter); |
||
| 156 | if ($result instanceof DbEntityInterface) { |
||
| 157 | $loadedObject = $result; |
||
| 158 | $result = $this->cacheFilter($loadedObject); |
||
| 159 | } |
||
| 160 | return $result; |
||
| 161 | } |
||
| 162 | |||
| 163 | public function selectBoolean(string $statement, $parameter): bool |
||
| 164 | { |
||
| 165 | $result = $this->persistenceSession->selectList($statement, $parameter); |
||
| 166 | if (!empty($result)) { |
||
| 167 | return in_array(1, $result); |
||
| 168 | } |
||
| 169 | return false; |
||
| 170 | } |
||
| 171 | |||
| 172 | public function selectById(string $entityClass, string $id): ?DbEntityInterface |
||
| 173 | { |
||
| 174 | $persistentObject = $this->dbEntityCache->get($entityClass, $id); |
||
| 175 | if (!empty($persistentObject)) { |
||
| 176 | return $persistentObject; |
||
| 177 | } |
||
| 178 | |||
| 179 | $persistentObject = $this->persistenceSession->selectById($entityClass, $id); |
||
| 180 | |||
| 181 | if (empty($persistentObject)) { |
||
| 182 | return null; |
||
| 183 | } |
||
| 184 | // don't have to put object into the cache now. See onEntityLoaded() callback |
||
| 185 | return $persistentObject; |
||
| 186 | } |
||
| 187 | |||
| 188 | public function getCachedEntity(string $type, string $id): ?DbEntityInterface |
||
| 189 | { |
||
| 190 | return $this->dbEntityCache->get($type, $id); |
||
| 191 | } |
||
| 192 | |||
| 193 | public function getCachedEntitiesByType(string $type): array |
||
| 194 | { |
||
| 195 | return $this->dbEntityCache->getEntitiesByType($type); |
||
| 196 | } |
||
| 197 | |||
| 198 | protected function filterLoadedObjects(array $loadedObjects): array |
||
| 199 | { |
||
| 200 | if (empty($loadedObjects) || $loadedObjects[0] === null) { |
||
| 201 | return $loadedObjects; |
||
| 202 | } |
||
| 203 | if (!(is_a($loadedObjects[0], DbEntityInterface::class))) { |
||
| 204 | return $loadedObjects; |
||
| 205 | } |
||
| 206 | $filteredObjects = []; |
||
| 207 | foreach ($loadedObjects as $loadedObject) { |
||
| 208 | $cachedPersistentObject = $this->cacheFilter($loadedObject); |
||
| 209 | $filteredObjects[] = $cachedPersistentObject; |
||
| 210 | } |
||
| 211 | return $filteredObjects; |
||
| 212 | } |
||
| 213 | |||
| 214 | /** returns the object in the cache. if this object was loaded before, |
||
| 215 | * then the original object is returned. */ |
||
| 216 | protected function cacheFilter(DbEntityInterface $persistentObject): DbEntityInterface |
||
| 217 | { |
||
| 218 | $cachedPersistentObject = $this->dbEntityCache->get(get_class($persistentObject), $persistentObject->getId()); |
||
| 219 | if ($cachedPersistentObject !== null) { |
||
| 220 | return $cachedPersistentObject; |
||
| 221 | } else { |
||
| 222 | return $persistentObject; |
||
| 223 | } |
||
| 224 | } |
||
| 225 | |||
| 226 | public function onEntityLoaded(DbEntityInterface $entity): void |
||
| 227 | { |
||
| 228 | // we get a callback when the persistence session loads an object from the database |
||
| 229 | $cachedPersistentObject = $this->dbEntityCache->get(entity->getClass(), entity->getId()); |
||
| 230 | if (empty($cachedPersistentObject)) { |
||
| 231 | // only put into the cache if not already present |
||
| 232 | $this->dbEntityCache->putPersistent($entity); |
||
| 233 | |||
| 234 | // invoke postLoad() lifecycle method |
||
| 235 | if ($entity instanceof DbEntityLifecycleAwareInterface) { |
||
| 236 | $lifecycleAware = $entity; |
||
| 237 | $lifecycleAware->postLoad(); |
||
| 238 | } |
||
| 239 | } |
||
| 240 | } |
||
| 241 | |||
| 242 | public function lock(string $statement, $parameter = null): void |
||
| 243 | { |
||
| 244 | $this->persistenceSession->lock($statement, $parameter); |
||
| 245 | } |
||
| 246 | |||
| 247 | public function isDirty(DbEntityInterface $dbEntity): bool |
||
| 248 | { |
||
| 249 | $cachedEntity = $this->dbEntityCache->getCachedEntity($dbEntity); |
||
| 250 | if ($cachedEntity === null) { |
||
| 251 | return false; |
||
| 252 | } else { |
||
| 253 | return $cachedEntity->isDirty() || $cachedEntity->getEntityState() == DbEntityState::MERGED; |
||
| 254 | } |
||
| 255 | } |
||
| 256 | |||
| 257 | public function flush(): void |
||
| 258 | { |
||
| 259 | // flush the entity cache which inserts operations to the db operation manager |
||
| 260 | $this->flushEntityCache(); |
||
| 261 | |||
| 262 | // flush the db operation manager |
||
| 263 | $this->flushDbOperationManager(); |
||
| 264 | } |
||
| 265 | |||
| 266 | public function setIgnoreForeignKeysForNextFlush(bool $ignoreForeignKeysForNextFlush): void |
||
| 267 | { |
||
| 268 | $this->isIgnoreForeignKeysForNextFlush = $ignoreForeignKeysForNextFlush; |
||
| 269 | } |
||
| 270 | |||
| 271 | protected function flushDbOperationManager(): void |
||
| 272 | { |
||
| 273 | |||
| 274 | // obtain totally ordered operation list from operation manager |
||
| 275 | $operationsToFlush = $this->dbOperationManager->calculateFlush(); |
||
| 276 | if (empty($operationsToFlush)) { |
||
| 277 | return; |
||
| 278 | } |
||
| 279 | |||
| 280 | //LOG.databaseFlushSummary(operationsToFlush); |
||
| 281 | |||
| 282 | // If we want to delete all table data as bulk operation, on tables which have self references, |
||
| 283 | // We need to turn the foreign key check off on MySQL and MariaDB. |
||
| 284 | // On other databases we have to do nothing, the mapped statement will be empty. |
||
| 285 | if ($this->isIgnoreForeignKeysForNextFlush) { |
||
| 286 | $this->persistenceSession->executeNonEmptyUpdateStmt(self::TOGGLE_FOREIGN_KEY_STMT, false); |
||
| 287 | $this->persistenceSession->flushOperations(); |
||
| 288 | } |
||
| 289 | |||
| 290 | try { |
||
| 291 | $batches = CollectionUtil::partition($operationsToFlush, self::BATCH_SIZE); |
||
| 292 | foreach ($batches as $batch) { |
||
| 293 | $this->flushDbOperations($batch, $operationsToFlush); |
||
| 294 | } |
||
| 295 | } finally { |
||
| 296 | if ($this->isIgnoreForeignKeysForNextFlush) { |
||
| 297 | $this->persistenceSession->executeNonEmptyUpdateStmt(self::TOGGLE_FOREIGN_KEY_STMT, true); |
||
| 298 | $this->persistenceSession->flushOperations(); |
||
| 299 | $this->isIgnoreForeignKeysForNextFlush = false; |
||
| 300 | } |
||
| 301 | } |
||
| 302 | } |
||
| 303 | |||
| 304 | protected function flushDbOperations( |
||
| 305 | array &$operationsToFlush, |
||
| 306 | array $allOperations |
||
| 307 | ): void { |
||
| 308 | // execute the flush |
||
| 309 | while (!empty($operationsToFlush)) { |
||
| 310 | $flushResult = null; |
||
| 311 | try { |
||
| 312 | $flushResult = $this->persistenceSession->executeDbOperations($operationsToFlush); |
||
| 313 | } catch (\Exception $e) { |
||
| 314 | // Top level persistence exception |
||
| 315 | //throw LOG.flushDbOperationUnexpectedException(allOperations, e); |
||
| 316 | throw new \Exception("flushDbOperationUnexpectedException"); |
||
| 317 | } |
||
| 318 | |||
| 319 | $failedOperations = $flushResult->getFailedOperations(); |
||
| 320 | |||
| 321 | foreach ($failedOperations as $failedOperation) { |
||
| 322 | $failureState = $failedOperation->getState(); |
||
| 323 | |||
| 324 | if ($failureState == DbOperationState::FAILED_CONCURRENT_MODIFICATION) { |
||
| 325 | // this method throws an exception in case the flush cannot be continued; |
||
| 326 | // accordingly, this method will be left as well in this case |
||
| 327 | $this->handleConcurrentModification($failedOperation); |
||
| 328 | } elseif ($failureState == DbOperationState::FAILED_ERROR) { |
||
| 329 | // Top level persistence exception |
||
| 330 | $failure = $failedOperation->getFailure(); |
||
| 331 | //throw LOG.flushDbOperationException(allOperations, failedOperation, failure); |
||
| 332 | throw new \Exception("flushDbOperationException"); |
||
| 333 | } else { |
||
| 334 | // This branch should never be reached and the exception thus indicates a bug |
||
| 335 | throw new ProcessEngineException( |
||
| 336 | "Entity session returned a failed operation not " . |
||
| 337 | "in an error state. This indicates a bug" |
||
| 338 | ); |
||
| 339 | } |
||
| 340 | /*elseif ($failureState == DbOperationState::FAILED_CONCURRENT_MODIFICATION_CRDB) { |
||
| 341 | $this->handleConcurrentModificationCrdb($failedOperation); |
||
| 342 | }*/ |
||
| 343 | } |
||
| 344 | |||
| 345 | $remainingOperations = $flushResult->getRemainingOperations(); |
||
| 346 | |||
| 347 | // avoid infinite loops |
||
| 348 | EnsureUtil::ensureLessThan( |
||
| 349 | "Database flush did not process any operations. This indicates a bug.", |
||
| 350 | "remainingOperations", |
||
| 351 | count($remainingOperations), |
||
| 352 | count($operationsToFlush) |
||
| 353 | ); |
||
| 354 | |||
| 355 | $operationsToFlush = $remainingOperations; |
||
| 356 | } |
||
| 357 | } |
||
| 358 | |||
| 359 | public function flushEntity(DbEntityInterface $entity): void |
||
| 360 | { |
||
| 361 | $cachedEntity = $this->dbEntityCache->getCachedEntity($entity); |
||
| 362 | if ($cachedEntity !== null) { |
||
| 363 | $this->flushCachedEntity($cachedEntity); |
||
| 364 | } |
||
| 365 | |||
| 366 | $this->flushDbOperationManager(); |
||
| 367 | } |
||
| 368 | |||
| 369 | /** |
||
| 370 | * Decides if an operation that failed for concurrent modifications can be tolerated, |
||
| 371 | * or if OptimisticLockingException should be raised |
||
| 372 | * |
||
| 373 | * @param dbOperation |
||
| 374 | * @throws OptimisticLockingException if there is no handler for the failure |
||
| 375 | */ |
||
| 376 | protected function handleConcurrentModification(DbOperation $dbOperation): void |
||
| 377 | { |
||
| 378 | $handlingResult = $this->invokeOptimisticLockingListeners($dbOperation); |
||
| 379 | |||
| 380 | if ( |
||
| 381 | OptimisticLockingResult::THROW == $handlingResult |
||
| 382 | && canIgnoreHistoryModificationFailure($dbOperation) |
||
| 383 | ) { |
||
| 384 | $handlingResult = OptimisticLockingResult::IGNORE; |
||
| 385 | } |
||
| 386 | |||
| 387 | switch ($handlingResult) { |
||
| 388 | case OptimisticLockingResult::IGNORE: |
||
| 389 | break; |
||
| 390 | case OptimisticLockingResult::THROW: |
||
| 391 | default: |
||
| 392 | //throw LOG.concurrentUpdateDbEntityException(dbOperation); |
||
| 393 | throw new \Exception("concurrentUpdateDbEntityException"); |
||
| 394 | } |
||
| 395 | } |
||
| 396 | |||
| 397 | /*protected function handleConcurrentModificationCrdb(DbOperation $dbOperation): void |
||
| 398 | { |
||
| 399 | $handlingResult = $this->invokeOptimisticLockingListeners($dbOperation); |
||
| 400 | |||
| 401 | if (OptimisticLockingResult::IGNORE == $handlingResult) { |
||
| 402 | //LOG.crdbFailureIgnored(dbOperation); |
||
| 403 | } |
||
| 404 | |||
| 405 | // CRDB concurrent modification exceptions always lead to the transaction |
||
| 406 | // being aborted, so we must always throw an exception. |
||
| 407 | //throw LOG.crdbTransactionRetryException(dbOperation); |
||
| 408 | //throw new \Exception("crdbTransactionRetryException"); |
||
| 409 | }*/ |
||
| 410 | |||
| 411 | private function invokeOptimisticLockingListeners(DbOperation $dbOperation): OptimisticLockingResult |
||
| 412 | { |
||
| 413 | $handlingResult = OptimisticLockingResult::THROW; |
||
| 414 | |||
| 415 | if (!empty($this->optimisticLockingListeners)) { |
||
| 416 | foreach ($this->optimisticLockingListeners as $optimisticLockingListener) { |
||
| 417 | if ( |
||
| 418 | $optimisticLockingListener->getEntityType() === null |
||
| 419 | || is_a($optimisticLockingListener->getEntityType(), $dbOperation->getEntityType(), true) |
||
| 420 | ) { |
||
| 421 | $handlingResult = $optimisticLockingListener->failedOperation($dbOperation); |
||
| 422 | } |
||
| 423 | } |
||
| 424 | } |
||
| 425 | return $handlingResult; |
||
| 426 | } |
||
| 427 | |||
| 428 | /** |
||
| 429 | * Determines if a failed database operation (OptimisticLockingException) |
||
| 430 | * on a Historic entity can be ignored. |
||
| 431 | * |
||
| 432 | * @param dbOperation that failed |
||
| 433 | * @return bool true if the failure can be ignored |
||
| 434 | */ |
||
| 435 | protected function canIgnoreHistoryModificationFailure(DbOperation $dbOperation): bool |
||
| 436 | { |
||
| 437 | $dbEntity = $dbOperation->getEntity(); |
||
| 438 | return Context::getProcessEngineConfiguration()->isSkipHistoryOptimisticLockingExceptions() |
||
| 439 | && ($dbEntity instanceof HistoricEntityInterface || $this->isHistoricByteArray($dbEntity)); |
||
| 440 | } |
||
| 441 | |||
| 442 | protected function isHistoricByteArray(DbEntityInterface $dbEntity): bool |
||
| 443 | { |
||
| 444 | if ($dbEntity instanceof ByteArrayEntity) { |
||
| 445 | $byteArrayEntity = $dbEntity; |
||
| 446 | return $byteArrayEntity->getType() == ResourceTypes::history()->getValue(); |
||
| 447 | } else { |
||
| 448 | return false; |
||
| 449 | } |
||
| 450 | } |
||
| 451 | |||
| 452 | /** |
||
| 453 | * Flushes the entity cache: |
||
| 454 | * Depending on the entity state, the required DbOperation is performed and the cache is updated. |
||
| 455 | */ |
||
| 456 | protected function flushEntityCache(): void |
||
| 457 | { |
||
| 458 | $cachedEntities = $this->dbEntityCache->getCachedEntities(); |
||
| 459 | foreach ($cachedEntities as $cachedDbEntity) { |
||
| 460 | $this->flushCachedEntity($cachedDbEntity); |
||
| 461 | } |
||
| 462 | |||
| 463 | // log cache state after flush |
||
| 464 | //LOG.flushedCacheState(dbEntityCache->getCachedEntities()); |
||
| 465 | } |
||
| 466 | |||
| 467 | protected function flushCachedEntity(CachedDbEntityInterface $cachedDbEntity): void |
||
| 468 | { |
||
| 469 | if ($cachedDbEntity->getEntityState() == DbEntityState::TRANSIENT) { |
||
| 470 | // latest state of references in cache is relevant when determining insertion order |
||
| 471 | $cachedDbEntity->determineEntityReferences(); |
||
| 472 | // perform DbOperationType::INSERT |
||
| 473 | $this->performEntityOperation($cachedDbEntity, DbOperationType::INSERT); |
||
| 474 | // mark DbEntityState::PERSISTENT |
||
| 475 | $cachedDbEntity->setEntityState(DbEntityState::PERSISTENT); |
||
| 476 | } elseif ($cachedDbEntity->getEntityState() == DbEntityState::PERSISTENT && $cachedDbEntity->isDirty()) { |
||
| 477 | // object is dirty -> perform UPDATE |
||
| 478 | $this->performEntityOperation($cachedDbEntity, DbOperationType::UPDATE); |
||
| 479 | } elseif ($cachedDbEntity->getEntityState() == DbEntityState::MERGED) { |
||
| 480 | // perform UPDATE |
||
| 481 | $this->performEntityOperation($cachedDbEntity, DbOperationType::UPDATE); |
||
| 482 | // mark DbEntityState::PERSISTENT |
||
| 483 | $cachedDbEntity->setEntityState(DbEntityState::PERSISTENT); |
||
| 484 | } elseif ($cachedDbEntity->getEntityState() == DbEntityState::DELETED_TRANSIENT) { |
||
| 485 | // remove from cache |
||
| 486 | $this->dbEntityCache->remove($cachedDbEntity); |
||
| 487 | } elseif ( |
||
| 488 | $cachedDbEntity->getEntityState() == DbEntityState::DELETED_PERSISTENT |
||
| 489 | || $cachedDbEntity->getEntityState() == DbEntityState::DELETED_MERGED |
||
| 490 | ) { |
||
| 491 | // perform DbOperationType::DELETE |
||
| 492 | $this->performEntityOperation($cachedDbEntity, DbOperationType::DELETE); |
||
| 493 | // remove from cache |
||
| 494 | $this->dbEntityCache->remove($cachedDbEntity); |
||
| 495 | } |
||
| 496 | |||
| 497 | // if object is DbEntityState::PERSISTENT after flush |
||
| 498 | if ($cachedDbEntity->getEntityState() == DbEntityState::PERSISTENT) { |
||
| 499 | // make a new copy |
||
| 500 | $cachedDbEntity->makeCopy(); |
||
| 501 | // update cached references |
||
| 502 | $cachedDbEntity->determineEntityReferences(); |
||
| 503 | } |
||
| 504 | } |
||
| 505 | |||
| 506 | public function insert(DbEntityInterface $dbEntity): void |
||
| 507 | { |
||
| 508 | // generate Id if not present |
||
| 509 | $this->ensureHasId($dbEntity); |
||
| 510 | |||
| 511 | $this->validateId($dbEntity); |
||
| 512 | |||
| 513 | // put into cache |
||
| 514 | $this->dbEntityCache->putTransient($dbEntity); |
||
| 515 | } |
||
| 516 | |||
| 517 | public function merge(DbEntityInterface $dbEntity): void |
||
| 518 | { |
||
| 519 | |||
| 520 | if ($dbEntity->getId() === null) { |
||
| 521 | //throw LOG.mergeDbEntityException(dbEntity); |
||
| 522 | throw new \Exception("mergeDbEntityException"); |
||
| 523 | } |
||
| 524 | |||
| 525 | // NOTE: a proper implementation of merge() would fetch the entity from the database |
||
| 526 | // and merge the state changes. For now, we simply always perform an update. |
||
| 527 | // Supposedly, the "proper" implementation would reduce the number of situations where |
||
| 528 | // optimistic locking results in a conflict. |
||
| 529 | |||
| 530 | $this->dbEntityCache->putMerged($dbEntity); |
||
| 531 | } |
||
| 532 | |||
| 533 | public function forceUpdate(DbEntityInterface $entity): void |
||
| 534 | { |
||
| 535 | $cachedEntity = $this->dbEntityCache->getCachedEntity($entity); |
||
| 536 | if ($cachedEntity !== null && $cachedEntity->getEntityState() == DbEntityState::PERSISTENT) { |
||
| 537 | $cachedEntity->forceSetDirty(); |
||
| 538 | } |
||
| 539 | } |
||
| 540 | |||
| 541 | public function undoDelete(DbEntityInterface $entity): void |
||
| 542 | { |
||
| 543 | $this->dbEntityCache->undoDelete($entity); |
||
| 544 | } |
||
| 545 | |||
| 546 | public function update(string $entityType, string $statement, $parameter): void |
||
| 547 | { |
||
| 548 | $this->performBulkOperation($entityType, $statement, $parameter, DbOperationType::UPDATE_BULK); |
||
| 549 | } |
||
| 550 | |||
| 551 | /** |
||
| 552 | * Several update operations added by this method will be executed preserving the order of method calls, no matter what entity type they refer to. |
||
| 553 | * They will though be executed after all "not-bulk" operations (e.g. DbEntityManager#insert(DbEntity) or DbEntityManager#merge(DbEntity)) |
||
| 554 | * and after those updates added by {@link DbEntityManager#update(Class, String, Object)}. |
||
| 555 | * @param entityType |
||
| 556 | * @param statement |
||
| 557 | * @param parameter |
||
| 558 | */ |
||
| 559 | public function updatePreserveOrder(string $entityType, string $statement, $parameter): void |
||
| 560 | { |
||
| 561 | $this->performBulkOperationPreserveOrder($entityType, $statement, $parameter, DbOperationType::UPDATE_BULK); |
||
| 562 | } |
||
| 563 | |||
| 564 | public function delete($entityTypeOrEntity, string $statement = null, $parameter = null): void |
||
| 565 | { |
||
| 566 | if (is_string($entityTypeOrEntity)) { |
||
| 567 | $this->performBulkOperation($entityTypeOrEntity, $statement, $parameter, DbOperationType::DELETE_BULK); |
||
| 568 | } elseif ($entityTypeOrEntity instanceof DbEntityInterface) { |
||
| 569 | $this->dbEntityCache->setDeleted($entityTypeOrEntity); |
||
| 570 | } |
||
| 571 | } |
||
| 572 | |||
| 573 | /** |
||
| 574 | * Several delete operations added by this method will be executed preserving the order of method calls, no matter what entity type they refer to. |
||
| 575 | * They will though be executed after all "not-bulk" operations (e.g. DbEntityManager#insert(DbEntity) or DbEntityManager#merge(DbEntity)) |
||
| 576 | * and after those deletes added by {@link DbEntityManager#delete(Class, String, Object)}. |
||
| 577 | * @param entityType |
||
| 578 | * @param statement |
||
| 579 | * @param parameter |
||
| 580 | * @return delete operation |
||
| 581 | */ |
||
| 582 | public function deletePreserveOrder(string $entityType, string $statement, $parameter): DbBulkOperation |
||
| 583 | { |
||
| 584 | return $this->performBulkOperationPreserveOrder($entityType, $statement, $parameter, DbOperationType::DELETE_BULK); |
||
| 585 | } |
||
| 586 | |||
| 587 | protected function performBulkOperation(string $entityType, string $statement, $parameter, string $operationType): DbBulkOperation |
||
| 588 | { |
||
| 589 | // create operation |
||
| 590 | $bulkOperation = $this->createDbBulkOperation($entityType, $statement, $parameter, $operationType); |
||
| 591 | |||
| 592 | // schedule operation |
||
| 593 | $this->dbOperationManager->addOperation($bulkOperation); |
||
| 594 | return $bulkOperation; |
||
| 595 | } |
||
| 596 | |||
| 597 | protected function performBulkOperationPreserveOrder(string $entityType, string $statement, $parameter, string $operationType): DbBulkOperation |
||
| 598 | { |
||
| 599 | $bulkOperation = $this->createDbBulkOperation($entityType, $statement, $parameter, $operationType); |
||
| 600 | |||
| 601 | // schedule operation |
||
| 602 | $this->dbOperationManager->addOperationPreserveOrder($bulkOperation); |
||
| 603 | return $bulkOperation; |
||
| 604 | } |
||
| 605 | |||
| 606 | private function createDbBulkOperation(string $entityType, string $statement, $parameter, string $operationType): DbBulkOperation |
||
| 607 | { |
||
| 608 | // create operation |
||
| 609 | $bulkOperation = new DbBulkOperation(); |
||
| 610 | // configure operation |
||
| 611 | $bulkOperation->setOperationType($operationType); |
||
| 612 | $bulkOperation->setEntityType($entityType); |
||
| 613 | $bulkOperation->setStatement($statement); |
||
| 614 | $bulkOperation->setParameter($parameter); |
||
| 615 | return $bulkOperation; |
||
| 616 | } |
||
| 617 | |||
| 618 | protected function performEntityOperation(CachedDbEntityInterface $cachedDbEntity, string $type): void |
||
| 619 | { |
||
| 620 | $dbOperation = new DbEntityOperation(); |
||
| 621 | $dbOperation->setEntity($cachedDbEntity->getEntity()); |
||
| 622 | $dbOperation->setFlushRelevantEntityReferences($cachedDbEntity->getFlushRelevantEntityReferences()); |
||
| 623 | $dbOperation->setOperationType($type); |
||
| 624 | $this->dbOperationManager->addOperation($dbOperation); |
||
| 625 | } |
||
| 626 | |||
| 627 | public function close(): void |
||
| 628 | { |
||
| 629 | } |
||
| 630 | |||
| 631 | public function isDeleted(DbEntityInterface $object): bool |
||
| 632 | { |
||
| 633 | return $this->dbEntityCache->isDeleted($object); |
||
| 634 | } |
||
| 635 | |||
| 636 | protected function ensureHasId(DbEntityInterface $dbEntity): void |
||
| 637 | { |
||
| 638 | if ($dbEntity->getId() === null) { |
||
| 639 | $nextId = $this->idGenerator->getNextId(); |
||
| 640 | $dbEntity->setId($nextId); |
||
| 641 | } |
||
| 642 | } |
||
| 643 | |||
| 644 | protected function validateId(DbEntityInterface $dbEntity): void |
||
| 645 | { |
||
| 646 | EnsureUtil::ensureValidIndividualResourceId("Entity " . $dbEntity . " has an invalid id", $dbEntity->getId()); |
||
| 647 | } |
||
| 648 | |||
| 649 | public function pruneDeletedEntities(array $listToPrune): array |
||
| 650 | { |
||
| 651 | $prunedList = []; |
||
| 652 | foreach ($listToPrune as $potentiallyDeleted) { |
||
| 653 | if (!$this->isDeleted($potentiallyDeleted)) { |
||
| 654 | $prunedList[] = $potentiallyDeleted; |
||
| 655 | } |
||
| 656 | } |
||
| 657 | return $prunedList; |
||
| 658 | } |
||
| 659 | |||
| 660 | public function contains(DbEntityInterface $dbEntity): bool |
||
| 661 | { |
||
| 662 | return $this->dbEntityCache->contains($dbEntity); |
||
| 663 | } |
||
| 664 | |||
| 665 | // getters / setters ///////////////////////////////// |
||
| 666 | |||
| 667 | public function getDbOperationManager(): DbOperationManager |
||
| 668 | { |
||
| 669 | return $this->dbOperationManager; |
||
| 670 | } |
||
| 671 | |||
| 672 | public function setDbOperationManager(DbOperationManager $operationManager): void |
||
| 673 | { |
||
| 674 | $this->dbOperationManager = $operationManager; |
||
| 675 | } |
||
| 676 | |||
| 677 | public function getDbEntityCache(): DbEntityCache |
||
| 678 | { |
||
| 679 | return $this->dbEntityCache; |
||
| 680 | } |
||
| 681 | |||
| 682 | public function setDbEntityCache(DbEntityCache $dbEntityCache): void |
||
| 683 | { |
||
| 684 | $this->dbEntityCache = $dbEntityCache; |
||
| 685 | } |
||
| 686 | |||
| 687 | // query factory methods //////////////////////////////////////////////////// |
||
| 688 | |||
| 689 | public function createDeploymentQuery(): DeploymentQueryImpl |
||
| 690 | { |
||
| 691 | return new DeploymentQueryImpl(); |
||
| 692 | } |
||
| 693 | |||
| 694 | public function createProcessDefinitionQuery(): ProcessDefinitionQueryImpl |
||
| 695 | { |
||
| 696 | return new ProcessDefinitionQueryImpl(); |
||
| 697 | } |
||
| 698 | |||
| 699 | /*public CaseDefinitionQueryImpl createCaseDefinitionQuery() { |
||
| 700 | return new CaseDefinitionQueryImpl(); |
||
| 701 | }*/ |
||
| 702 | |||
| 703 | public function createProcessInstanceQuery(): ProcessInstanceQueryImpl |
||
| 704 | { |
||
| 705 | return new ProcessInstanceQueryImpl(); |
||
| 706 | } |
||
| 707 | |||
| 708 | public function createExecutionQuery(): ExecutionQueryImpl |
||
| 709 | { |
||
| 710 | return new ExecutionQueryImpl(); |
||
| 711 | } |
||
| 712 | |||
| 713 | public function createTaskQuery(): TaskQueryImpl |
||
| 714 | { |
||
| 715 | return new TaskQueryImpl(); |
||
| 716 | } |
||
| 717 | |||
| 718 | public function createJobQuery(): JobQueryImpl |
||
| 719 | { |
||
| 720 | return new JobQueryImpl(); |
||
| 721 | } |
||
| 722 | |||
| 723 | public function createHistoricProcessInstanceQuery(): HistoricProcessInstanceQueryImpl |
||
| 724 | { |
||
| 725 | return new HistoricProcessInstanceQueryImpl(); |
||
| 726 | } |
||
| 727 | |||
| 728 | public function createHistoricActivityInstanceQuery(): HistoricActivityInstanceQueryImpl |
||
| 729 | { |
||
| 730 | return new HistoricActivityInstanceQueryImpl(); |
||
| 731 | } |
||
| 732 | |||
| 733 | public function createHistoricTaskInstanceQuery(): HistoricTaskInstanceQueryImpl |
||
| 734 | { |
||
| 735 | return new HistoricTaskInstanceQueryImpl(); |
||
| 736 | } |
||
| 737 | |||
| 738 | public function createHistoricDetailQuery(): HistoricDetailQueryImpl |
||
| 739 | { |
||
| 740 | return new HistoricDetailQueryImpl(); |
||
| 741 | } |
||
| 742 | |||
| 743 | public function createHistoricVariableInstanceQuery(): HistoricVariableInstanceQueryImpl |
||
| 744 | { |
||
| 745 | return new HistoricVariableInstanceQueryImpl(); |
||
| 746 | } |
||
| 747 | |||
| 748 | public function createHistoricJobLogQuery(): HistoricJobLogQueryImpl |
||
| 749 | { |
||
| 750 | return new HistoricJobLogQueryImpl(); |
||
| 751 | } |
||
| 752 | |||
| 753 | public function createUserQuery(): UserQueryImpl |
||
| 754 | { |
||
| 755 | return new DbUserQueryImpl(); |
||
| 756 | } |
||
| 757 | |||
| 758 | public function createGroupQuery(): GroupQueryImpl |
||
| 759 | { |
||
| 760 | return new DbGroupQueryImpl(); |
||
| 761 | } |
||
| 762 | |||
| 763 | public function registerOptimisticLockingListener(OptimisticLockingListenerInterface $optimisticLockingListener = null): void |
||
| 764 | { |
||
| 765 | $this->optimisticLockingListeners[] = $optimisticLockingListener; |
||
| 766 | } |
||
| 767 | |||
| 768 | public function getTableNamesPresentInDatabase(): array |
||
| 771 | } |
||
| 772 | } |
||
| 773 |
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.
This is most likely a typographical error or the method has been renamed.