Multiple representations of the same entity are being merged with #OneToMany - postgresql

I have a web application built with Spring Boot connected with a db PostgreSql, the project is about a education institute and manage Students and invoicing...
I generate manually all the invoices. I add the first cuota(invoice) normally but when i want to generate the second, i've this issue:
java.lang.IllegalStateException: Multiple representations of the same entity [com.codeboros.app.entity.Cuota#1] are being merged. Detached: [com.codeboros.app.entity.Cuota#1788e1df]; Managed: [com.codeboros.app.entity.Cuota#2697e3fc]
I've this entities:
#Entity
#Table(name="ALUMNOS")
public class Alumno implements Serializable {
#OneToMany(mappedBy="alumno", cascade= {CascadeType.DETACH,CascadeType.PERSIST,CascadeType.DETACH,CascadeType.REMOVE,CascadeType.REFRESH,CascadeType.MERGE}, fetch=FetchType.LAZY)
#NotFound(action = NotFoundAction.IGNORE)
#JsonIgnore
private List<Cuota> cuotas;
}
#Entity
#DiscriminatorValue(value="Cuota")
public class Cuota extends Factura implements Serializable {
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="alumno_cod")
#NotFound(action = NotFoundAction.IGNORE)
Alumno alumno;
}
And the AlumnoController
#PostMapping("/alumnoGenCuota/{id}")
public String GenCuota(#PathVariable Long id, Cuota cuota) {
Alumno alumno = alumnoService.get(id);
cuota.setAlumno(alumno);
cuota.setMonto(alumno.getCurso().getCuota());
cuota.setDetalle(alumno.getCurso().getNombre()+": $"+alumno.getCurso().getCuota()); //detalle
alumno.AgregarCuota(cuota);
alumnoService.save(alumno);
return "redirect:/alumnocuotas/"+id;
}
I tried to remove CascadeType.MERGE but do not save the news Cuotas

If you are not using Hibernate, remove CascadeType.MERGE from the entity that is not allowing you to persist the detached entity or put all cascade type other than CascadeType.MERGE
If you are using Hibernate, add the following lines to your persistence.xml -
<property name="hibernate.event.merge.entity_copy_observer" value="allow"/>
When you set hibernate.event.merge.entity_copy_observer=allow, Hibernate will merge each entity copy detected while cascading the merge operation. In the process of merging each entity copy, Hibernate will cascade the merge operation from each entity copy to its assocations with cascade=CascadeType.MERGE or CascadeType.ALL. The entity state resulting from merging an entity copy will be overwritten when another entity copy is merged.

Related

Why do I have to evict my entity from the EclipseLink shared cache before querying with an entity graph will load my lazy properties?

Before updating an entity in my Jakarta EE application running on GlassFish 5.1.0 with EclipseLink 2.7.4 and Derby 10.14.2.0, I compare the updated entity to the saved entity and document the changes. I noticed recently that my compare code was not working with #OneToMany relationship properties and #ElementCollection properties, and I tracked the problem to Lazy loading of the #OneToMany and #ElementCollection properties. I was able to resolve the using the fetch attribute as follows:
Fetch Eager Entity
#Entity
public class Container implements Serializable {
#OneToMany(mappedBy = "container", fetch = FetchType.EAGER)
private List<AssetSerial> assets;
#ElementCollection (fetch = FetchType.EAGER)
private List<Reference> references;
I wasn't entirely happy with this solution, because I assumed that the developers defaulted these relationship types to lazy loading for a purpose, so I continued researching and was excited to find many references to JPA Entity Graphs. I immediately create the following code to force EclipseLink to initialize my lazy loading properties before documenting the entity changes.
Entity Graph Entity
#Entity
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#NamedEntityGraph(
name = "Container.eager",
attributeNodes = {
#NamedAttributeNode("assets"),
#NamedAttributeNode("references") })
public class Container implements Serializable {
Entity Manager Initialization
#PersistenceContext(unitName = "MYPU")
private EntityManager em;
Find Method that only Works once per Entity
Map<String, Object> props = new HashMap<String, Object>();
props.put("javax.persistence.loadgraph", em.getEntityGraph("Container.eager"));
Container managedContainer = em.find(Container.class, updatedContainer.getId(), props);
PersistenceUnitUtil tester = em.getEntityManagerFactory().getPersistenceUnitUtil();
logger.debug("Assets: {}", tester.isLoaded(managedContainer, "assets"));
logger.debug("References: {}", tester.isLoaded(managedContainer, "references"));
Unfortunately, the isLoaded test methods only return true the first time I call the find method on a specific entity. The second and subsequent times isLoaded returns false. I struggled with this issue for many hours and determined that this issue was that the EclipseLink shared cache was not honoring the entity graph hint I was passing to the find method. I solved the problem by evicting the entity from the cache immediately before calling the find as shown below.
Find Method that Works
em.getEntityManagerFactory().getCache().evict(Container.class, updatedContainer.getId());
Map<String, Object> props = new HashMap<String, Object>();
props.put("javax.persistence.loadgraph", em.getEntityGraph("Container.eager"));
Container managedContainer = em.find(Container.class, updatedContainer.getId(), props);
PersistenceUnitUtil tester = em.getEntityManagerFactory().getPersistenceUnitUtil();
logger.debug("Assets: {}", tester.isLoaded(managedContainer, "assets"));
logger.debug("References: {}", tester.isLoaded(managedContainer, "references"));
Now the isLoaded test always returns true, and I'm able to document all the changed in the updated entity.
In summary, I have the following questions:
Why is EclipseLink not honoring my entity graph?
Am I going to encountering problems manually evicting my entity from the cache?
Is there a better way to force EclipseLink to initialize my lazy loading properties?

JPA 2.0 Eclipselink - Adding/Deleting to child list gets optimistic lock

I am having an issue where eclipselink (2.5) is throwing an OptimisticLockException even though the only thing I'm modifiying is trying to either add or remove an item from a child list.
Entities :
#Entity
#Table(name="PLAN_ORG_RELATIONSHIP")
#Customizer(GridCacheCustomizer.class)
#AdditionalCriteria("CURRENT_TIMESTAMP BETWEEN this.startDate AND this.endDate")
#NamedQuery(name="PlanOrganizationRelationship.findAll", query="SELECT p FROM PlanOrganizationRelationship p")
#Portable
public class PlanOrganizationRelationship extends PrismObject implements Serializable {
#OneToMany(mappedBy="planOrganizationRelationship", cascade=CascadeType.PERSIST, orphanRemoval=true)
#PortableProperty(10)
private List<PlanOrganizationAction> planOrganizationActions;
public PlanOrganizationAction addPlanOrganizationAction(PlanOrganizationAction planOrganizationAction) {
getPlanOrganizationActions().add(planOrganizationAction);
planOrganizationAction.setPlanOrgRelationship(this);
return planOrganizationAction;
}
public PlanOrganizationAction removePlanOrganizationAction(PlanOrganizationAction planOrganizationAction) {
getPlanOrganizationActions().remove(planOrganizationAction);
planOrganizationAction.setPlanOrgRelationship(null);
return planOrganizationAction;
}
#Column(name="LST_UPDT_DT")
#Version
#PortableProperty(5)
private Timestamp lastUpdatedDate;
}
Other side of One To Many:
#Entity
#Table(name="PLAN_ORGANIZATION_ACTION")
#Customizer(GridCacheCustomizer.class)
#AdditionalCriteria("CURRENT_TIMESTAMP BETWEEN this.startDate AND this.endDate")
#NamedQuery(name="PlanOrganizationAction.findAll", query="SELECT p FROM PlanOrganizationAction p")
#Portable
public class PlanOrganizationAction extends PrismObject implements Serializable {
#ManyToOne
#JoinColumn(name="PLN_ORG_RLTNP_SEQ_ID")
#PortableProperty(7)
private PlanOrganizationRelationship planOrganizationRelationship;
}
I have 3 paths - Adding new Relationship with Actions (both entities new) or Add an Action or Remove Action
When I am adding both, I perist the parent and the children are persisted as well and that is the expected behavior.
When I try to add or remove I try something like
PrismOrganizationRelationship por = findById (..) //we are spring-data-jpa
por.addPlanOrganizationAction(action);
repo.save(por); // Throws optimistic lock - even though #Version is the same
Not sure what is causing this issue ?
Check cascade=CascadeType.PERSIST. It work only for persist(save newly entity) operation. your operation remove child entity mean updating main entity. Thats why, you need to useCascadeType.MARGE` for update operation.

JPA not updating ManyToMany relationship in returning result

Here are my entities:
#Entity
public class Actor {
private List<Film> films;
#ManyToMany
#JoinTable(name="film_actor",
joinColumns =#JoinColumn(name="actor_id"),
inverseJoinColumns = #JoinColumn(name="film_id"))
public List<Film> getFilms(){
return films;
}
//... more in here
Moving on:
#Entity
public class Film {
private List actors;
#ManyToMany
#JoinTable(name="film_actor",
joinColumns =#JoinColumn(name="film_id"),
inverseJoinColumns = #JoinColumn(name="actor_id"))
public List<Actor> getActors(){
return actors;
}
//... more in here
And the join table:
#javax.persistence.IdClass(com.tugay.sakkillaa.model.FilmActorPK.class)
#javax.persistence.Table(name = "film_actor", schema = "", catalog = "sakila")
#Entity
public class FilmActor {
private short actorId;
private short filmId;
private Timestamp lastUpdate;
So my problem is:
When I remove a Film from an Actor and merge that Actor, and check the database, I see that everything is fine. Say the actor id is 5 and the film id is 3, I see that these id 's are removed from film_actor table..
The problem is, in my JSF project, altough my beans are request scoped and they are supposed to be fetching the new information, for the Film part, they do not. They still bring me Actor with id = 3 for Film with id = 5. Here is a sample code:
#RequestScoped
#Named
public class FilmTableBackingBean {
#Inject
FilmDao filmDao;
List<Film> allFilms;
public List<Film> getAllFilms(){
if(allFilms == null || allFilms.isEmpty()){
allFilms = filmDao.getAll();
}
return allFilms;
}
}
So as you can see this is a request scoped bean. And everytime I access this bean, allFilms is initially is null. So new data is fetched from the database. However, this fetched data does not match with the data in the database. It still brings the Actor.
So I am guessing this is something like a cache issue.
Any help?
Edit: Only after I restart the Server, the fetched information by JPA is correct.
Edit: This does not help either:
#Entity
public class Film {
private short filmId;
#ManyToMany(mappedBy = "films", fetch = FetchType.EAGER)
public List<Actor> getActors(){
return actors;
}
The mapping is wrong.
The join table is mapped twice: once as the join table of the many-to-many association, and once as an entity. It's one or the other, but not both.
And the many-to-many is wrong as well. One side MUST be the inverse side and use the mappedBy attribute (and thus not define a join table, which is already defined at the other, owning side of the association). See example 7.24, and its preceeding text, in the Hibernate documentation (which also applies to other JPA implementations)
Side note: why use a short for an ID? A Long would be a wiser choice.
JB Nizet is correct, but you also need to maintain both sides of relationships as there is caching in JPA. The EntityManager itself caches managed entities, so make sure your JSF project is closing and re obtaining EntityManagers, clearing them if they are long lived or refreshing entities that might be stale. Providers like EclipseLink also have a second level cache http://wiki.eclipse.org/EclipseLink/Examples/JPA/Caching

#AdditionalCriteria in combination with #OneToOne gives QueryException

I'm trying to create a RESTful service. For multi tenancy I'm applying the #AdditionalCriteria annotation. However, when I join an entity using the #OneToOne annotation the following Exception is raised:
Exception [EclipseLink-6174] (Eclipse Persistence Services - 2.3.0.v20110604-
r9504): org.eclipse.persistence.exceptions.QueryException
Exception Description: No value was provided for the session property
[SECURITYID]. This exception is possible when using additional criteria or
tenant discriminator columns without specifying the associated contextual
property. These properties must be set through Entity Manager, Entity Manager
Factory or persistence unit properties. If using native EclipseLink, these
properties should be set directly on the session.
Query: ReadObjectQuery(name="readObject" referenceClass=Addresses
sql="SELECT id, city, country, postalcode, province, security_id, street
FROM addresses WHERE ((id = ?) AND (security_id = ?))")
I set the property [SECURITYID] at EntityManager level and without the join everything works just fine. But when I join an entity using the #OneToOne I see that property is not there. I don't have enough experience to determine if this is caused by me doing something wrong or if this is a bug. To me it looks like a different EntityManager is used to fetch the joined entity. But I'm guessing because of me lacking in knowledge. I also tried to set the properties on EntityManagerFactory level but to no avail.
Here is my setup.
Entity:
#Entity
#AdditionalCriteria("this.securityId=:SECURITYID")
#Table(name = "tasks", catalog = "catalog", schema = "schema")
#XmlRootElement
#NamedQueries(
{
#NamedQuery(name = "Tasks.findAll", query = "SELECT t FROM Tasks t")
})
public class Tasks implements Serializable
{
...
#OneToOne(fetch=FetchType.EAGER)
#JoinColumn(name="service_address_id")
private Addresses serviceAddress;
...
}
RESTFacade class
#Stateless
#Path("tasks")
public class TasksFacadeREST extends AbstractFacade<Tasks>
{
#PersistenceContext(unitName = "UnitName")
private EntityManager em;
...
#java.lang.Override
protected EntityManager getEntityManager()
{
// Temp! REMOVE WHEN DONE
sessionId = "123456789";
Identifier.setIdentity(em,sessionId);
em.setProperty("SECURITYID",Identifier.securityId);
em.setProperty("USERID",Identifier.userId);
return em;
}
Thanks & Rgds,
M
My guess is this is related to caching.
Normally the tenant property would be set on the EntityManagerFactory, not the EntityManager. This would mean you have a different EntityManagerFactory for each tenant. You could also define the tenant property in your persistence.xml and have a different persistence unit per tenant.
If you need to set the tenant per EntityManager, then you need to disable the shared cache, or setting the cache mode to protected.
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Single-Table_Multi-Tenancy#Setting_Properties_and_Caching_Scope
For reference the property to be disabled is eclipselink.cache.shared.default
<property name="eclipselink.cache.shared.default" value="false"/>

Merging an object with FetchType.EAGER relation leads to "FailedObject"

I have an entity VM with a relationship to another entity BP. The relationship is eagerly fetched. First I load a VM. After loading the VM is detached, serialized and changed at the client side. Now I want to update the changed entity so I use the EntityManager.merge() method from JPA. Now I run into the following error from OpenJPA:
"Encountered new object in persistent field "Vm.bp" during attach. However, this field does not allow cascade attach. Set the cascade attribute for this field to CascadeType.MERGE or CascadeType.ALL (JPA annotations) or "merge" or "all" (JPA orm.xml). You cannot attach a reference to a new object without cascading."
Why do I have to add a Cascade.MERGE to a relationship to another entity that will never change? And why does JPA think that BP is a new object ("...cannot attach reference to a new object...")?
When using ManyToOne relationships do I always have to add Cascade.MERGE in order to update the entity or is this because of the EAGER fetch type?
Here's my entity:
#Entity
#Table(name = "VM")
public class Vm extends BaseEntity implements Serializable {
public static final long serialVersionUID = -8495541781540291902L;
#Id
#SequenceGenerator(name = "SeqVm", sequenceName = "SEQ_VM")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SeqVm")
#Column(name = "ID")
private Long id;
// lots of other fields and relations
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "BP_ID")
private Bp bp;
// ...
}
I found the reason why this error message comes up: The #Version annotated database field of the related Bp entity was initialized with "0". Apparently OpenJPA (1.2.3) is not able to cope with entity versions of zero.
Setting the version to 1 solved my issue.