JPA how to remove parent without delete children? - jpa

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)

Related

Cascade-like deletion on a ManyToOne relationship

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);

Delete row from many-to-many table (VB.Net, .edmx)

I have Users and Regions. A User can be assigned to any number of Regions.
To implement this I have a table of Users, a table of Regions, and a third table UserRegion which is just UserID, RegionID (both columns form the primary key and they have foreign key relationships to the User and Region tables).
Entity Framework does not import the UserRegion table into my data model, instead it creates a property of each User object which is a list of Regions, and another on each Region object which is a list of Users. This is very useful except that I can't figure out how to un-associate a User from a Region.
The below code
Dim db as New DatabaseContext
Dim user = db.Users.Where(stuff).First()
user.Regions.Clear()
db.SaveChanges()
produces this error:
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
How can get rid of the relationship rows I don't want anymore?
I figured this out.
The relationship needs to be removed from both sides. So the code should be:
user.Regions.Clear()
For Each r in db.Regions
r.Users.Remove(user)
Next
db.SaveChanges()
Now I have a zillion for loops peppering this function but oh well. Hopefully this helps someone.

How can I create multiple composite foreign key constraints which reference the same table?

I am using EclipseLink 2.4.0 and I am trying to find a way to generate the following DDL statements:
ALTER TABLE DELTA ADD CONSTRAINT DELTAFK1 FOREIGN KEY (APPKEY, NEWREVISION) REFERENCES REVISION (APPKEY, REVISION);
ALTER TABLE DELTA ADD CONSTRAINT DELTAFK2 FOREIGN KEY (APPKEY, OLDREVISION) REFERENCES REVISION (APPKEY, REVISION);
Each row in the DELTA table represents the changes between the two specified revisions and the primary key is made up of APPKEY, NEWREVISION, and OLDREVISION. Only the first alter statement is being generated with the following relationship annotations defined in the Delta.java class:
public class Delta {
#EmbeddedId
private DeltaPK deltaPk;
#ManyToOne
#PrimaryKeyJoinColumns({
#PrimaryKeyJoinColumn(name="appKey", referencedColumnName="appKey"),
#PrimaryKeyJoinColumn(name="newRevision", referencedColumnName="revision")
})
private Revision newRevision;
#ManyToOne
#PrimaryKeyJoinColumns({
#PrimaryKeyJoinColumn(name="appKey", referencedColumnName="appKey"),
#PrimaryKeyJoinColumn(name="oldRevision", referencedColumnName="revision")
})
private Revision oldRevision;
The name values of each PrimaryKeyJoinColumn, ‘appKey’, ‘oldRevision’, and ‘newRevision’ are all fields defined in the DeltaPK class and the referencedColumnName values are fields defined in the Revision class.
I’ve tried a bunch of variations, and the closest I’ve gotten was when I comment out the PrimaryKeyJoinColumn for ‘appKey’ for the oldRevision object. Then the second alter statement is generated, but it only includes the oldRevision value (not appKey), as you would expect. Any thoughts or suggestions on how to accomplish this would be appreciated.
I had to walk through the EclipseLink source code in the debugger for a while before I found out how to solve this one. It turns out there was one key part of the problem that I did not mention in my original question (because I didn't know it was part of the problem). The fields 'appKey' and 'revision' from the Revision class are not the primary key for that table, however, they do make up a uniqueness constraint:
#Table(
name = "REVISION",
uniqueConstraints = #UniqueConstraint(columnNames = {"appKey", "revision"})
)
It turns out that EclipseLink was generating the name of the foreign key constraint partially based on the ordering of the columnNames in this uniqueness constraint. This caused both of my foreign key constraints to be generated with the same name which ultimately led to the second one being ignored and not generated. (See the following methods in org.eclipse.persistence.tools.schemaframework.TableDefinition if you'd like to put together all the details.)
buildForeignKeyConstraint(List<String> fkFieldNames, List<String> pkFieldNames, TableDefinition targetTable, DatabasePlatform platform)
buildForeignKeyConstraintName(String tableName, String fieldName, int maximumNameLength, DatabasePlatform platform)
addForeignKeyConstraint(ForeignKeyConstraint foreignKey)
In short, when I simply reordered the values for cloumnNames like so:
#UniqueConstraint(columnNames = {"revision", "appKey"})
I got two differently named foreign key constraints (because of the two mappings between 'revision' and 'newRevison' and 'oldRevision') as shown below:
ALTER TABLE DELTA ADD CONSTRAINT DELTA_NEWREVISION FOREIGN KEY (NEWREVISION, APPKEY) REFERENCES REVISION (REVISION, APPKEY)
ALTER TABLE DELTA ADD CONSTRAINT DELTA_OLDREVISION FOREIGN KEY (OLDREVISION, APPKEY) REFERENCES REVISION (REVISION, APPKEY)
You may get slightly different output for different database platforms (I was using Derby), but I think the general issue and solution are the same. I hope I explained that clear enough to help someone else out in the future.

Mysql workbench foreign key options [Restrict, Cascade, Set Null, No Action], what do they do?

In foreign key options on update and on delete.
What does each field [Restrict, Cascade, Set Null, No Action] do?
If you take one by one :
For both update and delete :
if you try to update / delete the parent row :
Restrict : Nothing gonna be delete if there is a child row
Cascade : the child row will be delete / update too
Set Null : the child column will be set to null if you delete the parent
No action : The child row will not be concern of the delete / update
The table containing the foreign key is called the referencing or child table, and the table containing the candidate key is called the referenced or parent table.
Set NULL : Sets the column value to NULL when you delete the parent table row.
CASCADE : CASCADE will propagate the change when the parent changes. If you delete a row, rows in constrained tables that reference that row will also be deleted, etc.
RESTRICT : RESTRICT causes you can not delete a given parent row if a child row exists that references the value for that parent row.
NO ACTION : NO ACTION and RESTRICT are very much alike. when an UPDATE or DELETE statement is executed on the referenced table, the DBMS verifies at the end of the statement execution that none of the referential relationships are violated. in short child row no concern if parent row delete or update.
When an UPDATE or DELETE operation affects a key value in the parent table that has matching rows in the child table, the result depends on the referential action specified using ON UPDATE and ON DELETE subclauses of the FOREIGN KEY clause. MySQL supports five options regarding the action to be taken, listed here:
CASCADE: Delete or update the row from the parent table, and automatically delete or update the matching rows in the child table. Both ON DELETE CASCADE and ON UPDATE CASCADE are supported. Between two tables, do not define several ON UPDATE CASCADE clauses that act on the same column in the parent table or in the child table.
SET NULL: Delete or update the row from the parent table, and set the foreign key column or columns in the child table to NULL. Both ON DELETE SET NULL and ON UPDATE SET NULL clauses are supported.
If you specify a SET NULL action, make sure that you have not declared the columns in the child table as NOT NULL.
RESTRICT: Rejects the delete or update operation for the parent table. Specifying RESTRICT (or NO ACTION) is the same as omitting the ON DELETE or ON UPDATE clause.
NO ACTION: A keyword from standard SQL. In MySQL, equivalent to RESTRICT. The MySQL Server rejects the delete or update operation for the parent table if there is a related foreign key value in the referenced table. Some database systems have deferred checks, and NO ACTION is a deferred check. In MySQL, foreign key constraints are checked immediately, so NO ACTION is the same as RESTRICT.
SET DEFAULT: This action is recognized by the MySQL parser, but InnoDB rejects table definitions containing ON DELETE SET DEFAULT or ON UPDATE SET DEFAULT clauses.
For an ON DELETE or ON UPDATE that is not specified, the default action is always RESTRICT.
Copied above text from:
https://dev.mysql.com/doc/refman/5.5/en/create-table-foreign-keys.html

Delete related elements in 1:N relation and not only inserts null in the foreign key

I have the following class
public class ObjectA{
private List<ObjectB> list;
}
ObjectA and ObjectB are in 1:N relation.
I want to delete some of ObjectB instances and I use:
while (objectA.list.Any())
objectA.list.Remove(objectA.list.First());
List is of the relation table -
List<ObjectAobjectB>
In the Database I have defined therelation as a nullable foreign key otherwise I get
The operation failed: The relationship could not be changed because one or more of
the foreign-key properties is non-nullable. When a change is made to a relationship,
the related foreign-key property is set to a null value. If the foreign-key does not
support null values, a new relationship must be defined, the foreign-key property
must be assigned another non-null value, or the unrelated object must be deleted.
So now that it is Nullable foreign key,
When I run sql profiling I get the following:
exec sp_executesql N'update [Schem].[ClassB]
set [ClassAID] = null
where ([Id] = #0)
',N'#0 uniqueidentifier',#0='092CE959-370A-4785-AF4A-93A0E4952C59'
It just enters a null in the relation instead of deleting the object.
What am I doing wrong?
Thanks.
objectA.list.Remove(objectA.list.First()); is removing the relationship, not the actual entity. If you want to delete the objectB's from the database then you have to remove them from the context like so:
foreach(var item in objectA.list.ToList())
context.ObjectBs.Remove(item);
I think it is a case of Cascade Delete.
I'm not sure but I think you can set the cascade delete (in your application AND in your database) to allow EF to make deletion on cascade.
Maybe look at some documentation to find how to do that. It seems there is a lot of related questions on SO.
Example
Example2
Example3