javaspringspring-boothibernatejpa

Spring Data JPA OneToOne ON DELETE SET NULL


I have two entities for RfidTags and Persons. One RfidTag is assigned to one Person. One Person can have one RfidTag. If I delete a Person that has an RfidTag, the assigned RfidTag should not get deleted, it should remain in the database, but the value for person_id should be set to NULL. I already have my SQL like this, but it is not working:

ALTER TABLE `rfid_tags`
ADD CONSTRAINT `FK_Rfid_Tag_Person`
FOREIGN KEY (`person_id`) REFERENCES `persons`(`id`)
ON DELETE SET NULL;

And here are my entities. I have already searched a lot and tried different things like changing the CascadeTypes or adding @OnDelete(onDeleteAction=SET_NULL), but nothing is working. Either the RfidTag will also get deleted (which I do not want) or I get an error like: org.hibernate.TransientObjectException: persistent instance references an unsaved transient instance of 'PersonEntity' (save the transient instance before flushing)

@Getter
@Setter
@Entity
@Table(name = "persons")
public class PersonEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private String id;

    @Column(nullable = false)
    private String firstName;

    @Column(nullable = false)
    private String lastName;

    @OneToOne(fetch = FetchType.LAZY, mappedBy = "person", cascade = CascadeType.DETACH)
    private RfidEntity rfid;
}
@Getter
@Setter
@Entity
@Table(name = "rfid_tags")
public class RfidEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private String id;

    @Column(nullable = false)
    private String label;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="person_id", referencedColumnName="id")
    private PersonEntity person;
}

I have already searched a lot and tried different things like changing the CascadeTypes or adding @OnDelete(onDeleteAction=SET_NULL)


Solution

  • Hibernate does not automatically handle ON DELETE SET NULL. You need to manually set the association to NULL before deleting the PersonEntity.

    1. Update RfidEntity Mapping

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "person_id", referencedColumnName = "id", nullable = true)
    private PersonEntity person;
    

    2. Remove Cascade from PersonEntity

    @OneToOne(fetch = FetchType.LAZY, mappedBy = "person")
    private RfidEntity rfid;
    

    3. Manually Set person to NULL Before Deletion

    @Transactional
    public void deletePerson(String personId) {
        PersonEntity person = personRepository.findById(personId).orElseThrow();
        if (person.getRfid() != null) {
            person.getRfid().setPerson(null);
            rfidRepository.save(person.getRfid());
        }
        personRepository.delete(person);
    }
    

    4. Verify SQL Constraint

    ALTER TABLE `rfid_tags`
    ADD CONSTRAINT `FK_Rfid_Tag_Person`
    FOREIGN KEY (`person_id`) REFERENCES `persons`(`id`)
    ON DELETE SET NULL;
    

    Note: Hibernate doesn’t enforce ON DELETE SET NULL; it must be handled explicitly in the application logic.