I'm using an API REST application CXF - SPRING - JPA - HIBERNATE - MYSQL.
My problem is that when I tried to persist two entities "with relation with pride" between, I have an exception occurs:
Quote:
"localizedMessage": nested exception is org. hibernate. StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1"
So, numerous of questions already exist with this exception, but I don't find response with them.
Before my code, I will explain the JPA entities and my Mysql tables
So I have Two Entity
A n - n relation between them with period. So the relation is
Id Concept
Id GroupOfItem
Beginning_date
End_date
Then, I decide to consider this relation not as a straight JPA n-n relation, but a third entity relation (1-n - n-1)
Code:
1-n n-1
Concept --> ConceptHasGroupOfConcept <-- GroupOfItem
PK PK PK
Id Concept Id Concept Id GroupOfItem
Id GroupOfItem
Beginning_date
End_date
So i have this entities
Code:
@MappedSuperclass
public abstract class EntityClassAbstract {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="sequential_number", unique=true, nullable=false)
private int id;
@Column(name="version_number", columnDefinition = "short DEFAULT 1", nullable = false)
@Version
private short versionNumber;
+ Constructor + getter & setter
}
Code:
@Entity
@Table(catalog="modeler",schema="modeler",name="concept")
@AttributeOverride(name="id", column=@Column(name="idconcept"))
public class Concept extends IdEntityClass implements Serializable {
//bi-directional many-to-one association to GroupOfItemTypeOfConceptTypeHasConceptType
@OneToMany(mappedBy="pKlink.entityOrigin",fetch=FetchType.LAZY) private Set<ConceptHasGroupOfItems> conceptHasGroupOfItemss;
public Concept() {}
+ Constructor + getter & setter
}
Code:
@Entity
@Table(catalog="modeler",schema="modeler",name="group_of_items")
@AttributeOverride(name="id", column=@Column(name="idgroup_of_items"))
public class GroupOfItems extends IdEntityClass implements Serializable {
//bi-directional many-to-one association to GroupOfItemTypeOfConceptTypeHasConceptType
@OneToMany(mappedBy="pKlink.entityLink",fetch=FetchType.EAGER, cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
private Set<ConceptHasGroupOfItems> conceptHasGroupOfItemss;
public GroupOfItems() {}
+ Constructor + getter & setter
public void setRelationsOfName(Set<ItemRelation> itemRelations, IInfoConnect iInfoConnect, String nameOfnoeud) {
conceptHasGroupOfItemss = new HashSet<ConceptHasGroupOfItems>();
for ( ItemRelation itemRelation : itemRelations)
{ LOGGER.debug("MESS 1 : rentré dans la boucle & nom de la relation " + itemRelation.getNameOfRelation());
if (itemRelation.getNameOfRelation().compareTo("ConceptHasGroupOfItems") == 0 ) {
LOGGER.debug("MESS 1 : ConceptHasGroupOfItems est trouvé ");
conceptHasGroupOfItemss = ConceptHasGroupOfItems.SetRelationOfName(itemRelation, iInfoConnect, nameOfnoeud, 2, this);
for (ConceptHasGroupOfItems conceptHasGroupOfItems : conceptHasGroupOfItemss)
this.addConceptHasGroupOfItems(conceptHasGroupOfItems);
}
}
And the entities for my pseudo relation
Code:
@Entity
@Table(catalog="modeler",schema="modeler",name="concept_has_group_of_item")
@AttributeOverrides({
@AttributeOverride(name="pKlink.entityOrigin", column=@Column(name="concept_idconcept")),
@AttributeOverride(name="pKlink.entityLink", column=@Column(name="group_of_item_idgroup_of_items"))})
@AssociationOverrides({
@AssociationOverride(name="pKlink.entityOrigin", joinColumns=@JoinColumn(name="concept_idconcept", nullable=false, referencedColumnName="idconcept")),
@AssociationOverride(name="pKlink.entityLink", joinColumns=@JoinColumn(name="group_of_item_idgroup_of_items", nullable=false, referencedColumnName="idgroup_of_items")) })
public class ConceptHasGroupOfItems extends implements Serializable {
@EmbeddedId RelationWithPeriodeBeetweenTwoEntityClassPK<Concept, GroupOfItems> pKlink;
public ConceptHasGroupOfItems () {
super();
pKlink = new RelationWithPeriodeBeetweenTwoEntityClassPK<Concept, GroupOfItems>();
}
+ getter / setter
public static Set<ConceptHasGroupOfItems> SetRelationOfName(ItemRelation itemRelation,IInfoConnect iInfoConnect, String nameOfnoeud) {
for ( ItemRelationOccurence itemRelationOccurence : itemRelation.getItemRelationOccurences())
{
ConceptHasGroupOfItems conceptHasGroupOfItems = new ConceptHasGroupOfItems();
//Test code, normaly, concept is read by a service
Concept concept = new Concept();
Concept.setId(1);
conceptHasGroupOfItems.getpKlink().setEntityOrigin(concept);
conceptHasGroupOfItems.getpKlink().setBeginOfValidity(LocalDateTime.now());
conceptHasGroupOfItems.getpKlink().setEndOfValidity(RolesEntities.LOCALDATETIME_END_OF_BEGINING);
conceptHasGroupOfItems.SetInitEntity(iInfoConnect);
conceptHasGroupOfItemss.add(conceptHasGroupOfItems);
}
}
}
Code:
@Embeddable
public class RelationWithPeriodeBeetweenTwoEntityClassPK<ITEM1 extends EntityIdClassAbstract,ITEM2 extends EntityIdClassAbstract>
implements Serializable
{
@ManyToOne(fetch=FetchType.LAZY) private ITEM1 entityOrigin;
@ManyToOne(fetch=FetchType.LAZY) private ITEM2 entityLink;
@Column(name="beginning_of_validity", nullable=false) protected LocalDateTime beginOfValidity;
@Column(name="end_of_validity", nullable=false) protected LocalDateTime endOfValidity;
public RelationWithPeriodeBeetweenTwoEntityClassPK() { super();}
+ getter / setter}
Now the scenario of my problem.
In a first time, I have persisted the entity Concept with a id 1 with a first call of an API REST.
In a second time, a call of an API REST to persist the entity GroupItems and the link ConceptHasGroupItems. the new GroupItems must be related to the concept id 1.
To do that, I use a "generic" service who creates a entity. This service generic work with an entity without Relation to create (even if it's concept or GroupItems)
If a link is present, the service sets a relation entity. the code to flow into a relation entity is inside an entity. example : the code which flow into ConceptHasGroupItems is Inside GroupItems (see jpa entity) and is called by the service.
So after the first call, the concept id 1 is created in base.
In second call, to create the groupitems and the relation, the exception happen. I see the log of hibernate and I'm surpise to see that
a insert to create GroupItems exist (normal)
a insert to create ConceptGroupItems exist (normal)
a update of GroupItems exist (????)
a update of ConceptGroupItems exist (????)
The exception occurs on the seconds update. On the updates, we see that hibernate know the PKs
Code:
/* insert com.omni.domain.modeler.entites.GroupOfItems
*/ insert
into
modeler.group_of_items
(action_date, action_time, action_timestamp, action_idaction, application_idapplication, channel_idchannel, component_idcomponent, interface_idinterface, person_idperson, time_zone_idtime_zone, version_number)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
/* insert com.omni.domain.modeler.entites.ConceptHasGroupOfItems
*/ insert
into
modeler.concept_has_group_of_item
(action_date, action_time, action_timestamp, action_idaction, application_idapplication, channel_idchannel, component_idcomponent, interface_idinterface, person_idperson, time_zone_idtime_zone, version_number, beginning_of_validity, end_of_validity, group_of_item_idgroup_of_items, concept_idconcept)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
/* update
com.omni.domain.modeler.entites.GroupOfItems */ update
modeler.group_of_items
set
action_date=?,
action_time=?,
action_timestamp=?,
action_idaction=?,
application_idapplication=?,
channel_idchannel=?,
component_idcomponent=?,
interface_idinterface=?,
person_idperson=?,
time_zone_idtime_zone=?,
version_number=?
where
idgroup_of_items=?
and version_number=?
/* update
com.omni.domain.modeler.entites.ConceptHasGroupOfItems */ update
modeler.concept_has_group_of_item
set
action_date=?,
action_time=?,
action_timestamp=?,
action_idaction=?,
application_idapplication=?,
channel_idchannel=?,
component_idcomponent=?,
interface_idinterface=?,
person_idperson=?,
time_zone_idtime_zone=?,
version_number=?
where
beginning_of_validity=?
and end_of_validity=?
and group_of_item_idgroup_of_items=?
and concept_idconcept=?
and version_number=?
I modify the code :
put the code which flow into the relation after the instruction PERSIST
to persist the entity relation ConceptHasGroupItems with a proper EntityManager and remove cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE} on the relation in the entity GroupItems
in the modify just above, i use the flush after before the groupItems persist and the ConceptHasGroupItems persist (even if i don't want use the flush)
give the GroupItems as parameter of setRelationsOfName() method to link ASAP in the code GroupItems in ConceptHasGroupItems
BUT ALWAYS, THE EXCEPTION OCCURS.
After, I try to use a technical ID as PK on ConceptHasGroupItems, and the old PK became just property in relation entity, AND IT'S WORK without change of coding (only the JPA entity ConceptHasGroupItems to declare the new pk).
So I'm sceptical cause i have a other relation entity without "period" which work. Imagine that ConceptHasGroupItems doesn't have "period" and it's work with the two ID as PK ...
Edit and in reading, this case works. It's just in persisting that it doesn't work.
Thanks for your Help