2

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.

2
  • Can you show the code which generate this exception? Commented Jan 17, 2017 at 0:53
  • I updated with code... to how I call save... Commented Jan 17, 2017 at 14:47

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

8 Comments

Are you sending an existing or new Usuario as part of the Cliente that the controller receives.
Existing... I think I make it work... just changed the cascade type to: cascade = CascadeType.MERGE and also remove the @MapsId annotation you said, thanks.
If you made it work by randomly changing the cascade type from ALL to Merge, and you think that solved your problems, then you really need to read the documentation! You need to understand why you got the detached error (why I asked the questions I did), without this knowledge the problem will almost certainly reappear. I have worked with JPA since 2009 and believe me, until you understand Entity lifecycle, there are a lot of error you will not understand, and you will spend a lot of time on Stack Overflow.
I know I know... of course I'll read the documentation but the linke you provide guide me through how to work with Detatched entities, so I tried the MERGE option. I'm new to JPA so this is very helpful... thanx a lot.
The reason your code failed is because when Cliente is new and Usuario already exists, Usuario is basically a detached entity (it was loaded by a different persistence context). Since you have cascade Persist, saving Cliente will cause save to be propagated to Usuario but since it is detached you get an error. Changing CascadeType from ALL to MERGE works because this means persist is not cascaded, but using cascade NONE would probably be more correct. But a better solution could be to merge/load the detached entity into the current persistence context.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.