ManyToMany remove problem - java

I have a table: DocumentType:
#ManyToMany(cascade = {CascadeType.REFRESH, CascadeType.MERGE, CascadeType.PERSIST}, fetch = FetchType.LAZY)
#JoinTable(
name = "document_type_property_type",
joinColumns = #JoinColumn(name = "document_type"),
inverseJoinColumns = #JoinColumn(name = "property_type")
)
#Cascade({org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
#ForeignKey(name = "FK_DOCUMENT_TYPE_PROPERTY_TYPE__DOCUMENT_TYPE", inverseName = "FK_DOCUMENT_TYPE_PROPERTY_TYPE__PROPERTY_TYPE")
#Sort(type = SortType.NATURAL)
private SortedSet<PropertyType> propertyTypes = new TreeSet<PropertyType>();
and PropertyType:
#ManyToMany(cascade = {CascadeType.REFRESH, CascadeType.MERGE, CascadeType.PERSIST}, fetch = FetchType.LAZY)
#JoinTable(
name = "document_type_property_type",
joinColumns = #JoinColumn(name = "property_type"),
inverseJoinColumns = #JoinColumn(name = "document_type")
)
#Cascade({org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
#Sort(type = SortType.NATURAL)
protected SortedSet<DocumentType> documentTypes = new TreeSet<DocumentType>();
As you see the bridge table for ManyToMany is: document_type_property_type.
I do not understand why if i remove a property type from a doc type it not only deletes it from bridge table (as i want/expect) but also deletes it from property type itself (which i want to avoid!).
Can you give me a work-around?
Thanks.
Edit: code for deleting a property type - doc type relation:
public void removePropertyType(final PropertyType propertyType) {
super.performDAudit(propertyType);
final DocumentType currentInstance = getInstance();
currentInstance.getPropertyTypes().remove(propertyType);
getEntityManager().persist(propertyType);
FacesMessages.instance().add(StatusMessage.Severity.INFO, "Property Type was succesfully removed from this document type");
}

I notice that you have the cascade type set to DELETE_ORPHAN on both sides of the relationship. I think you may either have to set it on one side or none. I am not sure that DELETE_ORPHAN is relevant in your scenario.
As I understand it, only one side of the relationship actually "owns" the relationship. That is the side that should manage all cascades and so on and the inverse side should do nothing.

Related

Hibernate ManyToMany Method threw 'org.hibernate.LazyInitializationException' exception

I have ManyToMany relationships in Hibernate
Project.class
#ManyToMany
#JoinTable(
name = "user_projects",
joinColumns = { #JoinColumn(name = "project_id")},
inverseJoinColumns = { #JoinColumn(name = "user_id")}
)
private Set<User> projectUsers = new HashSet<>();
User.class
#ManyToMany(fetch = FetchType.EAGER,
mappedBy="projectUsers",
cascade = CascadeType.ALL)
private Set<Project> userProjects = new HashSet<>();
And when I get current user thru
#AuthenticationPrincipal User user
I have Method threw 'org.hibernate.LazyInitializationException' exception when user.getUserProjects();
It's because when using #ManyToMany, first all Projects of user are fetched, then all Users of fetched Projects and so on... This cyclic dependency causes the error, because there is eager fetching missing on Project side.
The solution would be to use:
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "user_projects",
joinColumns = { #JoinColumn(name = "project_id")},
inverseJoinColumns = { #JoinColumn(name = "user_id")}
)
private Set<User> projectUsers = new HashSet<>();
Although I do not recommend this solution, because of performance issues (and possible side effects). Better solution would be to have an entity for join table.
Try to use #Transactional maybe can solved it because
the #Transactional annotation on the getFavorites() method indicates that the session will be closed at the end of this method

How to specify foreign key with the join column?

My code is as below. I am using the spring boot with jpa and postgresql database
I need user friendly name as foreign key.
#Entity
#Table(name="course_table")
public class Course extends BaseAuditingEntity {
#ManyToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER)
#JoinTable(name = "course_program_table", joinColumns = #JoinColumn(name = "course_id", referencedColumnName = "course_id", foreignKey = #ForeignKey(name = "fk_program_id")), inverseJoinColumns = #JoinColumn(name = "program_id", referencedColumnName = "program_id", foreignKey = #ForeignKey(name = "fk_course_id")))
private List programs;
}
I have given the name of foreign key using the #ForeignKey annotation but when I see db it is showing the randomly created foreignkey name.
CREATE TABLE course_program_table
(
course_id integer NOT NULL,
program_id integer NOT NULL,
CONSTRAINT fk_28c95hl4nqclyvyxuduei5nbf FOREIGN KEY (program_id)
REFERENCES public.program_table (program_id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION,
CONSTRAINT fk_5sainywquv8yyu24pjk3jptn7 FOREIGN KEY (course_id)
REFERENCES public.course_table (course_id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
I need foreign key as mentioned in the annotation like fk_program_id and fk_course_id.
Thanks in advance.
With a join table this is how you should specify it
#ManyToMany
#JoinTable(name = "course_program_table",
joinColumns = #JoinColumn(name = "course_id", ...)
foreignKey = #ForeignKey(name = "fk_program_id"),
inverseJoinColumns = #JoinColumn(name = "program_id", ...)
inverseForeignKey = #ForeignKey(name = "fk_course_id"))
private List programs;
This is how I do it with the JPA provider I use (not Hibernate), and that is why the #JoinTable has the "foreignKey"/"inverseForeignKey" attributes (the FKs are on/owned by the join table).
If that doesn't work then you need to be looking at raising a bug on your chosen JPA provider.
#ManyToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER)
#JoinTable(name = "tten_courseservice_course_program_table", joinColumns = #JoinColumn(name = "course_id", referencedColumnName = "course_id"), inverseJoinColumns = #JoinColumn(name = "program_id", referencedColumnName = "program_id"))
#ForeignKey(name="fk_tten_courseservice_course_table_course_id",inverseName="fk_tten_courseservice_program_table_program_id")
private List<ProgramEntity> programs;``
I have tried this and now I am able to generate foreign key name properly.
Hope it will help other.

What is the purpose of CascadeType.MERGE in Many to Many relationships?

Suppose we have a Post class with a tags reference that is a List:like this (Original source):
#ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "post_tag",
joinColumns = #JoinColumn(name = "post_id"),
inverseJoinColumns = #JoinColumn(name = "tag_id")
)
private List<Tag> tags = new ArrayList<>();
Why do we need CascadeType.MERGE? Is this in the event that, for example, we remove tags from the list and then we persist the post, thus we want the result to "Merge" (Unmap the tags from the post in the many to many join table) with the current database state?

JPA Map mapping to join table with 3 columns

I have list that is mapped by a join table. What I need to do is make the combinations of "layouts" and 'views' not unique and also each with an index. What I thought of trying to do is making a Map<Integer, View> and somehow make the join table have a third column 'id'. What would happen is get the views of that layout and populate them with the id from the join table as a key in the map.
Any idea how to do that, or maybe a better idea for what I need?
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(
name = "layout_view",
joinColumns = {#JoinColumn(name = "layout_id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "view_id", nullable = false)}
)
private List<View> views;
What I imagine:
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(
name = "layout_view",
joinColumns = {#JoinColumn(name = "layout_id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "view_id", nullable = false)}
//some code for third column and populating it as keys
)
private Map<Integer, View> views;
You can already have multiple same views for a layout by using a List, as you're doing.
To preserve (and be able to change) the order, see OrderColumn and the Hibernate documentation about ordered lists​.
The same documentation also describes how to implement your original idea with a map, if you really want that.

Create one to one relationship with join table in hibernate

I have a many to one relationship from the class client to estimate.
#OneToMany( mappedBy = "fkIdClientEstimate", cascade = CascadeType.ALL)
private List<Estimate> estimateList = new ArrayList<>();
But is it possible to create a one to one relationship from estimate to client. I tried something like this but it does not enter any values into the database ?
#OneToOne( cascade = CascadeType.ALL)
#JoinTable(
name = "tbl_estimate_client",
joinColumns = #JoinColumn( name="id_estimate"),
inverseJoinColumns = #JoinColumn( name="id_client"))
private Client client ;
I also tried it with private Client client = new clientBut it creates a new client into the database instead.

Categories