Is JpaRepository.save() suitable for entities with auto generated IDs if we want to UPSERT them? - mysql

I'm facing a problem with duplicate records in a MySQL database when I'm upserting using JpaRepository.saveAll() entities which are already present in the database. I want new entities to be inserted, existing ones to be updated(if there are changes to any of the properties), otherwise no action is expected.
The entity classes id property is annotated with #GeneratedValue(GenerationType.IDENTITY) and the id column in the corresponding table in MySQL has auto-increment enabled. I'm pointing that out because JpaRepository.save(), which is invoked for each entity in saveAll(), does a check by id if the entity is already present in the database.
Here is where in my opinion the contradiction between save(), when used for updating, and auto-generation of IDs occurs: You can't update existing records because all of the entities passed into saveAll() will have newly generated IDs and thus the check in save() will always say that they are not present in the database.
Is my logic correct?
The only solution to the problem that I can think of is to create a custom query that compares the records in the database with the newly passed entities by the values of another column whose values are unique. I can't compare them by id because I will encounter the same problem as in save().
Is that good enough and are there any other solutions?

Depending how you look at it, your are either wrong or right.
What you describe in terms of behaviour is correct: If you pass in an entity with id set to null to save you will always create a new row in the database and never perform an update. In that sense you are correct. That behaviour is independent of how the id gets generated for new entities.
But the id of an entity defines its identity. If two entities have the same id they represent the same row in the database thus the same logical entity and the behaviour of save is exactly that of an upsert. This is also independent of how the id gets generated for new entities.
If you want an upsert based on a different column (or columns) you need to write custom code, for example using an actual upsert statement in a query annotation. Alternatively you can try to load the entity by the columns in question and if you succeed set its values as desired and otherwise create a new entity and save that.

Related

How to store modifications without changing the original DB record

I'm building an app in which users can create entities that are stored in my Entity table with some caracteristics like "space_below" and "width". Users can also create their own groups of entities by choosing amongst everybody's entities. Once they picked someone else's entity, I want them to be able to modify some caracteristics like the "width" and save these modifications for their own group. What would be to best way to do that in my DB?
First approach
Each time a user do a modification, create a new record (almost a duplicate) in the Entity table and then only refer to this new entity from the group where it was change.
In this case, I would need to add a column to my Entity table to differentiate original entites from copied entites.
Second approach
In any scenario, I will have a junction table between the Group table and the Entity table. In this junction table, I could add a "modification" column in which I store a JSON with the modifications. Something like { space_below: '8', width: '200' }. If there is no modficiation, the value of the column will be null.
I think I prefer the second approach since the first one involves a lot of duplicated data in the Entity table, but might not be aware of other implications.

How to refer to hardcoded values in code from the database?

In the (MySQL) database, I'm storing a view hierarchy, with each row in a table referring to a single view. There are several types of views, but they're stored in the same table.
In the application code, each type of view has its own class. Each row in the database instantiates one of these classes.
How should I refer to these classes from the database, so the application knows which class to use?
I can think of several possibilities:
Just specify the class name directly in the table, but this has the disadvantage of having to change lots of rows if the class name changes (which can be done in a single query if required).
Have a separate table storing class names, and use foreign keys to point to the row storing the correct class name. In this case, I could forgo having an ID field in this lookup table and instead have the class name as the primary key and target foreign key, and rely on a cascading UPDATE if the class name changes?
Are there better options available?
If I understand correctly you want to maintain an association between view-names and class-names.
Your bullets suggests, that there can be more than one view for the same class and both of your suggestions would work. The second bullet has the advantage that you can change the class name with a single update. But that doesn't buy you much, because as soon as more than just a single class-name changes, i.e. when the association itself changes, you need to update more than one row.
You might even create a separate table, holding this association. This would be the model for an n:m relationship, which is too general, so you'd have to place a unique constraint on the view-name. Essentially this will just factor out the concern of associating view-names with class-names and allow you to change this mechanism entirely without having to mess with your tables (except the one holding this association).
But actually I would not store any of this stuff in the database
(I also find it irritating that view-names are stored in the database and not in the application logic). The fact that there are class-names, should be of no concern to your database. This is application logic and it should be handled there. So what you need is a way to instantiate an object when the view-name is known. This looks like a classic factory to me. After all, if a class name changes, it is a change in the application code and life is easier, when all resulting changes lie in the application code as well.

Entity Framework 4.2 - How to realize TPT-Inheritance with Database-generated Primarykey Value?

I want to use the EF (4.2) in the following scenario:
There exists a database already (so I chose the database-first approach) and it is a SQL Anywhere DB.
I want to use persistence-ignorant business objects, so I use the DbContext Template to generate POCO classes from the EDM.
There is one simple inheritance hierarchy among my entities: an abstract base entity and two concrete derived entities.
In the database there is one table for each type of the inheritance hierarchy (Table-Per-Type Strategy).
Each of these three tables has a primary key column (Id, type:integer), and the association of a concrete entity to the base entity is done by having the same Id in both tables (that means that the primary key (Id) of the concrete type tables is at the same time a foreign key to the base table; a pretty common approach I think).
I had to define the Inheritance manually in the designer, since the EDM assistant does not automatically recognize, that is want to have an inheritance association between the described entities.
Until this point there wasn't any bigger problem. Now to the issue at hand:
There is a restriction for the database I use: Primarykey values have to be generated by the database, using a database function.
I want to call this function in a before-insert-trigger defined on the base-table.
To let the entity framework know that a value is generated by the database, I set the StoreGeneratedPattern property of the Id Property of the base-entity to Identity (As I understood, this is the way to tell EF to get the generated value after inserting a new instance of an entity).
When I create a new instance of a derived entity, add it to the corresponding DbSet of the DbContext and call SaveChanges on the context, a DbUpdateException is thrown, stating that a foreignkey constraint is violated.
By checking the request-log of the DB, I see that the base entity got inserted in the base table, but on inserting the row in the derived table, the above mentioned error occurs, because it obviously doesn't use the newly generated Id of the new entry in the base table.
Since I don't think there is much I can do on a database level against that, the question is, if the EDM or DbContext can be configured (or modified) to insert the base row first, then take the generated Id and use it for insertion of the derived row.
I know there are several way to avoid this situation (not using inheritance, using a stored procedure to insert a new derived entity, calling the id-generating db-function before inserting and set the Id property myself on the entity), but at the moment the above-described behavior would be the most preferable, so I want to make sure not to overlook something before deciding for any "plan B".
Any suggestions on this topic are much appreciated,
Thanks in advance.
Here is the code of the trigger:
ALTER TRIGGER "TRG_GENERATE_ID" before insert order 1 on
BASE_TABLE
referencing new as NewEntry
for each row
begin
declare NewID integer;
set NewID = F_GET_NEW_ID('BASE_TABLE', NewEntry.SOME_OTHER_ID);
set NewEntry.ID = NewID
end
The function "F_GET_NEW_ID" is called in the trigger to generate the new ID for a new entry in the base table. It has two parameters:
"Tablename" -> The name of the table for which a new ID should be generated,
and a second parameter that takes the value of a standardcolumn in all tables of the database (it is required to generate the new ID).

Linq-to-sql Error when removing new child record before submit

I have 2 tables with a master-detail relationship. We use a DataGridView to handle the detail records. The detail records have fields with lookup values (foreign keys). Instead of setting the ID value of the lookup tables we set the object value. This all works well, instead of one situation. If we add a detail row and set at least one of the lookup fields and remove this row before saving (submitchanges), we receive this error:
An attempt was made to remove a relationship between a TableX and a TableY. However, one of the relationship's foreign keys (TableY.TableXID) cannot be set to null.
This problem was discussed in this question (scroll down to answer of Neil Barnwell), but it doesn't have an answer for this particular problem. The solution "Call Datacontext.GetChanges" doesn't help as Datacontext doesnt expose a method GetChanges and GetChangedset fails with same error.
I assume there is some kind of mistake when removing the row. When you add the detail row to the master row, it is automatically attached to the context. So if you add with:
myMasterRow.DetailsRows.Add(myDetails);
You have to use DeleteOnSubmit to reverse the adding:
context.DetailsRows.DeleteOnSubmit(myDetails);
The data context is smart enough to treat the deletion as "undo add", so the entry will never hit the database.
It seems that if you set an object of the entity and remove the new record before any submit Linq still thinks you want to add the new record. In this case you have to set all objects that have FK references to null.
See this post which describes the problem and solution in detail: http://social.msdn.microsoft.com/Forums/en/linqtosql/thread/4170c7bb-f727-4e6a-9190-fc268ae4ce4b

Entity framework code first custom Id

I need to have a custom Id when creating a model. For eg.
These are my constraints:
8 digits.
based on contraints, an Id must begin with certain numbers.
How do I make sure I generate no duplicates? I'm using a repository pattern, so My save method looks like:
public User SaveUser(User user);
You can be sure that you will not generate duplicates because it is Id = Primary key which must be unique. Once you try to save the entity whit duplicate id you will get an exception.
The generation algorithm depends on many other factors including where do you want to generate Id, how do you want to differ new Id, how complex is the logic for generation and how do constraint changes and one more important thing - can you have a gaps in the sequence of subsequent ids (for example if you rollback transaction with already generated id)? You will have to find your own mechanism based on these requirements.
I did this few times and I used separate table for sequences storing last used number for each sequence type (constraint) + stored procedure for generating next, storing it and returning it back to application + intensive locking / restrictive transaction isolation levels.