I have Document entity having read-only (from client perspective) property modificationAuthor (I want it to be modified only on server side which is aware of currently logged user). Document entity has has no setters for modificationAuthor property neither in proxy nor in domain object.
The problem is that it's not a simple property but other entity (User) so I need to add modificationAuthor to the paths when I invoke requestFactory.find(id).with(paths).fire() to get Document instance. (so that modificationAuthor is not null when I want to read it from Document).
But after adding it to paths and trying to persist Document object I'm getting exception: Could not locate setter for property modificationAuthor.... It seems that RF requires setters even for properties that are designed to be read-only.
Is there currently any way to avoid adding setters to such properties and be able to read it and persist parent object?
This is a side-effect of issue 5952, which will (should) be fixed in GWT 2.5, to be released in a month or so: RF erroneously thinks the modificationAuthor property has been modified (because it fails at comparing the User entity with itself), so it sends an operation to the server for that property, and the server then tries to set the property value to the given entity (which happens to be the one that's already there).
In the mean time, I'm afraid you'll have to add a dummy setter (it does not need to actually assign the property value) to your Document domain object.
Related
In Orion, is it possible to change the type of an entity?
Related question, it is possible to change the type of an attribute?
Edit: Changing the type of an entity doesn't seem to be possible. So creating a new entity seems to be a solution. But how to preserve dateCreated and dateModified?
In Orion, is it possible to change the type of an entity
Not directly. Entity id and entity type are immutable. However, you can create a copy of the entity with the new type, then delete the old entity. That will have effectively the same effect.
Related question, it is possible to change the type of an attribute?
Yes, it is. When you update an attribute, not only the value but also the type (and metadata) can be changed.
EDIT: dateModified and dateCreation attributes and metadata are designed to be managed by Context Broker automatically. Clients cannot modify them, they are "read-only". The idea is that a given context consumer client (which, in principle, is independent of the context producer client creating/updating the entity) has a trustable timestamp that nobody could alter.
So, you have basically two alternatives:
Use your custom timestamp attributes and metadata. They can be "moved" to the new entity when you create it. However, CB will not maintain them automatically so your application would have to do it.
Use an out-of-API process, moving the entity at DB level. However, this can be complicated, as you need direct access to DB.
EDIT2: for the second case (DB based process) take into account the Orion DB model. In particular:
_id.type is for the entity type
creDate is for the entity creation date
modDate is for the entity modification date
attrs.A.creDate is for the attribute A creation date
attrs.A.modDate is for the attribute A modification date
In our project we are using kotlin with JPA. All of our entities are immutable so, it is not possible to set fields of our entities directly. You have to create a new instance by using the copy method. If you want these changes to be reflected to database, you must persist this newly created entity with an explicit function call.
In the beginning, this approach looks perfect to us. However, nowadays we are having some problems like some of our instances are changing unexpectedly in the memory.
val instance1 = repository.findById(entityId)
repository.save(instance1.copy(deletedAt = Instant.now()))
..
..
assertNull(instance1.deletedAt())
In the code snipped above, instance1 is retrieved from database and its deletedAt field is set with copy method and the new instance which is created with this copy method is passed to save method of the repository. We don't set any field of instance1, we create a new instance to do these changes. However, the result on assert line is unexpectedly not-null.
It seems, There is a confliction on JPA persistence context (first level cache) and kotlin's immutable and copy method logic.
Is anyone facing this problem or any suggestion or best practices when using JPA and immutable Kotlin entities?
I suspect the problem is that you're ignoring the return value from save(). Its docs say:
Saves a given entity. Use the returned instance for further operations as the save operation might have changed the entity instance completely.
But you're not doing that; you're instead continuing to use the original instance which (as that says) may have changed.
Instead, store the return value from save(), and use that thereafter. (Either by making instance1 a var, or creating a new val and not referring to instance1 afterward.)
(This isn't a Kotlin-specific problem, and is exactly the same in Java. JPA , Spring, &c work their magic by futzing with the bytecode, so can do things your code can't — such as changing immutable values. Most of the time you can ignore it, but this case makes it obvious.)
Immutable types are not compatible on how JPA works.
JPA works around the concept of UnitOfWork, which mean objects retrieved from the database lives in a PersistedContext (1st level cache) and they get discarded once the EntityManager is closed (on a web application at the end of the HTTP request).
When using the copy method in an entity you just retrieved from the database, the copied object is considered detached from the current session meaning that changes on it cannot be tracked by JPA and the underlying implememtation (Hibernate / EclipseLink) have hard time figuring out which SQL statement needs to be fired (Insert/Update/Delete ????)
Things got way more complex when you have complex object graph with OneToMany associations and cascading options.
So my recommendation is unfortunately is to avoid Immutable types when using JPA.
Scenario:
I use breeze for querying in our SPA and raw http methods for POST PUT DELETE methods.
A POST method on a resource returns the created resource including it's created childobjects.
I want to attach this created resource to the beeze entity manager.
I've tried adding the entity using
manager.createEntity('Driver', data, breeze.EntityState.Unchanged);
where the data object contains the driver resource and an array of related child entities.
But get an exception: "Collection navigation properties may NOT be set."
Do I have to create the local entity by hand and attach the child entities or does breeze support this scenario?
The reason for this exception is that the 'createEntity' method creates the entity and then assigns your 'data' to it, but for any collection navigation properties the collection already exists and Breeze won't allow you to replace it. This was a deliberate choice for existing entities where other parts of the application might hold a reference to the collection, but is overkill in this case where you are the 'first' to access the collection. For now, the workaround is to update any navigation collections instead of replacing them.
So the simplest way to do this is to call the createEntity method without any collection data properties, and then push data into your collections
var driver = manager.createEntity('Driver', dataWithoutCollections, EntityState.Unchanged)
// then update collection properties by pushing into them.
trafficFines.forEach(function(trafficFine) {
driver.trafficFines.push(trafficFine); // or use push.apply
};
Note that adding to an entity's collection properties will not cause an EntityState change so this should be sufficient.
Based on this issue, I will add a feature request to suppress this exception in the case where you are calling the 'createEntity' method, since there is no danger that some other part of the application has already had access to the new entity.
I have been working with the Entity Framework + Self-Tracking entities, and came out with a problem:
Is there any way to determine when an entity has been changed??
For example: If you have an entity User with two fields: Name and Password, you can know if an User instance has been changed making:
<user>.ChangeTracker.State != ObjectState.Unchanged;
My problem is when the User has a Person, and the person has a field Email. I want that if the email field is changed, then the corresponding User is changed too.
I have been trying with methods such as: <user>.StartTrackingAll(); but this does not work with navigation properties (or maybe i am doing something wrong). Some help about this can be found here.
Remember that the Self tracking entities are autogenerated via T4 templates, so the clases can't be modified.
First when wanting to know if any entity in a so-called object graph has changed you can recurse through all entities contained in trackable collections or one-to-one navigation properties of a root entity (user in your case). This way you can know if a person inside the root entity has changed. This is actually how I check complex object graphs for any changes in any of the contained entities. But also for checking out if any of these entities have critical validation errors (so the user can't persist them yet).
Remember that the Self tracking entities are autogenerated via T4 templates, so the clases can't be modified.
Not true. First of all you can modify the T4 template to generate more (complex) code to achieve the things you want. And second, it generates partial classes which can easily be extended with custom (non-generated) code.
If you change the email in the Person instance only that instance is correctly marked as modified. That is absolutely correct behaviour. What do you expect? Do you expect that change to property in related entity will propagate changed state to relations? That would make STEs completely useless because any single change to entity graph would make all entities in the graph modified and each this modification causes additional roundtrip to the database.
If you want to set User as modified when you are changing email simply create some method or handle some event and call person.User.MarkAsModified()
I am using Entity Frameworks Code First. I have one entity that I need to keep a change history on. This entity has a double property and when it changes I need to record the amount change amount and date that it occurred. This means I need the old value the new value subtract and post ever time that value changes or when dbContect.SaveChanges() it called.
This project is really simple and I would like to keep it this way so I would prefer not add a service layer. I am simply making repository request in MVC controllers. (I know this is not pure but it is very agile)
How can I intercept changes to this entity so I can write to a change log?
You can listen for the ObjectContext.SavingChanges event, and then use the ObjectContext.ObjectStateManager property to look for the ObjectStateEntry(s) for the entity type that you are interested in.
ObjectStateEntry has properties to access the CurrentValues and OriginalValues, or only the original values for updatable properties using the GetUpdatableOriginalValues method.
Note: I have not tested this, but hopefully it will work for you.