Search code examples
javahibernatejpaentitymanager

Detached entity passed to persist when saving


I have some codes , but it gets a error at personRepository.save(person2) , no need to fix this but just explaining for me why this throws : detached entity passed to persist.

  @Entity
    public class Person {

        @Id
        @GeneratedValue
        @Column(name = "id")
        private Long id;

        @Column(name = "name")
        private String name;

        @Column(name = "wallet_id", insertable = false, updatable = false)
        private Long wallet_id;

        @ManyToOne  (fetch = FetchType.LAZY, cascade = CascadeType.ALL)
        @JoinColumn(name = "wallet_id",referencedColumnName = "id", insertable = true, updatable = true )
        private Wallet wallet;
    }




    public void testSave(String name) {
            Wallet walletNewNoHaveInDB = new Wallet(); // Generated id.

            Person person = new Person(); // Generated id.
            person.setName(name);
            person.setWallet(walletNewNoHaveInDB);
            personRepository.save(person); // This is OK and inserted into DB both(wallet , person).

            Person person2 = new Person(); // Generated id.
            person2.setName(name);
            person2.setWallet(walletNewNoHaveInDB);
            personRepository.save(person2); /// detached entity passed to persist
      }

Solution

  • I understand you did not annotate your test with @Transactional - in that case scenario spring is creating transaction for you to perform the "save" as you call it. More on that here: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions

    In 1st case you are having "new" objects (without ID, and unmanaged by JPA provider), and you did specify cascading so JPA provider knows how to save it in 1 transaction.

    In 2nd case, you are adding wallet that is already managed object, but without transaction.

    You either need to do it all in @Transactional scope (annotate your test), or if you pass object from "outside" of transaction (that's yr scenario), again...you need to have a transaction - so you need to start it and call merge() on the object that comes from outside of it.