I have a Person object. Person has a manager property which is again of type Person.
#OneToOne(optional = true, cascade = { CascadeType.ALL })
private Person manager;
Say John is the manager and Bob is the employee. When I am trying to delete John, it fails since Bob becomes orphan (without Manager). That should be allowed in my use case. But marking this relationship "optional" doesn't help. And Cascade doesn't seem to have any significance here.
I presume this is possible with JPA. Any help?
#Entity
public class Person {
#Id
private String id;
private String name;
private Integer age;
private Address address;
#JoinColumn(name = "manager_id", nullable = true, insertable = true, updatable = true)
#OneToOne(optional = true, cascade = { CascadeType.ALL })
private Person manager;
#OneToMany(mappedBy = "manager", cascade = { CascadeType.ALL })
public Collection<Person> reportees;
You should set null as Bob's manager, before trying to delete John. Also, #Andrei is right, you should map this as bidirectional #ManyToOne relation (although your code will work if you know all the persons that have John as their manager).
I doubt the relationship is #OneToOne, as there are many employees that have the same manager.
If you insist that it is #OneToOne, then make the relationship bidirectional, by adding the property in the Person Java class:
#OneToOne(mappedBy="manager", cascade = { CascadeType.ALL })
private Person employee;
I deleted the parent entity before deleting children. Not the ideal solution but works for me.
I also tried the bidirectional relationship (by adding #ManyToOne reportees property of type Person) and marked the relationship "optional" using #JoinColumn also marked nullable=true. But nothing worked.
Related
I have an one-to-many collection annotated as following
#Entity
#Table(name = "students")
public class Student {
#OneToMany(mappedBy = "student", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
#OrderColumn(name ="index")
private List<Preference> preferences;
}
Preference entity
#Entity
#Table(name = "preferences")
public class Preference {
// id
#ManyToOne
#JoinColumn(name = "student_id")
private Student student;
#ManyToOne
#JoinColumn(name = "project_id")
private Project project;
private Integer index;
}
I try to remove an element from the list like this:
public void removePreference(Preference preference) {
preferences.remove(preference);
preference.setStudent(null);
}
The above code is not working, the entity is not removed from the table. How can i achieve this?
Assuming that the Project to Preference relationship is defined the same way that Student to Preference then your problem comes from the fact that your Preference entity is the child of two others entity.
When you remove it from the student's preference it is marked as an removed entity by Hibernate but as it is still referenced by the project Hibernate will re-manage it, as it is specified in the JPA specification for this use case. Check section 3.2.2 and 3.2.3 about persisting and removing.
To solve your case you can delete it from all sides or check if a #ManyToMany relationship between Student and Project with Preference as join table wouldn't suit better.
Posting this here as I wasn't seeing much interest here: http://www.java-forums.org/jpa/96175-openjpa-one-many-within-one-many-merge-problems.html
Trying to figure out if this is a problem with OpenJPA or something I may be doing wrong...
I'm facing a problem when trying to use OpenJPA to update an Entity that contains a One to Many relationship to another Entity, that has a One to Many relationship to another. Here's a quick example of what I'm talking about:
#Entity
#Table(name = "school")
public class School {
#Column(name = "id")
protected Long id;
#Column(name = "name")
protected String name;
#OneToMany(mappedBy = "school", orphanRemoval = true, cascade = CascadeType.ALL)
protected Collection<ClassRoom> classRooms;
}
#Entity
#Table(name = "classroom")
public class ClassRoom {
#Column(name = "id")
protected Long id;
#Column(name = "room_number")
protected String roomNumber;
#ManyToOne
#JoinColumn(name = "school_id")
protected School school;
#OneToMany(mappedBy = "classRoom", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
protected Collection<Desk> desks;
}
#Entity
#Table(name = "desk")
public class Desk {
#Column(name = "id")
protected Long id;
#ManyToOne
#JoinColumn(name = "classroom_id")
protected ClassRoom classRoom;
}
In the SchoolService class, I have the following update method:
#Transactional
public void update(School school) {
em.merge(school);
}
I'm trying to remove a Class Room from the School. I remove it from the classRooms collection and call update. I'm noticing if the Class Room has no desks, there are no issues. But if the Class Room has desks, it throws a constraint error as it seems to try to delete the Class Room first, then the Desks. (There is a foreign key constraint for the classroom_id column)
Am I going about this the wrong way? Is there some setting I'm missing to get it to delete the interior "Desk" instances first before deleting the Class Room instance that was removed?
Any help would be appreciated. If you need any more info, please just let me know.
Thanks,
There are various bug reports around FK violations in OpenJPA when cascading remove operations to child entities:
The OpenJPA FAQ notes that the following:
http://openjpa.apache.org/faq.html#reorder
Can OpenJPA reorder SQL statements to satisfy database foreign key
constraints?
Yes. OpenJPA can reorder and/or batch the SQL statements using
different configurable strategies. The default strategy is capable of
reordering the SQL statements to satisfy foreign key constraints.
However ,you must tell OpenJPA to read the existing foreign key
information from the database schema:
It would seem you can force the correct ordering of the statements by either setting the following property in your OpenJPA config
<property name="openjpa.jdbc.SchemaFactory"> value="native(ForeignKeys=true)"/>
or by adding the org.apache.openjpa.persistence.jdbc.ForeignKey annotation to the mapping:
#OneToMany(mappedBy = "classRoom", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#org.apache.openjpa.persistence.jdbc.ForeignKey
protected Collection<Desk> desks;
See also:
https://issues.apache.org/jira/browse/OPENJPA-1936
Here is my scenario: i have person entity which looks like below.
#Entity
public class Person{
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<PhoneNumber> phoneNumbers = new HashSet<>(0);
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "AGENCY_ID")
private Agency agency;
I am unable to retrieve correct data,when i query for persons.
Problems i have :
1. duplicate records.
2. person with no agency not returning .
3. Bad performance
Here is what i tried, and see combination of above problems
query.from(qPerson).leftJoin(qPerson.phoneNumbers, telecommNumber).leftJoin(qPerson.agency,qAgency);
I have problem 1: which is obvious(in one-to-many relationship) and this can be solved in direct hibernate by using distinct(). I tried distinct in queryDsl and that doesnt seem to work well.
query.from(qPerson).leftJoin(qPerson.phoneNumbers, telecommNumber).fetch().leftJoin(qPerson.agency,qAgency).fetch();
I have problem 3 in this case: returns results correctly but performance is really bad.(Cartesian product problem, i guess).
query.from(qPerson).fetchAll();
I have problem 2 in this case :This one performs well, but doesnt return person without agency when i try to sort on agency field for example. But returns that person if i dont add below to the query.
query.orderBy(person.agency.agencyIdentifierDescription.asc());
I am trying to arrive at a solution that solves above three problems. Thanks for your help.
Well, you should define your entities as following:
"In JPA a ManyToOne relationship is always (well almost always) required to define a OneToMany relationship, the ManyToOne always defines the foreign key (JoinColumn) and the OneToMany must use a mappedBy to define its inverse ManyToOne."
from Wiki:
ManyToOne
OneToMany
example:
public class Person {
#ID
private Integer id;
#OneToMany(mappedBy = "person")
private Set<PhoneNumber> = phoneNumbers;
#ManyToOne
#JoinTable(name="agency_person", joinColumns={#JoinColumn(name="person_id", referencedColumnName="id")}, inverseJoinColumns={#JoinColumn(name="agency_id", referencedColumnName="id")})
private Agency agency;
//Getters & Setters
}
//---------------------------------------------------
public class PhoneNumber {
#ID
private Integer id;
#ManyToOne
#JoinTable(name="phonenumber_person", joinColumns={#JoinColumn(name="phone_id", referencedColumnName="id")}, inverseJoinColumns={#JoinColumn(name="person_id", referencedColumnName="id")})
private Person person;
//Getters & Setters
}
//---------------------------------------------------
public class Agency {
#ID
private Integer id;
#OneToMany(mappedBy = "agency")
private Set<Person> persons;
//Getters & Setters
}
I am trying to get the following type of mapping to work
Table event has the following columns:
id (PK)
prodgroup
errandtype
table errandtype : errandtype
table prodgroup: prodgroup
I have corresponding JPA classes
#Entity
#Table(name="event")
public class MyEvent {
#Id
int id;
// what mapping should go here?
Prodgroup prodgroup;
// what mapping should go here?
ErrandType errandtype;
}
#Entity
public class Prodgroup {
#Id
private String prodgroup;
}
#Entity
public class ErrandType {
#Id
private String errandtype;
}
Ok so questions are marked as comments in the code but I'll try to be explicit anyway.
In the above example I want my Prodgroup and ErrandType fields in the MyEvent class to be set to corresponding Prodgroup and Errandtype instances
I have tried #OneToOne relationships with #joincolumns and with mappedby attribute, but I just can't get it working and I've lost all sense of logical approach. My grasp of JPA entity mapping is clearly weak.
So can anyone bring some clarity?
It should be:
#Entity
#Table(name="event")
public class MyEvent {
#Id
int id;
// what mapping should go here?
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "prodgroup_id", insertable = true, updatable = true)
Prodgroup prodgroup;
// what mapping should go here?
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "errandtype_id", insertable = true, updatable = true)
ErrandType errandtype;
}
#Entity
public class Prodgroup {
#Id
private String prodgroup;
}
#Entity
public class ErrandType {
#Id
private String errandtype;
}
FetchType Eager means the object will be always loaded (would be "Lazy" by default if not specified).
CascadeType.ALL means mearge/persist/remove will be also done to linked tables.
Sebastian
Your table columns event.prodgroup and event.errandtype are foreign keys to respective tables (prodgroup, errandtype). So you need #ManyToOne association (because many events may share one prodgroup or errantype).
I have two classes, say Group and Person with a ManyToMany-Relation that is mapped in a JoinTable.
If I delete a Person that has a relation to a Group, I want to delete the entry from the join table (not delete the group itself!).
How do I have to define the cascade-Annotations? I didn't found a really helpful documentation but several unsolved board discussions...
public class Group {
#ManyToMany(
cascade = { javax.persistence.CascadeType.? },
fetch = FetchType.EAGER)
#Cascade({CascadeType.?})
#JoinTable(name = "PERSON_GROUP",
joinColumns = { #JoinColumn(name = "GROUP_ID") },
inverseJoinColumns = { #JoinColumn(name = "PERSON_ID") })
private List<Person> persons;
}
public class Person {
#ManyToMany(
cascade = { javax.persistence.CascadeType.? },
fetch = FetchType.EAGER,
mappedBy = "persons",
targetEntity = Group.class)
#Cascade({CascadeType.?})
private List<Group> group;
}
Cascade will not clean up the leftover references to the deleted Person that remain on the Group object in memory. You have to do that manually. It seems like cascade should do this, but sadly that's not the way it works.
Based on the info provided in your question, I don't think you need any cascade options set on your Person or Group entities. It doesn't sound like they share a parent/child relationship where the existence of one depends upon the other. That's the kind of relationship where I would expect to see some cascade options.
I believe what you want is:
cascade = CascadeType.ALL
To remove the DB relation, remove the association from each group. Remove the person from the Group.persons collection and remove the Group from the Person.group collection, then persist your person object.
You can probably do it on a database specifically (depends on your database and it capabilities). By adding "on delete cascade" to the foreign key of the relationship table.