I have an Entity MXGroup
which is logically deletable via a status
property.
This entity is persisted into a MySQL database via Hibernate.
When I create this entity via my JSF page, persist it, then attempt to update this entity's status
column without reloading my page in between creation and logically deleting it, the change to the status column does not get persisted.
The entity is clearly in Hibernate's cache and the database. Updating the property and then calling merge
with it doesn't merge the changes into the database.
I've explicitly called flush
and getTransaction().commit()
on my entity manager after merging the entity, but still no dice.
If I refresh my page (Probably getting a different EntityManager
as my EntityManagers
are conversation scoped) then I can suddenly delete my MXGroup
without any issue.
I'm aware there are workarounds that I could do - so this is largely an academic exercise as to why this is happening...
My MXGroup
entities are loaded via a RESTful interface via ajax with the following methods: (PersonalGroup entity extends MXGroup entity)
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("personal/")
public Response restPersonalGroups() {
return Response.ok().entity(serializePersonalGroups()).build();
}
public String serializePersonalGroups() {
final List<PersonalGroup> personalGroupsList = this.userGroupService.getPersonalGroups();
String personalGroups = serializeGroupList(personalGroupsList);
return personalGroups;
}
With the userGroupService.getPersonalGroups()
method looking like:
public List<PersonalGroup> getPersonalGroups() {
CriteriaBuilder builder = this.getEntityManager().getCriteriaBuilder();
CriteriaQuery<PersonalGroup> criteria = builder.createQuery(PersonalGroup.class);
Root<PersonalGroup> root = criteria.from(PersonalGroup.class);
List<Predicate> predicateList = new ArrayList<Predicate>();
predicateList.addAll(Arrays.asList(getCommonPredicates(PersonalGroup.class, builder, criteria, root)));
predicateList.add(builder.equal(root.get(PersonalGroup_.user), getUser()));
TypedQuery<PersonalGroup> query = this.getEntityManager().createQuery(
criteria.select(root)
.where(predicateList.toArray(new Predicate[predicateList.size()]))
.orderBy(builder.asc(root.get(MXGroup_.name)))
.distinct(true));
List<PersonalGroup> personalGroups = query.getResultList();
for (PersonalGroup pg : personalGroups) {
this.getEntityManager().refresh(pg);
}
return personalGroups;
}
getCommonPredicates
in this circumstance only adds a predicate checking if status == 1
The delete functionality looks like:
public void deletePersonalGroup() throws BadLoginNameException, UniqueUserLoginException {
String groupIdStr = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("groupId");
int groupId = Integer.parseInt(groupIdStr);
MXGroup group = this.userGroupService.findMXGroup(groupId);
group.setStatus(0);
this.userGroupService.mergeMXGroup(group);
this.userGroupService.forceCommit();
}
with the mergeMXGroup
being:
@Override
public void mergeMXGroup(MXGroup group) {
super.merge(group);
}
whose super
implementation looks like:
protected <T> T merge(T entity) {
if (canUseService() && beforeMergeEntity(entity)) {
T merge = this.getEntityManager().merge(entity);
return merge;
}
return null;
}
And finally I force a flush and a commit like this:
public void forceCommit() {
this.getEntityManager().getTransaction().commit();
this.getEntityManager().flush();
}
The RESTful interface will almost certainly have a different entity manager to the delete code as the delete code is CDI managed and the RESTful interface is EJB. But I would have thought the flush and commit would have solved the issue? In fact this is the first time I've had to explicitly flush / commit in this entire project...
Any thoughts?
I'm reviewing the spec with regard to the semantics of the merge operation.
Either one of these scenarios could possibly explain the cause of your issue (or perhaps not):
The semantics of the merge operation applied to an entity X are as follows:
- If X is a detached entity, the state of X is copied onto a pre-existing managed entity instance X' of the same identity or a new managed copy X' of X is created.
- If X is a new entity instance, a new managed entity instance X' is created and the state of X is copied into the new managed entity instance X'.
- If X is a removed entity instance, an IllegalArgumentException will be thrown by the merge operation (or the transaction commit will fail).
- If X is a managed entity, it is ignored by the merge operation, however, the merge operation is cascaded to entities referenced by relationships from X if these relationships have been annotated with the cascade element value cascade=MERGE or cascade=ALL annotation.
- If X is an entity merged to X', with a reference to another entity Y, where cascade=MERGE or cascade=ALL is not specified, then navigation of the same association from X' yields a reference to a managed object Y' with the same persistent identity as Y
The persistence provider must not merge fields marked LAZY that have not been fetched: it must ignore such fields when merging.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With