#Entity
public class EUser {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#ManyToMany(fetch=FetchType.EAGER)
private List<UserRole> roles;
}
when doing the following action
EUser approveUser = (EUser) userService.getOne(2);
approveUser.getRoles().clear();
userService.update(approveUser);
System.out.println(approveUser.getRoles().size());
it says the size is zero but when i go the db in the EUser_UserRole table i see the value still present. How to solve this??
also in the EUser_UserRole it says
This table does not contain a unique column. Grid edit, checkbox, Edit, Copy and Delete features are not available
how can i delete add edit delete manually??
Cascading is indeed a way to let Hibernate do the removal and if I see the posted code, that is most likely what is asked for. But since the question is about manually deleting while cascading is more automatic deletion, I have to add the suggestions to:
use EntityManager.remove()
invoke a JPQL delete query
Which more fit more the description of "manual" deletion.
se CascadeType. Reference
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<UserRole> roles;
Update :
orphanRemoval attribute can use JPA 2.x version. You have to find out the deleted UserRole data by comparing old rolesList and new rolesList`
orphanRemoval attribute does not support in ManyToMany mapping.
Do not use a cascade for ManyToMany relationships. This can result in an undesired rippling deletion over a wide entity cluster easier than one might hope.
If you want to clear the relationship (delete rows from the join table) for a single user to their roles, you will need to clear the relationship fields on both sides, meaning clearing the List of UserRole in EUser and removing the current EUser from the lists in the respective UserRole instances.
EDIT:
You are not deleting any entities from the database when clearing the lists of related entities. The only result will be that some rows in the join table will be deleted and after the next fetch/refresh, your EUser and UserRole instances will no longer be related.
If you want to remove the UserRoles DB entries, you can do so after removing the relationships to
Related
Let's say I have following model structure:
#Entity
#Table(....)
public class AnnotationGroup{
...
private List<AnnotationOption> options;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#JoinColumn(name = "annotation_group_id", nullable = false)
public List<AnnotationOption> getOptions() {
return options;
}
}
#Entity
#Table(...)
public class AnnotationOption {
private Long id;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Override
public Long getId() {
return id;
}
}
At the moment I have group1 with AnnotationOptions opt1 opt2 and opt3
Then I want to replace all option with only one option opt1
Additionally I have constraint in database:
CONSTRAINT "UQ_ANNOTATION_OPTION_name_annotation_group_id" UNIQUE (annotation_option_name, annotation_group_id)
And this one fires:
Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "UQ_ANNOTATION_OPTION_name_annotation_group_id"
Detail: Key (name, annotation_group_id)=(opt1, 3) already exists.
Actually isuue that hibernate removes orphans after update.
Can you suggest something t resolve issue?
There are so many things that are wrong in this example:
EAGER fetching on the #OneToManycollection is almost always a bad idea.
Unidirectional collections are also bad, use the bidirectional one.
If you get this exception, most likely you cleared all the elements and re-added back the ones that you want to be retained.
The best way to fix it is to explicitly merge the existing set of children with the incoming ones so that:
New child entities are being added to the collection.
The child entities that are no longer needed are removed.
The child entities matching the business key (annotation_group_name, study_id) are updated with the incoming data.
According to Hibernate documentation hibernate perform in the following order to preserve foreign-key constraint:
Inserts, in the order they were performed
Updates
Deletion of collection elements
Insertion of collection elements
Deletes, in the order they were performed
For your special need you should manually flush the transaction to force the deletion in database before.
I am using Hibernate 3.5.4 version as Orm I have two tables which have many to one relationship , Like Table 'Book' can have many 'Authors' associated with It.
#OneToMany(mappedBy = "key.bookId", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public Set<BookAuthor> getAuthors() {
return authors;
}
But we use soft delete for deleting the association (we maintain a column named isDeleted) , i want to fetch the entity based on isDeleted check if its 1 it should not be loaded , else if 0 load it.
Is it possible by modifying this current fetching strategy to provide above support or there is another better solution that can be applied please let me know.
Have a look at the #Filter or #Where Annotation.
As far as I know this is the usual way to restrict collection fetching.
I am using hibernate with JPA annotations for relationship mapping.
I have three entities in my code User Group & User_Group
User & Group are in a ManyToMany relationship.
User_Group is a kinda bridge table but with some additional fields. So here is the modified mapping code.
User
#Entity
#Table(name = "USERS")
public class User {
#OneToMany(mappedBy = "user")
private Set<UserGroup> userGroups
}
Group
#Entity
#Table(name = "GROUPS")
public class Group {
#OneToMany(mappedBy = "group")
private Set<UserGroup> userGroups
}
UserGroup
#Entity
#Table(name = "USERS_GROUPS")
public class UserGroup {
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "GROUP_ID")
private Group group;
}
When I set the user & group object to the usergroup & save it.
User user = new User("tommy", "ymmot", "tommy#gmail.com");
Group group = new Group("Coders");
UserGroup userGroup = new UserGroup();
userGroup.setGroup(group);
userGroup.setUser(user);
userGroup.setActivated(true);
userGroup.setRegisteredDate(new Date());
session.save(userGroup);
Things work fine. With CascadeType.ALL the group object & user object are updated too. But when I delete the userGroup object. The child object are deleted too.
Deletion of child objects is a strict no no.
There is no CascadeType.SAVE-UPDATE in JPA, which just does save or update but no delete. How do I achieve this.
If I remove the CascadeType.ALL from the mapping the child objects don't get updated & I need them to be updated.
SAVE_UPDATE is for save(), update(), and saveOrUpdate(), which are 3 Hibernate-proprietary methods. JPA only has persist() and merge(). So, if you want to use cascading on Hibernate-proprietary methods, you'll need to use Hibernate-proprietary annotations. In this case, Cascade.
Or you could stop using the Hibernate Session, and use the standard JPA API instead.
CascadeType.ALL includes CascadeType.REMOVE too.
The solution is to use all CascadeType.* you need except CascadeType.REMOVE, like so:
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE}))
in your UserGroup definitions.
It's almost always a code smell when propagating from child to parent entity, it should be the other way round.
From Cascading best practices:
Cascading only makes sense only for Parent – Child associations (the
Parent entity state transition being cascaded to its Child entities).
Cascading from Child to Parent is not very useful and usually, it’s a
mapping code smell.
From Hibernate best practices:
Avoid cascade remove for huge relationships
Most developers (myself included) get a little nervous when they see a
CascadeType.REMOVE definition for a relationship. It tells Hibernate
to also delete the related entities when it deletes this one. There is
always the fear that the related entity also uses cascade remove for
some of its relationships and that Hibernate might delete more
database records than intended. During all the years I’ve worked with
Hibernate, this has never happened to me, and I don’t think it’s a
real issue. But cascade remove makes it incredibly hard to understand
what exactly happens if you delete an entity. And that’s something you
should always avoid. If you have a closer look at how Hibernate
deletes the related entities, you will find another reason to avoid
it. Hibernate performs 2 SQL statements for each related entity: 1
SELECT statement to fetch the entity from the database and 1 DELETE
statement to remove it. This might be OK, if there are only 1 or 2
related entities but creates performance issues if there are large
numbers of them.
My Entity is like
#Entity
#Table(name = "Item")
public class Item implements Serializable {
#Id
#GeneratedValue
#Column(name = "ID")
private long id;
#JoinColumn(name = "PARENT_ID")
#JsonIgnore
private Item parent;
}
I am doing 3 things in a single transaction
Persist some items using EntityManager
hibernate query "from item where id in newIdList"
hibernate query "from item where parent=parentid"
In First step after persisting new items I do entityManager.flush(); and flush mode here is commit.
In second step I do given hibernate query. Here I get the proper result but in third step when I do hibernate query it returns me the results. But this result does not contain the newly persisted query.
I think the problem is due to parentId condition. As per requirements I cannot change the condition. Is there any way we can solve this problem?
#JoinColumn does not establish a relationship from Item > Parent.
You need to annotate this relationship with the relevant association mapping, #OneToOne, #ManyToMany, #OneToMany, #ManyToOne etc.
Try 3rd step after commit... This is not direct solution to your problem but just give a try..
I have the following mapping:
#Entity
#Table(name = "Prequalifications")
public class Prequalification implements Serializable
{
...
#ManyToMany
#JoinTable(name = "Partnerships", joinColumns = #JoinColumn(name = "prequalification_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "company_id", referencedColumnName = "id"))
private Set<Company> companies;
...
}
In a #ManyToMany + #JoinTable mapped relationship, isn't it kind of implicit that the association (link) entities (here Partnerships) are automatically persisted, removed, etc. even though
by default, relationships have an empty cascade set
? The above quote was taken from "Pro JPA 2, by Mike Keith".
Executing
em.merge(prequalification);
on the above entity does persist the associated partnerships without any cascade types specified.
Am I correct that this implicit cascade has to be performed? This isn't mentioned anywhere I looked...
The rows in the join table will be inserted/deleted as part of the owning Entity (if bi-directional the side without the mappedBy). So if you persist or remove or update the Prequalification the join table rows will also be inserted or deleted.
The target Company objects will not be cascaded to. So on remove() they will not be deleted, if the list is updated they will not be deleted unless orphanRemovla is set. Persist should also not be cascaded, but what happens when you have references to "detached" objects is somewhat of a grey area. Technically an error should be thrown, because the object is new and the relationship was not cascade persist. It may also try to insert and get a constraint error. It should not cascade the persist, although your object model is technically in an invalid state, so what occurs may depend on the provider.
Wanted to add a comment, but don't have enough rep for it.
I had the same question as #D-Dᴙum: "Where in the docs can we find a reference to this behaviour?"
I found it in the Hibernate docs (many-to-many).
If you scroll just a bit just below the code example there, you will find:
When an entity is removed from the #ManyToMany collection, Hibernate simply deletes the joining record in the link table. Unfortunately, this operation requires removing all entries associated with a given parent and recreating the ones that are listed in the current running persistent context.
Where the "link table" refers to the "join table".
Hope this helps.