Luisinho Rojas
Luisinho Rojas

Reputation: 199

Spring JPA nested Exception: detached entity passed to persist

I tried almost all what I could find here in SO, and another sites tutorials about creating an One to One Relationship with Hibernate.

So, I have two models, here are the last modifications, like for example the @MapsId annotation I also removed in previous test.

Usuario:

@Entity
@Table(name="usuarios")
@JsonIdentityInfo(
          generator = ObjectIdGenerators.PropertyGenerator.class, 
          property = "id")
public class Usuario {
    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="usuarios_id_seq")
    @SequenceGenerator(name="usuarios_id_seq", sequenceName="usuarios_id_seq", allocationSize=1)
    @Column(name="id")
    private Long id;

    @ManyToOne
    @JoinTable(name="roles_usuarios", joinColumns={@JoinColumn(name="usuarios_id", referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="roles_id", referencedColumnName="id")})
    private Rol rol;

    @OneToOne(cascade = CascadeType.ALL, mappedBy="usuario")
    private Cliente cliente;

Cliente:

@Entity
@Table(name="clientes")
@JsonIdentityInfo(
          generator = ObjectIdGenerators.PropertyGenerator.class, 
          property = "id")
public class Cliente {
    @Id
    //@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="clientes_id_seq")
    //@SequenceGenerator(name="clientes_id_seq", sequenceName="clientes_id_seq", allocationSize=1)
    //@Column(name="id")
    private Long id;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name="usuario_id", referencedColumnName="id")
    @MapsId
    private Usuario usuario;

Cliente Controller:

@PostMapping("/")
    public ResponseEntity<Void> postCliente(@RequestBody Cliente cliente, UriComponentsBuilder ucBuilder) {

        if( clienteService.isClienteExist(cliente) ){
            return new ResponseEntity<Void>(HttpStatus.CONFLICT);
        }

        clienteService.save(cliente);

        HttpHeaders headers = new HttpHeaders();

        headers.setLocation( ucBuilder.path("/{id}").buildAndExpand(cliente.getId()).toUri() );

        return new ResponseEntity<Void>(headers, HttpStatus.CREATED);

    }

Cliente Service:

@Override
    public Cliente save(Cliente cliente) {

        Cliente clt = new Cliente();

        clt.setUsuario(cliente.getUsuario());
        clt.setRazonSocial(cliente.getRazonSocial());
        clt.setRfc(cliente.getRfc());
        clt.setDireccion(cliente.getDireccion());
        clt.setEmail(cliente.getEmail());
        clt.setTelefono(cliente.getTelefono());
        clt.setContacto(cliente.getContacto());
        clt.setTelContacto(cliente.getTelContacto());
        clt.setEmailContacto(cliente.getEmailContacto());

        return clienteRepository.save(clt);

    }

If you notice I also have a many to one relationship with a Rol table which works fine, but when I pass information in the OneToOne which I pass it as a JSON it produces: detached entity passed to persist: com.swargos.entities.Usuario

IDK if I'm missing some annotations, or is that the database is created when running the spring application.

Upvotes: 2

Views: 2325

Answers (1)

Klaus Groenbaek
Klaus Groenbaek

Reputation: 5045

I'm providing a somewhat qualified guess, since you didn't include code that shows how you call persist.

The error means that the Usuario instance you are passing to persist() already has a primary key, but it is not a managed entity of that persistence context, see here for Entity Object lifecycle

My guess is that the Usuario instance was loaded by another EntityManager, then json-serialized to the front-end, and then posted back to the backend, and you need to set it on a Cliente (Since you have cascade in both directions it may also be the Cliente being set on the Usuario). Every time an entity has been loaded in one Persistence Context, and you want to save it in another you must either call em.merge() or you must call em.find() to load it into it (and then set the changes).

JPA is not magic, the life-cycle of the Entities and the Persistence Context which manage them is well defined, and unless the developer understands how these mechanisms work, a lot of time will be wasted trying to work against the framework.

Also @MapsId should only be used if Cliente used an @EmbeddedId for it primary key, which does not seem to be the case.

Upvotes: 2

Related Questions