Can't quite understand how to delete fields properly with Hibernate - java

I have many-to-many relation in my database (entities are Participant and Event)
//part of participant model
#ManyToMany(fetch = FetchType.LAZY , cascade = { CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(name = "participant_event",
joinColumns = {#JoinColumn(name = "participant_id")},
inverseJoinColumns = {#JoinColumn(name = "event_id")})
//part of event model
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "events", cascade = CascadeType.ALL)
Right at this moment, deleting an Event causes deleting all the Participants who are to visit this event. Removing CascadeType.ALL in event model causes no result after deleting Event (by deleting I mean .remove). What is the proper way to delete Event only and keep all the participants?
Code is here

In a many-to-many association, CascadeType.REMOVE is not desirable, since it deletes the actual entities (not just their associated link table entries).
The proper way to delete associations is to dissociate the entities:
public void removeEvent(Event event) {
events.remove(event);
event.setParticipant(null);
}
To delete an Event, you'd have to remove from all the Participants. For that you have to do something like this:
Event deletableEvent = ...;
for(Iterator<Participant> participantIterator = event.getParticipnts(); participantIterator.hasNext();) {
Participant participant = participantIterator.next();
participant.getEvents().remove(deletableEvent);
participantIterator.remove();
}
entityManager.flush();
entityManager.remove(deletableEvent);

Related

How can i save related entity in Database using dual Many-To-Many relationship?

I have two entities:
User
Work
they have many-to-many relationship with each-over.
#ManyToMany(
fetch = FetchType.LAZY,
cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
#JoinTable(
name = "user_work",
joinColumns = #JoinColumn(name = "work_id"),
inverseJoinColumns = #JoinColumn(name = "user_id"))
private List<User> workUsers;
when i add new work using JPA :
List<UserDto> users = new ArrayList<>();
users.add(new User(something something));
WorkDto work1 = new WorkDto(1, users));
workRepository.save(workMapper.fromDto(work1));
problem : when i save my work entity in the database it does not save user, so when i extract it , work says that is has no users.
How can i insert work into database and add users to it as well? I have mutual table with work_id and user_id of course
Are you executing it within a transaction with #Transactional? Have you #Override the equals method?
More detail is needed like the relationship in the User class

How to use the #ManyToMany with two lists on the same table

I have a situation where an entity could use another entity, and it could be used by another, so i have defined a ManyToMany relation that reference the same entity, so i could have listUse and listUsedBy, and both are persisted in the same table entity_usage :
#ManyToMany
#JoinTable(name = "entity_usage",
joinColumns = {
#JoinColumn(name = "id_use", referencedColumnName = "id")},
inverseJoinColumns = {
#JoinColumn(name = "id_used_by", referencedColumnName = "id")})
private List<Entity> listUse;
#ManyToMany
#JoinTable(name = "entity_usage",
joinColumns = {
#JoinColumn(name = "id_use_by", referencedColumnName = "id")},
inverseJoinColumns = {
#JoinColumn(name = "id_use", referencedColumnName = "id")})
private List<Entity> listUsedBy;
Exemple : Entity A could use Entity B and C, so Entity B and C are used by A.
Now my problem is when i add B and C to listUse, they are persisted in entity_usage, but when try to display listUsedBy i have to redeploy my project, otherwise listUsedBy remains empty, is there a way to refresh listUsedBy when persist my entity without having to redeploy my project.
This is the general approach:
#Entity
public class SomeEntity
{
#ManyToMany
#JoinTable(name = "entity_usage",
joinColumns = #JoinColumn(name = "using_id"),
inverseJoinColumns = #JoinColumn(name = "used_by_id"))
private Set<SomeEntity> using = new LinkedHashSet<>();
#ManyToMany(mappedBy = "using")
private Set<SomeEntity> usedBy = new LinkedHashSet<>();
public void addUsing(SomeEntity entity)
{
this.using.add(entity);
entity.usedBy.add(this);
}
public void addUsedBy(SomeEntity entity)
{
this.usedBy.add(entity);
entity.using.add(this);
}
}
and it's used:
public void someMethod(long parentEntityId, long childEntityId)
{
EntityManager em = getSomeEntityManager();
SomeEntity parentEntity = em.find(SomeEntity.class, parentEntityId);
SomeEntity childEntity = em.find(SomeEntity.class, childEntityId);
parentEntity.addUsing(childEntity);
}
typically this is a transactional EJB method.
Note that there's no need to em.merge anything, since entities are already managed by em.find.
Anyway, whichever method you'll use to manage your entities (query, find, persist, merge), remember that's important to call addUsing/addUsedBy only when both entities are managed.
This is one of the main incoherences that ORM logic cannot handle by its own: you have to inform both entities (parent and child) of their relation. It's not sufficient to set the relation only on one side - if you only say that A is parent of B, B still doesn't know who is its parent.
However, there exists alternative approaches, like setting only the owning side of the relation (parent.getChildren().add(child)), flush, and refresh the child.
Nevertheless (as I experienced very well on my skin) the alternatives are very hard to handle in real world complex applications.
As a side note, I'd use Set instead of List for the relation, unless you need some kind of insertion-order.

How do I leave orphaned rows in a #JoinTable for a #ManyToMany relationship in Hibernate?

I have the following database schema
item
ID
batch
ID
batch_queue
ID
batch_item
FK_BatchID
FK_ItemID
and both entity classes for batch and batch_queue have the following field
#ManyToMany
#JoinTable(name = "batch_item",
joinColumns = { #JoinColumn(name = "FK_BatchID") },
inverseJoinColumns = { #JoinColumn(name = "FK_ItemID") })
private List<Item> items;
When I delete a BatchQueue entity the rows in batch_item are considered orphaned and they get deleted too. I would like these rows to not be deleted so they can be used by Batch entities.
(Note: batch_queue is essentially a subset of the batch table)

Hibernate removes relation on update

I want to define my #ManyToMany relationship with JPA Annotations so that relations are not removed when updating entity.
#ManyToMany(targetEntity=Event.class, cascade={CascadeType.ALL}, fetch = FetchType.LAZY)
#JoinTable(
name = "event_user",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "event_id")
)
private Set<Event> events;
and Event class
#ManyToMany(cascade = {CascadeType.ALL}, mappedBy="events", targetEntity=User.class, fetch = FetchType.LAZY)
private Set<User> attending;
I thought setting CascadeType.REMOVE would not trigger deletion when updating but when I call update on a user object, all its related events are removed.
This is from my DAO
#Override
public User update(User entity) {
sessionFactory.getCurrentSession().update(entity);
return entity;
}
When I call update on my entity, Hibernate does:
Hibernate: delete from event_user where user_id=?
The comments on your questions are correct so far. You obviously do not load the entity from the database before updating it. Hence, hibernate updates everything just as it finds it in your entity. So, load the entity (by id?), merge your changes and update it afterwards.
Btw you should also consider using the delete orphans annotation. You would hence make sure that events to a user would also get deleted when setting the event collection to null and not only when removing the entire user.

JPA/Hibernate: ManyToMany delete relation

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.

Categories

Resources