Foreign key empty on Envers audit table for relation OneToMany - spring-boot

I have the following relation and the foreign key is always empty in the audit table after the new revision:
#ManyToOne
#Audited(targetAuditMode=RelationshipTargetAuditMode.NOT_AUDITED)
#JoinColumn(name="mail_iid")
#private Mail mail;
...
#OneToMany(cascade=Cascade.ALL, orphan = true, fetch= fetchType.LAZY)
#JoinColumn(name="mail_iid")
private List<Attachments> attachments;
After the insertion of a new register, the original table have the iid but not the revision one.
Somebody knows about this issue.

There is only one way for this to happen, which is not managing bidirectional relationships properly.
I suspect you are never calling Attachments#setMail to assign the newly created Mail entity to the Attachments entities and instead simply add the Attachments entity to the collection that your Mail entity cascades.
This type of one-sided maintenance of bidirectional relationships is wrong and can lead to really incorrect results, particularly if entity instances are being inspected from the 1LC and are never being refreshed from the database; which is precisely why you're seeing the audit table with null in your mail_iid field.
Your code should make sure that both sides of the relationship get set properly
// setup bidirectional mappings
attachments.setMail( mail );
mail.getAttachments().add( attachments );
When you do it this way, you'll end up with mail_iid being populated in your audit table as you would have expected and also avoids any issues when traversing cached instances of an entity's object graph that is already loaded in the 1LC.

Related

Why is Hibernate #OnetoMany relationship required?

I have two entities Library and Books which are associated by Hibernate #OneToMany in a spring boot project. Fetching books in a particular library through the getter functions renders a LazyInitialisationException. The solution that I could find was making a query in the Books entity and fetching all the books corresponding to the library-id of the library. So, I was thinking why is oneToMany relationship required if we can just store a key corresponding to library in the Books table.
Simply storing a key doesn't provide any consistency assurances. Also, using defined OneToMany or ManyToOne you can also define the cascade types (you would only need to save the parent entity and then all the children would automatically be saved, in a single transaction).
The quick way to fix your problem would be to use FetchType EAGER, but I would recommend fixing whatever you have misconfigured.

How to generate a foreign key for a Many-to-one relationship (unidirectional)?

I want to have an entity that have a many to one relationship with another entity, but with generated foreign key using JPA (no foreign key in the database), is it possible?
I know there is a solution using one to many and many to one, but I want to only have a many to one, because I only want it to be a unidirectional
You can have OneToMany and ManyToOne, both in unidirectional and bidirectional way. Obviously, when you have a many-to-one relation from one side, you will have a one-to-many from the opposite side.
Also, you should note that only one foreign key in the many-side to one-side can handle this relation.
If you use #JoinColumn(name="some_column_name") just below one of the #OneToMany or #ManyToOne annotations, the hbm2ddl should be able to create the proper foreign key in your table.
However, try not to rely on hbm2ddl and maintain the database schema yourself.

Treat Entity with Id NULL as NEW

To the question "Save Differences with Entity ID" I found the following answer:
"For Entities, Id property cannot be null, so you need to map this class as ValueObject. If so,
Id property is treated as regular property and it not goes to GlobalId of this object."
My question is:
Why can't an entity be treated as NEW if the Id is NULL?
I have an object graph that is fetched from the database, and between two javers commits an entity is added to a list in the graph.
Two commits and in the second commit there is a new entity (Id NULL)
Get the change => exeption because Javers can't create a GlobalId.
I can get arround this by doing EntityManager - persist (creates Id:s), but I would like to avoid doing that. The present code may do a persist later or it just lets the transaction finish.
Because the Id is NULL, the entity is NEW. Would it be possible to generate a unigue temp Id (allow Id = NULL) to be able to create the GlobalId?
In the change list, the entity would be reported as NEW. No need to compare with earlier commits.
You should compare/commit your objects when they are fully initialized so when they have Ids.
An entity without Id can't be handled by JaVers for several reasons:
it can't be compared to other entity/version (diff algorithm is based on GlobalIds)
it can't be queried from JaVersRepository (queries use GlobalIds)
If you are using Hibernate, compare/commit your new objects after Hibernate assigns them Ids from sequences.
Another options:
don't use sequence-generated values as JaVers Id but some business identifiers
if an Entity doesn't have a business identifier you can generate UUID in a constructor and use it as JaVers id (and also database PK if you like)

How to cascade on softdeletes in Laravel4?

Tried to use foreign keys with delete cascade and softDeletes without much luck.
I have 2 tables: Users, Events. Both tables have softDeletes.
Users can have 0..n Events.
Events have an user_id, used as foreign key on users, like this:
$table->foreign('user_id')->references('id')->on('users')->onDelete('CASCADE')->onUpdate('CASCADE');
Problem is, when I delete an User, it gets soft-deleted, but its Events do not - either soft deletion or physical deletion.
Am I doing something wrong, or is this the correct Eloquent behavior?
Secondly, if that is the correct behavior, how to best implement deletion cascade? maybe overriding the delete() method in my Models like this ...
public function delete()
{
//delete all events...
__parent::delete()
}
?
The DB's foreign key won't do anything because you haven't changed the primary key in question. Only if you update or delete the primary key will the related rows be modified.
From everything I can find about this topic, the solution is to use Eloquent's Model Events to listen for a delete event, and update the related tables.
Here's one StackOverflow question about it.
Alternatively, you can "extend" the delete() method and include the functionality directly as well. Here's an example.
You're overthinking this.
Either just delete the events right before you delete the users:
$user->events()->delete();
$user->delete();
Or create a customer delete function in the user model:
public function customDelete(){
$this->events()->delete();
return $this->delete();
}
You could also add a model observer and watch for the deleting or delete event, but in the scenario you mentioned above, the previous two methods would be a more simple solution.
http://laravel.com/docs/4.2/eloquent#model-observers
If I understand correctly, you are trying to cascade softdeletes in both tables?
I believe to do this with ON UPDATE CASCADE is not the correct approach. I'll try to explain why...
To even attempt to do this you need to create a relationship of foreign key to composite key.
ie you need to link the (events.user_id and deleted_at) to (user.id and delete_at). You change one, it'll update the other.
First you will need to add a default rule to your deleted_at columns, as you can not link on null values.
So add to your migrations for both tables...
$table->softDeletes()->default('0000-00-00 00:00:00');
Add to your user table a unique key using 'id' and 'deleted_at'
Schema::table('users; function($table) {
$table->unique(array('id','deleted_at'))
});
Then in the events table create a foreign key like so (links to the unique key)
Schema::table('events; function($table) {
$table->foreign(array('user_id','deleted_at'),'events_deleted_at_foreign_key')->
}->references(array('id','deleted_at'))->on('users')->onUpdate('CASCADE'));
Run this, you should now find if you soft delete your user, it will soft delete its' events.
However if you now try to soft delete an event, it will fail on the foreign key restraint. Why you might ask!?
Well what you're doing is creating a Parent Child relationship using id,deleted_at in both tables. Updating the parent, will update the child. And the relationship is unbroken. However if you Update the child, the relationship is now broken, leaving the child as an orphan in the table. This fails the foreign key restraint.
Sooo a long winded answer, but hopefully a good explanation of why what you're trying to do won't work and save you a whole lot of time trying to do this with ON UPDATE CASCADE. Either get in to the TRIGGERS, and TRIGGER a function to handle what you're trying to do, or handle it in your application. Personally I'd do it with TRIGGERS so the database remains it's own entity and not having to rely on anything to keep data integrity.
delimiter //
CREATE TRIGGER soft_delete_child AFTER UPDATE ON db.users FOR EACH ROW
BEGIN
IF NEW.deleted_at <> OLD.deleted_at THEN
UPDATE events SET deleted_at=NEW.deleted_at WHERE events.user_id=NEW.id;
END IF;
END;
//
delimiter ;

Magento: How to delete parent object when child reference changes?

Given:
Two custom classes in Magento with a Many-to-One relationship between them.
The child holds a foreign key to the parent.
The database is set to cascade deletes.
There are cases when a child's reference changes to a different parent. In some of those cases, I want to delete the parent in the afterSave method of the child. When I do this, the child itself disappears, since the change of FK to the new parent hasn't been written to the database yet, and the database level cascade kicks in.
How can I arrange for the deletion of the parent object after the write of the new foreign key in the child object?
afterSave triggers before the query has been written to DB, as you've noticed yourself. You need to use *_save_commit_after event. Where asterisk is your Models event_prefix. Create an Observer and listen for this event, that way you can be sure that info in DB has been already updated, and you won't suffer the foreign key effect.

Resources