Reputation: 1207
I had one problem, Need to perform complete delete and then insert. Tried one approach, can suggest is there any other better way ?
{
"incidentTime": 1491207083634,
"estCode": 152,
"incidentParamTrans": [
{
"paramValueList": [
11,
12,
14
]
}
]
}
THis is the main entity class.
@Entity
@Table(name="IR_TB_INCIDENT_HDR")
public class IncidentHdr implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@SequenceGenerator(name="IR_TB_INCIDENT_HDR_INCIDENTID_GENERATOR", sequenceName="IR_SEQ_INCIDENT_ID")
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="IR_TB_INCIDENT_HDR_INCIDENTID_GENERATOR")
@Column(name="INCIDENT_ID")
private long incidentId;
@OneToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE, CascadeType.REMOVE}, fetch = FetchType.LAZY, mappedBy="incidentHdr")
private Set<IncidentParamTran> incidentParamTrans;
public IncidentHdr() {
}
}
This is the entity class with one to many mapping. where this paramValueList in the DTO(from request json) is fetch and insert as 3(paramId) records into IncidentParamTrans table.
/**
* The persistent class.
*
*/
@Entity
@Table(name="IR_TB_INCIDENT_PARAM_TRAN")
public class IncidentParamTran implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@SequenceGenerator(name="IR_TB_INCIDENT_PARAM_TRAN_GENERATOR", sequenceName="IR_SEQ_INCIDENT_PARAM_RUN_ID")
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="IR_TB_INCIDENT_PARAM_TRAN_GENERATOR")
@Column(name="PARAM_RUN_ID")
private long paramRunId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="INCIDENT_ID")
private IncidentHdr incidentHdr;
@Column(name="PARAM_ID")
private BigDecimal paramId;
public IncidentParamTran() {
}
}
If Input is like this.
{
"incidentId": 4700,
"incidentTime": 1491207083634,
"estCode": 152,
"incidentParamTrans": [
{
"paramValueList": [
10,
14,
]
}
]
}
Had to delete all the data in IncidentParamTrans table, having incident Id is 4700. where incident Id is not a primary key.
After that I had to insert all new records (2 records) into IncidentParamTrans.
Performing a complete delete and doing new insert, which in turn throws flushing exception.
Code That I tried
The service layer code snippet
@Override
@Transactional(rollbackFor=IncidentReportingException.class)
public IncidentHdrDto saveIncidentReport(IncidentHdrDto incidentHdrDto)
if(incidentHdrDto.getIncidentParamTrans()!= null ){
Set<IncidentParamTranDto> incidentParamTranDtos = new HashSet<IncidentParamTranDto>();
// dto.getSensitivityPattern().remove
IncidentHdr inc = mapper.map(incidentHdrDto, IncidentHdr.class);
incidentParamTransRepo.deleteByIncidentHdr(inc);
for(IncidentParamTranDto item:incidentHdrDto.getIncidentParamTrans()){
if(item != null){
for(BigDecimal paramItem: item.getParamValueList()){
IncidentParamTranDto val = new IncidentParamTranDto();
val.setParamId(paramItem);
val.setIncidentHdr(incidentHdrDto);
incidentParamTranDtos.add(val);
}
}
}
incidentHdrDto.setIncidentParamTrans(incidentParamTranDtos);
}
result = saveIncidentHdr(incidentHdrDto);
}
map the dto to entity and calling repo save method.
@Override
public IncidentHdrDto saveIncidentHdr(IncidentHdrDto incidentHdrDto)
throws IncidentReportingException {
return mapper.map(iReportingRepo.save(mapper.map(incidentHdrDto, IncidentHdr.class)),IncidentHdrDto.class);
}
Repo class used to delete
public interface IncidentParamTransRepo extends JpaRepository<IncidentParamTran, Long> {
Long deleteByIncidentHdr(IncidentHdr inc);
}
Exception :
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: om.gov.moh.irs.model.entity.incident.IncidentHdr
Upvotes: 0
Views: 407
Reputation: 771
I believe it fails due to the fact that IncidentHdr.incidentParamTrans
collection remains in an invalid state because the actual removal of the incident params is handled by the incidentParamTransRepo.deleteByIncidentHdr(inc);
repository call but on the other hand the ownership over the CRUD operations is set on the IncidentHdr.incidentParamTrans
via the cascade behavior configuration.
To avoid a confusion I would suggest to try out following approach :
IncidentHdr.incidentParamTrans
collection : inc .getIncidentParamTrans().clear()
IncidentHdr
: incidentHdrRepository.**saveAndFlush**(inc)
. This will trigger delete query you were executing with a manual repository call. It is important to execute the save with a flush so the actual deletion query is executed at this point prior to the collection being filled again.The advantage of this approach is that Hibernate can track all the changes done on the IncidentHdr.incidentParamTrans
collection and make sure that the states are handled correctly.
One of the downsides of this approach is that it loads whole IncidentHdr.incidentParamTrans
collection into the memory, however, based on your configuration I believe it should not be a problem since you already do that (by delegating the CRUD management of the IncidentHdr.incidentParamTrans
to the IncidentHdr
entity).
Hope this helps.
Upvotes: 1