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
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;