How to remove entities from many-to-many hibernate? - java

I am new to Hibernate and now trying to understand cascade types. I know there mey be a lot of similiar questions, but let's say I have a many-to-many relationship between two tables: state and capital , which creates state_capital which stores two foreign keys of capital and state. If I were to remove a state entity or capital entity, it should remove only either state entity from state table or capital entity from capital table plus the corresponding records in state_capital. Currently If I try to remove a capital entity, it automatically removes the linked state and vice versa. Here's the relationship:
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
#JoinTable(name = "state_capital",
joinColumns = {
#JoinColumn(name = "id_state", referencedColumnName = "id_state",
nullable = false, updatable = false)},
inverseJoinColumns = {
#JoinColumn(name = "id_capital", referencedColumnName = "id_capital",
nullable = false, updatable = false)})
private Set<Capital> capitals = new HashSet<>();
#ManyToMany(mappedBy = "capitals", fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private Set<State> states = new HashSet<>();
What should I do?

Related

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.

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.

ORA-02292 when using Hibernate

I am trying to implement a delete function but all I get is this ORA-02292 ERROR:
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-02292: Integritäts-Constraint (VDMA.FK892DE8B473F40868) verletzt - untergeordneter Datensatz gefunden
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:447)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:396)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:951)
I have an Entity (MainEntity) that has an n:m relationship with its child entity.
For example: A User can have multiple cars, each car can be driven by different users.
When a User is deleted, I want the associations between the User and the Car to be deleted as well. That´s why I thought I could do the following :
User Entity
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "user_car", joinColumns = {#JoinColumn(name = DATABASE_COLUMN_ID, nullable = false, updatable = false) }, inverseJoinColumns = {#JoinColumn(name = DATABASE_COLUMN_TYPE_ID,
nullable = true, updatable = false) })
private Set<UserCar> userCars;
Car Entity
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "userCars")
private Set<User> users;
The result is :
The tables get created, the data gets persisted - everything works fine.
EXCEPT deleting entries: As soon as I try to delete a User and the User has a car (and therefore a user : car relationship) I do get the errors shown above.
If the user doesn´t have any cars I can delete him without any issues. So the problem must be the constraint in the USER_CAR table.
Clear userCars as well:
user.getUserCars().clear()
This will break the association between user and associated cars (it will delete the corresponding records from the junction table).
Also, you don't want CascadeType.ALL on many-to-many associations, because it implicitly contains CascadeType.REMOVE. Using it means that removal will be cascaded to cars as well, although there are other users who are associated with those cars.
Pls make sure you delete the child records, adding the orphanRemoval will help
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL,orphanRemoval=true)
#JoinTable(name = "user_car", joinColumns = {#JoinColumn(name = DATABASE_COLUMN_ID, nullable = false, updatable = false) }, inverseJoinColumns = {#JoinColumn(name = DATABASE_COLUMN_TYPE_ID,
nullable = true, updatable = false) })
private Set userCars;
This will delete all cars assoicated with a user.

ManyToMany Hibernate annotation

I have this relationship in my database:
And I want to be able to create an order, that contains more products (as well more instances of the same product). Nevertheless I am able to have just one row in the order_has_product table for every single order_id. Moreover, I tried to load the data via lazyloading, but it won't work and it makes the whole app to run really slowly. Here are definitions of the relationship in my entities:
OrderEntity:
#ManyToMany(fetch = FetchType.LAZY)
#Cascade({org.hibernate.annotations.CascadeType.ALL})
#JoinTable(name = "order_has_product",
joinColumns = {#JoinColumn(name = "order_id", nullable = false, updatable = false)},
inverseJoinColumns = {#JoinColumn(name = "product_id", nullable = false, updatable = false)})
private List<ProductEntity> products;
ProductEntity:
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "products")
private List<OrderEntity> orders;
Any idea how to fix lazyloading and to make it possible to have more products in order?

ManyToMany remove problem

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.

Categories