New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data access improvements #1033

Merged
merged 9 commits into from Aug 3, 2018

Conversation

Projects
None yet
2 participants
@knstvk
Copy link
Member

knstvk commented Jul 9, 2018

Overview

Currently, we are mostly promoting EntityManager and entity listeners for writing business logic on middleware. There are the following difficulties in this approach:

  • Developers need to understand entity states: new, managed, detached. Newly persisted entities can contain references to detached objects. This is ok for persistence, but unclear and leads to ugly code in entity listeners. Besides, developers constantly overuse merge() and loading with views when it is not needed.
  • Implicit flushes when running queries.
  • Bypassing row-level security, dynamic attributes, cross-datastore references.
  • No standard event for "an entity just changed and committed to the database".
  • Works only for entities stored in a relational database (e.g. when the standard RdbmsStore is used).

Suggested changes include:

  • Promote using existing DataManager where no transactional logic is required, i.e. where atomic load/commit operations in separate transactions are enough.

  • Introduce TransactionalDataManager which mimics the DataManager interface but can join to an existing transaction. It has the following properties:

    • If there is an active transaction, joins it, otherwise creates and commits a transaction same as DataManager.
    • Returns entities in detached state, so no automatic saving of changes on transaction commit is performed. Lazy loading also doesn't work, so a developer has to load entities with appropriate views.
    • No persistence context, no implicit flushes.
    • Applies row-level security, works with dynamic attributes and cross-datastore references as expected.
  • Introduce EntityChangedEvent. It is a Spring's ApplicationEvent of the middle tier which is sent when an entity instance is saved to the database. The event can be handled both inside the transaction and after its completion (using @TransactionalEventListener). The event is sent only for entities annotated with @PublishEntityChangedEvents.

    EntityChangedEvent does not contain the changed object but only its id. Also, the getOldValue(attributeName) method returns ids of references instead of objects. This is done intentionally to make the developer reload objects with required view, dynamic attributes and other parameters. This also allows us to keep the security and other logic just in one place - in the loading mechanism, and saves from potential bugs and inconsistencies.

  • Promote using standard JPA lifecycle callbacks (@PrePersist, @PreUpdate, @PostLoad, etc.) for simple changes of entity attributes before saving and after loading.

So developers will have data access tools of two categories. The recommended set of tools will include simple to use and predictable DataManager/TransactionalDataManager, standard and familiar Spring events and JPA lifecycle callbacks. For complex cases there are CUBA entity and transaction listeners.

New API usage examples

DataManagerTransactionalUsageTest

Sequence of events for create/update/delete in one transaction

BalanceWorker,
OperationWorker, Account

Known issues and limitations

  • EntityChangedEvent is not suitable for validations and changing attributes before save. It is possible but not effective performance-wise because database update has been already performed (though the transaction is not yet committed and can be rolled back or the record can be updated again). So JPA lifecycle callbacks or CUBA entity listeners should be used for this purpose.

    Maybe to introduce something like EntityChangingEvent which is sent before saving to the database and contains the changed object?

  • EntityChangedEvent.getChanges().getOldValue() is not tested for collection attributes and probably doesn't work yet - DONE

  • TransactionalDataManager should also have load(LoadContext) and secure() methods - DONE

  • more?

@knstvk knstvk changed the base branch from master to release_6_10 Jul 9, 2018

@cuba-platform cuba-platform deleted a comment from CLAassistant Jul 9, 2018

knstvk added some commits Jul 11, 2018

Data access improvements for 6.x
Transactions factory, add transactions() method to TransactionalDataManager
Data access improvements for 6.x
Add TransactionalDataManager.secure()
Data access improvements for 6.x
Add load methods accepting LoadContext to TransactionalDataManager
Data access improvements for 6.x
Old values of collection attributes in EntityChangedEvent
Data access improvements for 6.x
AttributeChanges for CREATE event; Javadocs
Data access improvements for 6.x
Dynamic attributes in EntityChangedEvent

@knstvk knstvk merged commit 04991ed into release_6_10 Aug 3, 2018

1 of 3 checks passed

continuous-integration/travis-ci/pr The Travis CI build is in progress
Details
continuous-integration/travis-ci/push The Travis CI build is in progress
Details
license/cla Contributor License Agreement is signed.
Details

@knstvk knstvk deleted the feature/data_access_6 branch Aug 3, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment