hibernatecascading-deleteshibernate-cascade

Hibernate ConstraintViolationException on delete


I have 2 entities which are Geofence and Device, once the user assign one geofence to some device there will be an association created represented by GeofenceDevice entity.

The problem is that once I want to delete a GeofenceDevice object I get an error :

Cannot delete or update a parent row: a foreign key constraint fails (`db_gpstracking`.`tj_geofence_device_gdev`, CONSTRAINT `FKE085AA815DC5054A` FOREIGN KEY (`GDEV_GEOFENCE_ID`) REFERENCES `t_geofence_geo` (`GEO_ID`))

Here is my code :

Device entity

package com.sifast.gpstracking.model;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.hibernate.annotations.NamedQueries;
import org.hibernate.annotations.NamedQuery;

import com.sifast.gpstracking.utils.TypeEnum;

@NamedQueries({ @NamedQuery(name = "findDeviceByUniqueId", query = "from Device d where d.uniqueId = :uniqueId") })
@Entity
@Table(name = "T_DEVICE_DEV")
public class Device implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "DEV_ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(name = "DEV_NAME", length = 1024, nullable = false)
    private String name;

    @Column(name = "DEV_UNIQUE_ID", nullable = false, unique = true)
    private String uniqueId;

    // TODO Add column last modification and date insert in every table
    @Column(name = "DEV_LAST_UPDATE", nullable = true)
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastUpdate;

    @Column(name = "DEV_PROTOCOL", length = 1024, nullable = true)
    private String protocol;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "device")
    private List<Position> listPosition;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.DETACH)
    @JoinColumn(name = "DEV_USER_ID", referencedColumnName = "USR_USER_ID", nullable = false)
    private User user;

    @Enumerated(EnumType.STRING)
    @Column(name = "DEV_TYPE", nullable = false)
    private TypeEnum type;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "device")
    private List<GeofenceDevice> listGeofenceDevice;

    public Device() {
        super();
        // TODO Auto-generated constructor stub
    }

    public Device(String name, String uniqueId, Date lastUpdate, String protocol) {
        this.name = name;
        this.uniqueId = uniqueId;
        this.lastUpdate = lastUpdate;
        this.protocol = protocol;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getUniqueId() {
        return uniqueId;
    }

    public void setUniqueId(String uniqueId) {
        this.uniqueId = uniqueId;
    }

    public Date getLastUpdate() {
        return lastUpdate;
    }

    public void setLastUpdate(Date lastUpdate) {
        this.lastUpdate = lastUpdate;
    }

    public String getProtocol() {
        return protocol;
    }

    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }

    public List<Position> getListPosition() {
        return listPosition;
    }

    public void setListPosition(List<Position> listPosition) {
        this.listPosition = listPosition;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public TypeEnum getType() {
        return type;
    }

    public void setType(TypeEnum type) {
        this.type = type;
    }

    public List<GeofenceDevice> getListGeofenceDevice() {
        return listGeofenceDevice;
    }

    public void setListGeofenceDevice(List<GeofenceDevice> listGeofenceDevice) {
        this.listGeofenceDevice = listGeofenceDevice;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (int) (id ^ (id >>> 32));
        result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Device other = (Device) obj;
        if (id != other.id)
            return false;
        if (uniqueId == null) {
            if (other.uniqueId != null)
                return false;
        } else if (!uniqueId.equals(other.uniqueId))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return name;
    }

    // @PrePersist
    // private void updateCreationDate() {
    // //creationDate = ...
    // }
    //
    // @PreUpdate
    // private void updateModificationDate() {
    // //creationDate = ...
    // }
}

Gefence entity

package com.sifast.gpstracking.model;

import java.io.Serializable;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import com.sifast.gpstracking.utils.ShapeTypeEnum;

@Entity
@Table(name = "T_GEOFENCE_GEO")
public class Geofence implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "GEO_ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(name = "GEO_NAME", nullable = true)
    private String name;

    @Column(name = "GEO_STATUS", nullable = false)
    private boolean status;

    // si inside == true alors c'est alerte d'entrée sinon c'est une alerte de sortie de la zone
    @Column(name = "GEO_ALERT_TYPE", nullable = false)
    private boolean inside;

    @Column(name = "GEO_SHAPE_TYPE", nullable = false)
    @Enumerated(EnumType.STRING)
    private ShapeTypeEnum shape;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "geofence", cascade = CascadeType.ALL)
    private List<Point> listPoint;

    public Geofence() {
        super();
        // TODO Auto-generated constructor stub
    }

    public Geofence(String name, boolean status, boolean inside, ShapeTypeEnum shape) {
        this.name = name;
        this.status = status;
        this.inside = inside;
        this.shape = shape;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isStatus() {
        return status;
    }

    public void setStatus(boolean status) {
        this.status = status;
    }

    public ShapeTypeEnum getShape() {
        return shape;
    }

    public void setShape(ShapeTypeEnum shape) {
        this.shape = shape;
    }

    public boolean isInside() {
        return inside;
    }

    public void setInside(boolean inside) {
        this.inside = inside;
    }

    public List<Point> getListPoint() {
        return listPoint;
    }

    public void setListPoint(List<Point> listPoint) {
        this.listPoint = listPoint;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((shape == null) ? 0 : shape.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Geofence other = (Geofence) obj;
        if (shape != other.shape)
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "Geofence [shape=" + shape + "]";
    }
}

GeofenceDevice entity

package com.sifast.gpstracking.model;

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.hibernate.Query;
import org.hibernate.annotations.NamedQueries;
import org.hibernate.annotations.NamedQuery;

@NamedQueries({
        @NamedQuery(name = "findGeofenceDeviceByGeofenceIdAndDeviceId", query = "from GeofenceDevice geoDev where geoDev.device = :device and geoDev.geofence = :geofence"),
        @NamedQuery(name = "findAllGeofenceDeviceByGeofenceId", query = "from GeofenceDevice geoDev where geoDev.geofence = :geofence") })
@Entity
@Table(name = "TJ_GEOFENCE_DEVICE_GDEV")
public class GeofenceDevice implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "GDEV_ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "GDEV_GEOFENCE_ID", referencedColumnName = "GEO_ID", nullable = false)
    private Geofence geofence;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "GDEV_DEVICE_ID", referencedColumnName = "DEV_ID", nullable = false)
    private Device device;

    // si status == true alors la geofencing correspond à ce device est activé sinon il est désactivé
    @Column(name = "GDEV_STATUS", nullable = true)
    private boolean status;

    // si inside == true alors c'est alerte d'entrée sinon c'est une alerte de sortie de la zone
    @Column(name = "GDEV_ALERT_TYPE", nullable = true)
    private boolean inside;

    public GeofenceDevice() {
        super();
        // TODO Auto-generated constructor stub
    }

    public GeofenceDevice(Geofence geofence, Device device, boolean status, boolean inside) {
        this.geofence = geofence;
        this.device = device;
        this.status = status;
        this.inside = inside;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Geofence getGeofence() {
        return geofence;
    }

    public void setGeofence(Geofence geofence) {
        this.geofence = geofence;
    }

    public Device getDevice() {
        return device;
    }

    public void setDevice(Device device) {
        this.device = device;
    }

    public boolean isStatus() {
        return status;
    }

    public void setStatus(boolean status) {
        this.status = status;
    }

    public boolean isInside() {
        return inside;
    }

    public void setInside(boolean inside) {
        this.inside = inside;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((device == null) ? 0 : device.hashCode());
        result = prime * result + ((geofence == null) ? 0 : geofence.hashCode());
        result = prime * result + (inside ? 1231 : 1237);
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        GeofenceDevice other = (GeofenceDevice) obj;
        if (device == null) {
            if (other.device != null)
                return false;
        } else if (!device.equals(other.device))
            return false;
        if (geofence == null) {
            if (other.geofence != null)
                return false;
        } else if (!geofence.equals(other.geofence))
            return false;
        if (inside != other.inside)
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "GeofenceDevice [geofence=" + geofence + ", device=" + device + "]";
    }

}

And here is a link for the Logs


Solution

  • After several hours I found a solution for my problem !

    Actually the fact of deleting a row from an association of 2 entities is called an orphanRemoval in Entity Relationships

    So the changes I made in code are :

    Device entity :

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "device", orphanRemoval=true)
    

    Geofence entity :

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "geofence", orphanRemoval=true)
    private List<GeofenceDevice> listGeofenceDevice;