How to cascade delete entities with unidirectional 'ManyToOne' relationship with JPA - java

I have two entity classes 'User' and 'Department' with unidirectional 'ManyToOne' relationship as below.
public class User{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "DEPARTMENT_ID", nullable = true)
private Department department;
}
public class Department{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
If I want to delete some users and cascade remove the referenced departments if not any user references the department, is there any features of JPA to use please?

You can use CascadeType.DELETE, however this annotation only applies to the objects in the EntityManager, not the database. You want to be sure that ON DELETE CASCADE is added to the database constraint. To verify, you can configure JPA to generate a ddl file. Take a look at the ddl file, you'll notice that ON DELETE CASCADE is not part of the constraint. Add ON DELETE CASCADE to actual SQL in the ddl file, then update your database schema from the ddl. This will fix your problem .
This link shows how to use ON DELETE CASCADE on for CONSTRAINT in MySQL. You do this on the constraint. You can also do it in a CREATE TABLE or ALTER TABLE statement. It's likely that JPA creates the constraint in an ALTER TABLE statement. Simply add ON DELETE CASCADE to that statement.
Note that some JPA implementors do provide a means for this functionality.
Hibernate does supply this functionality using the #OnDelete annotation.

You can tell hibernate to delete 'orphan' entries with;
#Cascade({ org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })

Related

JPA Hibernate Lazy many-to-one fetch proxy

I'm using JPA 2.1 and Hibernate 4.3.7
I tried to tuned my app so I turn relationships to lazy and fetch only what I need them
I have a problem with the many-to-one relationships, when turn to lazy when I load the entity again Hibernate replace the entity by a proxy even if I fetch the entity and this proxy is not working in the view part (JSF) of the application.
The problem disapear when the many-to-one is in eager mode but hibernate perform one select more for each many-to-one even if I don't need them
#Entity
public class Department {
#Id
private Integer id;
//...
}
1/
#Entity
public class Employee {
#Id
private Integer id;
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "id_department", referencedColumnName = "id")
private Department department;
//...
}
the JPQL query:
SELECT e FROM Employee e LEFT JOIN FETCH e.department WHERE e.id=:id
=> one select query => faster but department is of type Department_$$_jvst3ac_5f (employee.getDepartment().getClass().getCanonicalName()) and this proxy doesn't work in the view part of the application
2/
#Entity
public class Employee {
#Id
private Integer id;
#ManyToOne(fetch = FetchType.EAGER, optional = true)
#JoinColumn(name = "id_department", referencedColumnName = "id")
private Department department;
//...
}
the JPQL query:
SELECT e FROM Employee e WHERE e.id=:id
=> two selects => slower but department is loaded as Department and everything goes fine in the view part of the application
The relation is unidirectional, Department have no references of emplyees
Is this possible to have the department without proxy when using FETCH JOIN?
After the response of Luiggi I will precised that the data are fetched with lazy many-to-one + fetch join. When I do a employee.getDepartment().toString() I have Department{ id=11, ...} but the class of this department is still Department_$$_jvst3ac_5f. For reason I don't know, the JSF/PrimeFaces selectOneMenu component don't work properly whith HibernateProxy even if the data are fetched
I tried to use the Hibernate annotation #LazyToOne(LazyToOneOption.FALSE) in addition of #ManyToOne(fetch = FetchType.LAZY) but the result is similar of #ManyToOne(fetch = FetchType.EAGER) alone...
The problem is that when you use lazy loading you will obtain a proxy of the class (as you already stated) and this proxy can fetch the data from database only if the hibernatesession is still open. Seems like your session is being closed when returning the data to the view, so when trying to use the lazily-loaded field in the view you're getting the exception.
Possible solutions:
Keep the field as fetch eager and pay the overhead for each query against your entity (probably this isn't good and can affect performance, but is a solution).
Maintain your field as lazy and use the proper get method before the Hibernate session is closed in order to the proxy to retrieve the relevant data to be used after the session is closed.

Storing data using hibernate for inter related tables

I have two tables (say table A and table B). Table B has foreign key from table A primary key. I generated my java entities using netbeans IDE and i now have something like:
For table A:
#Entity
#Table(name = "WORKFLOW_TRANSACTION")
public class WorkflowTransaction implements {
#OneToMany(mappedBy = "wtId")
private Collection<WorkflowTask> workflowTaskCollection;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "APP_ID")
private BigDecimal appId;
.
.
.
For table B:
#Entity
#Table(name = "WORKFLOW_TASK")
public class WorkflowTask implements Serializable {
#JoinColumn(name = "WT_ID", referencedColumnName = "APP_ID")
#ManyToOne
private WorkflowTransaction wtId;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "TASK_ID")
private BigDecimal taskId;
#Column(name = "STEP_NUM")
private BigInteger stepNum;
.
.
.
Now my questions are:
What is the correct way to save data, should I create object for table B entity and set it in table A entity and then save table A?
I am generating entity beans using netbeans IDE feature. Are there any known disadvantages of it?, if yes, what?
You have bi-directional association, so you need to setTable B property in Table A entity class and also vice-versa. Since you have not declared cascading, you need to save the Table A entity first and then Table B entity.
Alternatively, if you save Table B and then Table A entity classes, the hibernate generates an extra SQL update command to maintain the relationship.
But if you want hibernate to save Table B entity when you save Table A entity then you need to add Cascade property:
#OneToMany(mappedBy = "wtId", cascade=CascadeType.ALL)
I don't think you will have any disadvantages if you use Netbeans for generating the entity classes, it save you time in writing the entities. But if you want to learn then writing entities without Netbeans is good.
To achieve this you can use two type of techniques XML mapping or Annotations
In both these techniques the common point is use
cascade="save-update"
What happens is that you set an attribute in one table getter and it will automatically insert into the many relation when inserting into one relation table

How to realize ON UPDATE CASCADE and ON DELETE CASCADE in Hibernate(JPA)?

I have two tables: Accounts and Mails.
1 user from Accounts can have some mails (1 : M ratationship).
When user is deleting his profile (from Accounts), hibernate should delete all mails for that user (from Mails). Accounts has id (PK) and other columns. Mails has id(PK), user_id(FK: user_id -> Accounts.id) and other columns.
How to achieve above kind of implementation on Hibernate (or JPA)?
My entity classes are as follows:
//Accounts class
#OneToMany(mappedBy = "accounts", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Mails> mails;
//Mails class
#ManyToOne
#JoinColumn(name="user_id" , referencedColumnName="id", insertable=false, updatable=false)
private Accounts accounts;
But when I'm editing or deleting some parent rows, hibernate says: Cannot delete or update a parent row: a foreign key constraint fails. So exactly where am I making ​​a mistake?
UPDATE
This error occurs when we execute SQL or HQL queries and it's not true. We should use entitymanager.remove or entitymanager.merge methods. My entity classes are valid.
it seems you have a bi-directional relation between account and mails.
In the account class you have to use somethink like:
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "accounts")
private Set<Mails> mails;
Property mappedby is required for bi-directional.
Hint: In my opinion you should only use bi-directional relations if it is really necessary.

Hibernate: Issue with OnDelete Annotation

Hello
I have the following two entities
#Entity
public class DocumentCollection {
#Id
#GeneratedValue
private Long id;
#OneToMany(targetEntity=Document.class,mappedBy="documentCollection",cascade=javax.persistence.CascadeType.ALL)
#OnDelete(action = OnDeleteAction.CASCADE)
#Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<Document> documents;
...
}
And:
#Entity
public class Document {
#Id
#GeneratedValue
private Long id;
#ManyToOne
private DocumentCollection documentCollection;
...
}
When a DocumentCollection gets deleted, all referenced documents should be deleted too.
But I'm getting this error:
Cannot delete or update a parent row: a foreign key constraint fails (`tms_db`.`document`, CONSTRAINT `FK3737353BEB85533C` FOREIGN KEY (`documentCollection_id`) REFERENCES `documentcollection` (`id`))
I also tried putting the #OnDelete Annotation in the document class but didn't work.
So what am I missing? For your information, I'am using Hibernate 3.6.0.Final.
UPDATE: I did a mysql schema dump and noticed that there is no ON DELETE CASCADE statement in the document table schema. Hibernate only generates the foreign key constraints, which leads to the mentioned error. Has anybody an idea why hibernate does NOT generate the "ON DELETE CASCADE" statement?
Hope, somebody can help me
thx
#OnDelete annotation affects the way Hibernate generates database schema. If your schema is not generated by Hibernate, or haven't been updated after adding this annotation, it wouldn't work correctly.
If you want to use manually created database schema with #OnDelete(action = OnDeleteAction.CASCADE), you should manually define foreign key constraint in your schema as on delete cascade.

How to do bulk delete in JPA when using Element Collections?

I am having trouble working out how to do a bulk delete of a Person object using JPA, when the Person objects contain data stored using an #ElementCollection. Any ideas on how to do this would be much appreciated.
#Entity
#Table(name="at_person")
public class Person implements Comparable<Person> {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
private long id = 0;
#Column(name="name", nullable = true, length = 128)
private String name = "";
#ElementCollection
#Column(name = "email")
#CollectionTable(name = "person_email", joinColumns = #JoinColumn(name = "person_id"))
private Set<String> email = new HashSet<String>();
}
What I am doing at the moment is this, and it fails with a foreign key constraint error:
Query query=em.createQuery("DELETE FROM Person");
Caused by: java.sql.SQLException: integrity constraint violation:
foreign key no action; FKCEC6E942485388AB table: PERSON_EMAIL
If it can be a pure JPA annotation rather than a Hibernate annotation that would be a bonus!
I'll let you interpret the part of the JPA 2.0 specification that mentions that a bulk delete operation is not cascaded:
4.10 Bulk Update and Delete Operations
...
A delete operation only applies to
entities of the specified class and
its subclasses. It does not cascade to
related entities.
And the fact is that Hibernate won't cascade a delete to a collection table either. This has been reported in HHH-5529 and the suggested approaches are:
You could also (a) clean up the collection table yourself or (b) use cascading foreign keys in the schema.
In other words, (a) use native SQL or (b) use a cascade delete constraint at the database level - and you'll have to add it manually, I don't think you can use #OnDelete with the #ElementCollection annotation (same story as HHH-4301 IMO).

Categories

Resources