jpaRepository delete() or deleteById() don´t works, springBoot

163 views Asked by At

I need to delete an Archivo record and I have the next case. I am having problems to delete using detele() and deleteById() the code is doing nothing.

the next one is the main code:

@Override
public RespuestaVo delete(Integer archId) {
    RespuestaVo respuestaVo = new RespuestaVo();

    try {
       if (!validarArchivoDeUsuario(archId)){
            respuestaVo = new RespuestaVo(401, HttpStatus.UNAUTHORIZED, "Acceso a recursos no autorizado.");
            return respuestaVo;
        }
        respuestaVo = new RespuestaVo(201, HttpStatus.OK, "El archivo ha sido eliminado con éxito.");
        iArchivosDao.deleteById(archId);

    } catch (Exception ex) {
        respuestaVo = new RespuestaVo(400, HttpStatus.BAD_REQUEST, "No ha sido posible eliminar el archivo, inténtelo más tarde.");
        Logger.getLogger(LogosEmprendimientosServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
    }
    return respuestaVo;
}

This code use a method call "validarArchivoDeUsuario()" it is use just to validate if the archivo ouns to the user. this method do what it have to do.

 private boolean validarArchivoDeUsuario(int archId) {
    Personas persona = new Personas();
    persona = jwtProvider.getUserID();
    List<Emprendimientos> emprendimientos;

    emprendimientos =  iEmprendimientosDao.findByPersIdAndEmprEstado(persona, ACTIVO);
    for (Emprendimientos emprendimiento: emprendimientos){
        for (Archivos archivo: emprendimiento.getArchivosCollection()){
            if (archivo.getArchId().equals(Integer.valueOf(archId))){
                return true;
            }
        }
    }
    return false;
}

If I use the code like is now it go to the validation then go to the iArchivosDao.deleteById(archId); line but it dont do anything.(nothing in log)

But if I comment the "for" inside validarArchivoDeUsuario and hardcode the return true like next code. it also goes to iArchivosDao.deleteById(archId); line but in this case it deletes the record:

log: org.hibernate.SQL : delete from archivos where arch_id=? Hibernate: delete from archivos where arch_id=?

private boolean validarArchivoDeUsuario(int archId) {
    Personas persona = new Personas();
    persona = jwtProvider.getUserID();
    List<Emprendimientos> emprendimientos;

    emprendimientos =  iEmprendimientosDao.findByPersIdAndEmprEstado(persona, ACTIVO);
    /*for (Emprendimientos emprendimiento: emprendimientos){
        for (Archivos archivo: emprendimiento.getArchivosCollection()){
            if (archivo.getArchId().equals(Integer.valueOf(archId))){
                return true;
            }
        }
    }*/
    return true;
}

Looks like some in here affect the delete. I try setting emprendimientos , archivo, emprendimiento to null before return true but didn't work

Emprendimientos.java ...

@OneToMany(cascade = CascadeType.ALL, mappedBy = "emprId")
private Collection<Archivos> archivosCollection;

...

Archivos.java ...

@Entity
@Table(name = "ARCHIVOS")
@Data
public class Archivos {
...

@JsonBackReference("emprId-Archivo")
@JoinColumn(name = "EMPR_ID", referencedColumnName = "EMPR_ID")
@ManyToOne(optional = false)
priva

te Emprendimientos emprId;

...

Thanks for your healt!

3

There are 3 answers

1
MAHADEV CHAURASIYA On

It looks like that issue may be related to the way your JPA entities and the associated relationships are mapped in your Spring Boot application. When you uncomment the for loop inside the validarArchivoDeUsuario method, it's possible that the associated entities and relationships are loaded, and the delete operation works as expected.

try with apply the @Transactional annotation to your delete method

@Transactional
public RespuestaVo delete(Integer archId) {
    RespuestaVo respuestaVo = new RespuestaVo();

    try {
        // ... Add your existing code ...

        respuestaVo = new RespuestaVo(201, HttpStatus.OK, "El archivo ha sido eliminado con éxito.");
        iArchivosDao.deleteById(archId);

    } catch (Exception ex) {
        // ... your existing error handling ...
    }
    return respuestaVo;
}
0
Murat K. On

For me it looks like, for loop can not find the record and can not delete it. Please try this:

for (Emprendimientos emprendimiento: emprendimientos) {
    for (Archivos archivo: emprendimiento.getArchivosCollection()) {
        if (archivo.getArchId().equals(Integer.valueOf(archId))) {
            // Check if the record exists
            if (iArchivosDao.findById(archId).isPresent()) {
                // Delete the record
                iArchivosDao.deleteById(archId);
                return true;
            } else {
                // The record does not exist
                return false;
            }
        }
    }
}

And do not forget @Transactional annotation because you are modifying the database.

0
Chris On

Issue is with your cascade options on the archivosCollection mapping, specifically cascade PERSIST (included in CascadeType.ALL). You are deleting an instance of Archivos but leaving an instance of Emprendimientos in the same persistence unit still referencing it. JPA will process your delete request, but when it comes to checking the managed Emprendimientos instance, JPA will find that same reference and is required to persist it back in, undoing your delete operation.

You should be removing this and any other references to this Archivos before deleting it. If you don't, you may find similar behavior at odd times, and doing so will give your application the chance and places to put callbacks to clear caches should caching be implemented later on.

You've already got one solution - by directly calling a native query you are forcing the delete statement directly to the database. JPA will still believe though that the Archivos it has in the cache exists and won't try to re-insert it - for now anyway. You'll want to ensure that any cached instance of Emprendimientos is cleared and re-read from the database after commit or you risk having that stale data re-inserted later on.

Alterantively, you can just remove the cascadeType.PERSIST from the relationship:

@OneToMany(cascade = CascadeType.DELETE, mappedBy = "emprId")
private Collection<Archivos> archivosCollection;

Only use cascade operations where you are sure you must - in most situations it is easier to make a manual archivosCollection.forEach(arch -> em.persist(arch)); than it is to deal with not having the operation cascade.

Third option, the one I would recommend in most situations, is to maintain the bidirectional relationship. Since you are deleting Archivos, you should clear out any references to it. There are performance reasons sometimes not to do so; the default way to do so might be to call

if (null != archivos.emprId) {
  archivos.emprId.archivosCollection.remove(archivos);
}

Which if everything isn't already loaded, might force a bunch of SQL queries. This can be tempered by checking if the relationships are loaded already with isLoaded checks:

if (null != archivos.emprId) {
  if (Persistence.getPersistenceUtil().isLoaded(archivos.emprId, "archivosCollection") ) {
    archivos.emprId.archivosCollection.remove(archivos);
  }
}