Cascade-like deletion on a ManyToOne relationship - postgresql

I have a #ManyToOne relation between Workpackages and Projects. Where a project can have one to many workpackages.
My Workpackage entity refers to the Project one like such:
#NotNull
#ManyToOne(targetEntity = ProjectEntity.class)
private ProjectEntity project;
Therefore each line in my Workpackage table references the id of a certain project.
But when I try to delete a project, I get a foreign key constraint (which makes sense, since my workpackage depends on a project, and should be deleted before the former is deleted).
But a cascade = CascadeType.REMOVE won't work since I am deleting my project and not my Workpackage. Would there be a way to delete the Workpackage related to a certain project upon its deletion?
Being new to Hibernate, nothing obvious comes to mind other than going through every Workpackage and deleting them one by one.

ON DELETE CASCADE : if you delete a project row in table Project the engine will delete as well the related workPackages. This can used to make automatic cleanups on secondary tables.
When declaring schema, do the following thing
alter table Workpackage add constraint constraintName
foreign key (id) references Project(id) on delete cascade
you will have to set child's object as null explicitly.
for (Workpackage child : project.getWorkpackages()) {
child.setProject(null);
}
session.delete(OwningSide);

Related

Foreign Key constraint on a #OneToMany relationship

I want to make sure I understand what I am doing in my DB representation & with hibernate.
My project management tool lets my user create a Project that is composed of Phase(s).
I do not wish to create a link table and have proceeded like such in my entities:
My ProjectEntity:
#OneToMany(mappedBy = "id")
private Set<PhaseEntity> phases = new HashSet<>(0);
and my PhaseEntity:
#NotNull
#ManyToOne(targetEntity = ProjectEntity.class)
private ProjectEntity project;
After filling up my PhaseEntity and saving it via my DAO
phaseDao.save(phaseEntity);
I get the following error:
ERROR: insert or update on table "phase" violates foreign key constraint "fk8x1x98c4hq4ps0d30y6nxe90u"
Detail: Key (phase_id)=(1820) is not present in table "project".
The way I see it, pushing a phase containing a specific ProjectEntity to my database should create 1 entry and update another one, the first should be my phase entry, and the latter should be an update to a project by adding a phase to its Set.
Do I have to explicitly save my phase in my project table?
Do yo see any flaws in my reasoning?
Just sharing the knowledge!
"a Project that is composed of Phase(s)"
So , if you want to insert a phase in "Phase" table, you need to have a role in Project table which has the same 'class' column value as "class" column value of newly inserted row in Phase table.
A foreign key is a column (or columns) that references a column (most often the primary key) of another table. The purpose of the foreign key is to ensure referential integrity of the data. In other words, only values that are supposed to appear in the database are permitted.
We have two tables, a Project table that includes all projects, and a Phase table that includes all phases. A Project that is composed of Phase(s). To enforce this logic, we place a foreign key on the Phase table and have it reference the primary key of the Project table. This way, we can ensure that all phases in the Phase table are related to a project in the Phase table. In other words, the Phases table cannot contain information on a project that is not in the Project table.

Entity Framework 6 Casscade Deletes and DropForeignKey fails on auto generated constraint name

Entity Framework 6 Casscading Deletes and DropForeignKey fails on auto generated constraint name
I've been running into a bit of an issue with Entity Framework and cascade deletes between two tables on several one-to-many relationships.
Initially it looked like the correct path to take was to configure the table mappings with the OnModelCreating method of DbContext turning off cascade delete in a manner such as
modelBuilder.Entity<SourceTable>()
.HasOptional(x => x.NavigationProperty)
.WithOptionalDependent()
.WillCascadeOnDelete(false);
This however did not work throwing an exception stating
Cannot delete or update a parent row: a foreign key constraint fails...
More research lead me to believe that this is because all affected entities must be loaded into the context (eager fetched) so that entity framework may set the FK references to null as part of the transaction. This is not practical for my needs based on the size of the relational graph I'd be dealing with.
My next approach was to modify the Seed method of the Configuration class and run some arbitrary SQL to drop the Foreign Key constraint and re-add it as a ON DELETE SET NULL constaint. This worked in most cases, however one of the consraints has what appears to be an auto generated unpredicatable name that is diffrent on each call of Update-Database. Given that the name can't be predicted the ALTER statments aren't particualr helpful
context.Database.ExecuteSqlCommand(#"ALTER TABLE SourceTable DROP FOREIGN KEY FK_9405957d032142c3a1227821a9ed1fdf;
ALTER TABLE SourceTable
ADD CONSTRAINT FK_ReasonableName
FOREIGN KEY (NavigationProperty_Id) REFERENCES NavigationProperty (Id) ON DELETE SET NULL;");
Finally, I've taken the apprach to use the migration functionality (DbMigration) and override Up method and leveraging the DropForeignKey method along side more explicit SQL to re-add the constraint (EF does not appear to provide a factility to create a ON DELETE SET NULL constraint).
DropForeignKey("SourceTable", "NavigationProperty_Id", "DestinationTable");
Sql("ALTER TABLE SourceTable ADD CONSTRAINT FK_ReasonableName FOREIGN KEY (NavigationProperty_Id) REFERENCES DestinationTable (Id) ON DELETE SET NULL;");
This works great, up until I encounter the constraint with the auto generate name. At this point the DropForeignKey method fails with an exception that is swallowed up by
System.Runtime.Serialization.SerializationException: Type is not resolved for member 'MySql.Data.MySqlClient.MySqlException,MySql.Data...
When dumping the migration to a SQL script file it becomes clear that the DropForeignKey simply generates a FK with a more predictable, non-ambiguous byte stream array.
Is there a proper EF Code First approach to solve the problem of setting FK column values to null when deleting the refrenced row, or am I stuck having to hand code SQL in order to gain this functionality?

How to enable cascading delete in Edmx Designer on many to many relation

I am using VS2012 and the Entity designer to generate both the database and the models. I have a very basic scenario of Table1 to Table1and2JoinTable to Table2. Something like Students, Classes, StudentClasses. You can have many students in many classes. I would like to have a cascading delete. So if you delete a student any rows in the StudentClass join table are deleted for that student id. Same for deleting a class any rows in the StudentClass are deleted for that class id. After setting up the many to many association in the designer and setting the cascade delete options you get the following error when you attempt to generate the database:
Error 132: End 'Student' on relationship 'Model1.StudentClass' cannot have operation specified since its multiplicity is ''. Operations cannot be specified on ends with multiplicity ''.
Here is a small example:
Here is the association created:
And the resulting error messages:
Here is a portion of the SQL code for generating the database tables:
-- Creating foreign key on [Students_Id] in table 'StudentClass'
ALTER TABLE [dbo].[StudentClass]
ADD CONSTRAINT [FK_StudentClass_Student]
FOREIGN KEY ([Students_Id])
REFERENCES [dbo].[Students]
([Id])
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- This should be ON DELETE CASCADE ON UPDATE NO ACTION;
GO
-- Creating foreign key on [Classes_Id] in table 'StudentClass'
ALTER TABLE [dbo].[StudentClass]
ADD CONSTRAINT [FK_StudentClass_Class]
FOREIGN KEY ([Classes_Id])
REFERENCES [dbo].[Classes]
([Id])
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- This should be ON DELETE CASCADE ON UPDATE NO ACTION;
GO
I know how to work around this by just editing the database script and add in the on delete cascade option. But, I don't want to do this because I am going to come back to the designer many times as the project grows and I don't want to have to remember this step every time.
Has anyone figured out how to resolve this?
It seems to be an edmx restriction, which I don't really understand. Code-first is perfectly capable of generating a junction table with two cascading ON DELETE constraints, but model-first and database-first do not allow the same configuration in the edmx. Normally, cascade actions are configured on the 'one' end of an association. Maybe it is too complex to check the validity of cascade actions on '*' ends (only when both ends are '*').
For the cascaded delete to happen with a context based on an edmx model, you have to load a parent and its children and then delete the parent.
var cls = db.Classes.Include(c => c.Students).Single(c => c.Id = 1);
db.Classes.Remove(cls);
db.SaveChanges();
The executed SQL statements show that the Class is fetched from the database in a JOIN statement with Student. Then the StudentClasss and the Class are deleted respectively.
Obviously, this is much more expensive than enabling cascaded delete in the database.
The work-around to modify the DDL each time after is was generated is not attractive, of course. But I think the only alternative is to make StudentClass part of the model and configure cascaded delete on the 'one' ends of the new associations. Or go code-first.
First of all make sure that you have an ON DELETE CASCADE specified in your Foreign Keys on database side.
I had similar problem and just adding ON DELETE CASCADE solve it instead of setting End1OnDelete and End2OnDelete properties.

JPA 2.0 Foreign Key Constraint

I have simple OneToOne relationship:
Data <-> TherapyResult
I would like to express the following constraints
with JPA.
If a Data entity gets removed the associated TherapyResult should be delete,too
If a TherapyResult entity gets removed the associated Data entity should remain in the db
The first constraint is really easy with JPA as I can add CascadingType.REMOVE
#OneToOne(cascade = { CascadeType.REMOVE, CascadeType.REFRESH })
private TherapyResult therapyResult;
For the second constraint I would like to add something like
#JoinColumn(columnDefinition = "DATA_ID BIGINT CONSTRAINT THERAPYRESULTDTAID FOREIGN KEY (DATA_ID) REFERENCES DATA (ID) ON DELETE SET NULL")
However this does not work. OpenJPA seems to have something similiar, but I want to use JPA 2.0 and EclipseLink. OpenJPA ForeignKey.
Another solution would be using #PreRemove described here, which works but looks a bit "none-best-practices" to me. However just a feeling.
My setup is:
Eclipse 3.7.1
EclipseLink 2.3
Apache Derby 10.8.3 and/or HSQLDB
any help is appreciated,
Muki
You can't use pure JPA to specify foreign keys ... that spec doesn't include the ability. JDO is the only standard with the ability to define FKs. You have to use implementation specifics, or just define the schema yourself and let the JPA impl run on it.
If you have a foreign key from Data to TherapyResult, and Data is the owner of the association, then
removing the Data will delete the TherapyResult automatically if cascade is set to REMOVE
you just need to set the therapyResult field to null and then delete the TherapyResult to have what you need. Another option is to set orphanRemoval to true, in which case setting the therapyResult field to null is sufficient to remove it from the database
If you have a foreign key from TherapyResult to Data, and TherapyResult is the owner of the association, then
removing the Data will delete the TherapyResult automatically if cascade is set to REMOVE on the Data.therapyResult field.
removing the TherapyResult will leave the Data in the DB provided the cascade is not set to REMOVE on the TherapyResult.data field

JPA how to remove parent without delete children?

I'm trying to remove a parent, but I keep getting a Foreign Key violation. If I put Cascade.ALL in the parent, it delete the children too. And it's now what I want.
I have my parent class : Docteur
//bi-directional many-to-one association to Patient
#OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH}, orphanRemoval=false, mappedBy="docteur")
private List patients;
and my children are : Patient
I put that
#ManyToOne()
private Docteur docteur;
but in my case, the patient choul only have one Docteur.
In my Manager class. I try lot of things that didn't work
here my latest version
Clinique clinique = read(clinique_ID);
Docteur docteur = entityManager.createNamedQuery("getDocteur", Docteur.class).setParameter("clinique_ID", clinique_ID).setParameter("docteur_ID", docteur_ID).getSingleResult();
clinique.getDocteurs().remove(docteur);
entityManager.merge(clinique);
entityManager.persist(clinique);
Here the error that I get :
Cannot delete or update a parent row: a foreign key constraint fails (jerabi_asteriskdb/Patient, CONSTRAINT FK340C82E5A10F077E FOREIGN KEY (docteur_DOCTEUR_ID) REFERENCES Docteur (DOCTEUR_ID))
You get a foreign key violation because the database checks that every docteur_id in the patient table refers to a valid docteur. This is the whole point of foreign keys. The database ensures that you don't delete a docteur still referenced by patients.
In order to delete your docteur, you must ensure that no other record in the database references this docteur_id. So, you must update all the patients of this docteur and set their docteur_id to null :
Docteur docteur = entityManager.createNamedQuery("getDocteur", Docteur.class).setParameter("clinique_ID", clinique_ID).setParameter("docteur_ID", docteur_ID).getSingleResult();
for (Patient patient : docteur.getPatients()) {
patient.setDocteur(null);
}
docteur.patients.clear();
clinique.getDocteurs().remove(docteur);
Also, all the attached (persistent) entities are automatically updated by Hibernate. There is no need to persist and merge them. Read http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html_single/#objectstate-overview.
So that a relation database can enforce data integrity, references to dependent rows in referencing tables have to be considered. SQL 2003 specifies 5 different referential actions:
CASCADE: dependent rows get deleted
RESTRICT: delete fails with an error
NO ACTION: like delete, but allows triggers to run first, in case they fix the error
SET NULL: sets the referencing columns to null (at least one column must be nullable)
SET DEFAULT: sets the referencing columns to their default value (which will then reference another existing row in the table, unless at least one default is NULL)