FIWARE Orion: change entity type - fiware-orion

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

Related

Spring Data JDBC: Can I create my UUID PKs on the client side, and not on the server? [duplicate]

I'm playing around with spring-data-jdbc and discovered a problem, with I can't solve using Google.
No matter what I try to do, I just can't push a trivial object into the database (Bean1.java:25):
carRepository.save(new Car(2L, "BMW", "5"));
Both, without one and with a TransactionManager +#Transactional the database (apparently) does not commit the record.
The code is based on a Postgres database, but you might also simply use a H2 below and get the same result.
Here is the (minimalistic) source code:
https://github.com/bitmagier/spring-data-jdbc-sandbox/tree/stackoverflow-question
Can somebody tell me, why the car is not inserted into the database?
This is not related to transactions not working.
Instead, it's about Spring Data JDBC considering your instance an existing instance that needs updating (instead of inserting).
You can verify this is the problem by activating logging for org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate. You should see an update but no insert.
By default, Spring Data JDBC considers an entity as new when it has an id of an object type and a value of null or of a primitive type (e.g. int or long) and a value of 0.
If your entity has an attribute with #Version annotation that attribute will be used to determine if the instance is a new one.
You have the following options in order to make it work:
Set the id to null and configure your database schema so that it will automatically create a new value on insert. After the save your entity instance will contain the generated value from the database.
Note: Spring Data JDBC will set the id even if it is final in your entity.
Leave the id null and set it in a Before-Convert listener to the desired value.
Let your entity implement Persistable. This allows you to control when an entity is considered new. You'll probably need a listener as well so you can let the entity know it is not new any longer.
Beginning with version 1.1 of Spring Data JDBC you'll also be able to use a JdbcAggregateTemplate to do a direct insert, without inspecting the id, see https://jira.spring.io/browse/DATAJDBC-282. Of course, you can do that in a custom method of your repository, as is done in this example: https://github.com/spring-projects/spring-data-examples/pull/441

AsNoTracking vs HasNoKey

It's not completely clear to me what's the diference between using AsNoTracking() on a entity that has a key, and using HasNoKey(). As I understand, when using AsNoTracking(), even though the changetracker does not track changes on the resulting objects, the objects are still kept in the DbContext's memory. If you try to Attach a new object with the same key as one already in memory, you get an error. (correct me if i'm wrong).
So is the behavior the same if you use HasNoKey (former DbQuery<>)?
Introducing ModelBuilder.Entity<>().HasNoKey() is one of the breaking changes in EF Core 3.0 as stated:
A query type now becomes just an entity type without a primary key. Keyless entity types have the same functionality as query types in previous versions.
Query types were a means to query data that doesn't define a primary key in a structured way. That is, a query type was used for mapping entity types without keys (more likely from a view, but possibly from a table) while a regular entity type was used when a key was available (more likely from a table, but possibly from a view).
You said that:
If you try to Attach a new object with the same key as one already in memory, you get an error. (correct me if i'm wrong). So is the behavior the same if you use HasNoKey (former DbQuery<>)?
-Tracking in EF Core transaction are based on Entity Primary key. As the above documentation clearly stating that EntityType with .HasNoKey() is QueryType which does not have any key defined. So tracking is completely void in case of EntityType with HasNoKey().
For more details : Query types are consolidated with entity types

Validation in a Doctrine entity where that property itself is an Entity derived from another entity using constraints

First of all I hope this question is allowed because I guess its a rather framework-specific question (Symfony). I am running into the following problem:
A form is submitted and checked for validity for creating a new 'Toernooionderdeel' and as a result the Persist and Flush operations of Doctrine for this Entity are to be called attempting to put the newly created entity into the database. Fairly basic stuff to this point. But the form fails at ->isValid() before persisting and flushing can commence.
In my case the Constraints are applied on properties in various ways through annotation.
#Assert\Valid specifically is used on properties that define ManyToOne relationships with other entities and it all works fine, until...
I attempt to use #Assert\Valid on a property of 'Toernooionderdeel' called '$toernooi' which represents a ManyToOne relationship (Toernooionderdeel -> Toernooi).
The difference between this one and the other relationships I validate in the same way is that this 'Toernooi' Entity is derived from another entity, where the other entities aren't.
Despite obviously having a 'Toernooi' defined under the '$toernooi' property of 'Toernooionderdeel', the Constraint detects it as a violation and thus the form doesnt pass validation.
What things do i have to consider when doing this type of validation (using Constraints) on an 'advanced' entity construction like this? Has any of you done this before and if so, how did you do it?
When the entity referenced in a property ("child") is validated in the parent object via Assert\Valid, it's validity is also checked. When the child entity isn't valid, the parent isn't valid either (transitive).

Can a Orion Context Broker entity have two attributes with the same name but different type?

Can a Orion Context Broker entity have two attributes with the same name but different type?
If yes, is it controlled by Orion? Is an error returned when such an entity is created?
If no, what happens when a convenience operation tries to get the value of one of the 2 attributes (AFAIK, the attribute type is not passed in the operation).
From Orion 0.17.0 on, type is no longer used to identify an attribute. Thus, attributes are identified by name plus (optionally) metadata ID. I will assume version >=0.17.0 in the rest of this answer.
Orion doesn't control violation of that rule when processing operations to create entities or append attribute on existing entities. In those cases, only one instance of the attributes with the same identification is taken an stored in the DB, the others are ignored. It is not recommended at all that a client do such kind of operations (in the future, Orion may check that condition and return an error to the client).
Taking account the above paragraph and regarding what happens when a convenience operation tries to get the value of one of the 2 attributes (AFAIK, the attribute type is not passed in the operation)? note that situation cannot happend. I mean, at Orion DB will never store two attributes with the same identification associated to the same entity.
Some additional comment regarding metadata ID: I don't recommend the use of metadata ID as any potential ID can be included in the name and you will get your client much simpler, e.g. you don't need and attribute with name=temperature and id=outside if you use name=temperature::outside or any other namespacing technique.

How do I add a new record to objectStateEntryList in SaveChanges override

I have several entities that contain datetime fields for EffectiveAsOf and ExpiredAsOf. When an entity is modified I want to override the SaveChanges method and rather than just update the existing entity have the code save the original record back to the database with an ExpiredAsOf datetime set to the current time, and a new record inserted with the new data and EffectiveAsOf set to the current time with ExpiredAsOf set to null.
I know that the ObjectStateEntry items in the objectStateEntryList contain CurrentValues and Original values objects, as well as an Entity object. What does EF use to write data to the DB the CurrentValues data or the Entity? How do I go about creating a new entry? Or, am I going about this the wrong way entirely?
I know that I can handle this in the entities outside of EF, but would rather have EF detect and handle these entities automatically.
Thanks in advance for your help and insight,
Jim
EF by default will use both - it uses original values to check for a concurrency issue (i.e. if the record has changed since you loaded the data from the DB) then uses the entity's current/modified values to update the DB record.
It is not possible to have EF "detect and handle these entities automatically". You will need to create a new instance of the entity object, copy the values from the existing entity object, set the appropriate effective and expired dates on both objects, add the new entity object to the DbContext, then save changes. The best place to do this is by overriding the SaveChanges() method of your DbContext. To keep it as clean and manageable as possible, I suggest using the repository pattern.