user630209
user630209

Reputation: 1207

Complete delete and Insert throwing flushing exception

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

Answers (1)

Ruben
Ruben

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 :

  1. First convert the 'IncidentHdr.incidentParamTrans' into DTOs for a later use
  2. Clear the IncidentHdr.incidentParamTrans collection : inc .getIncidentParamTrans().clear()
  3. Save with flush the 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.
  4. Fill in again 'IncidentHdr.incidentParamTrans' and persist/save it

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

Related Questions