简体   繁体   中英

JPA: unidirectional many-to-one and cascading all

Say I have a unidirectional @ManyToOne relationship like the following:


    @Entity
    @Table(name = "TestUser")
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Setter
    public class TestUser {
    
        @Id
        @Column(nullable = false)
        private String id;
    
        @Column(name = "some_property")
        private String someProperty;
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof TestUser)) return false;
            return id != null && id.equals(((TestUser) o).getId());
        }
    
        @Override
        public int hashCode() {
            return getClass().hashCode();
        }
    }
    
    @Entity
    @Setter
    @Getter
    @NoArgsConstructor
    @AllArgsConstructor
    public class BugTicket {
    
        @Id
        @GeneratedValue
        @Column(name = "id", nullable = false)
        private Long id;
    
        @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
        @JoinColumn
        private TestUser testUser;
    
        public BugTicket(TestUser testUser) {
            this.testUser = testUser;
        }
    }
    
    class Driver {
    
        public void run(String... args) throws Exception {
            TestUser u1 = new TestUser("user-id-1", "value1");
            userRepository.save(u1);
    
            var ticket = new BugTicket(u1);
            ticketRepository.save(ticket);
    
            ticket.getTestUser().setSomeProperty("value2");
            ticketRepository.save(ticket);
        }
    }

I want to update the user entity's 'someProperty' whenever I update BugTicket entity. Hence I added CascadeType.ALL to BugTicket's user.

However, when save is called, hibernate tries to create a new user and throws exception


    Hibernate: insert into test_user (some_property, id) values (?, ?)
    2022-10-02 18:33:39.404  WARN 30632 --- [  restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 0, SQLState: 23505
    2022-10-02 18:33:39.405 ERROR 30632 --- [  restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper   : ERROR: duplicate key value violates unique constraint "test_user_pkey"
      Detail: Key (id)=(user-id-1) already exists.

Why does hibernate try to insert User once again? Is removing CascadeType.all from BugTicket, and explicitely saving user ' ticket.getTestUser().setSomeProperty("value2");' the only way?

When you save an entity with jparepository it returns the persisted user so you need to use that.

As @grigouille says the method should be transactional as without it when you save BugTicket an attempts is made to save TestUser again as it is not a managed entity.

Making the method transaction like this, it will only save TestUser once and as it becomes a managed entity when you update it later in the method you don't even have to call save as Hibernates dirt checking mechanism will write any changes to it to the database when the transaction is committed
 @Transactional public void saveBugTicketAndTestUser() { TestUser u1 = new TestUser("user-id-1", "value1"); TestUser savedUser = userRepository.save(u1); BugTicket ticket = new BugTicket(savedUser); ticketRepository.save(ticket); savedUser.setSomeProperty("value2"); }

When you try to save your "ticket", ticket.getTestUser() is detached so Hibernate will try to create it. You need to work inside a transaction.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM