Mapping tables, equals overridden compares two different classes

Hi everybody,

we are having some issues with the mapping of the tables described below.

The @Override equals of entity DeviceGroup returns always false because the Classes are different.
The left one is an instance of DeviceGroup while the second is an instance of DeviceGroup$HIbernateProxy. It happens only when the entities are the same.

Don’t know if I’m clear enough; if not let me know and I’ll add additional information.
Thanks

Tables (SQL)

CREATE TABLE {dbCoreSchema}.lt_device ( ID SERIAL PRIMARY KEY, deploymentID INTEGER NOT NULL REFERENCES {dbCoreSchema}.lt_deployment(ID) ON UPDATE CASCADE ON DELETE CASCADE,
nodemodelID INTEGER NULL REFERENCES {dbCoreSchema}.lt_nodemodel(ID) ON UPDATE CASCADE ON DELETE SET NULL DEFAULT NULL, bridgeID INTEGER NULL REFERENCES {dbCoreSchema}.lt_bridge(ID) ON UPDATE CASCADE ON DELETE SET NULL DEFAULT NULL,
name VARCHAR(100) NOT NULL,
location POINT NOT NULL DEFAULT(‘0,0’),
online BOOLEAN NOT NULL DEFAULT FALSE,
onmap BOOLEAN NOT NULL DEFAULT TRUE,
haslist BOOLEAN NOT NULL DEFAULT FALSE,
syncTime TIMESTAMP NULL,
parentID INTEGER NULL REFERENCES {dbCoreSchema}.lt_device(ID) ON UPDATE CASCADE ON DELETE CASCADE, devicetype {dbCoreSchema}.lt_devicetype,
devicetypekey VARCHAR(100) NULL,
insert_date TIMESTAMP NOT NULL DEFAULT NOW(),
insert_user VARCHAR(100) NOT NULL,
update_date TIMESTAMP NULL,
update_user VARCHAR(100) NULL);
CREATE UNIQUE INDEX ix01_phdevice ON ${dbCoreSchema}.lt_device(name);

– ======= DEVICE GROUP ======== –
CREATE TABLE {dbCoreSchema}.lt_devicegroup ( ID SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, description TEXT NULL, subgroup BOOLEAN NOT NULL DEFAULT FALSE, insert_date TIMESTAMP NOT NULL DEFAULT NOW(), insert_user VARCHAR(100) NOT NULL, update_date TIMESTAMP NULL, update_user VARCHAR(100) NULL); CREATE UNIQUE INDEX ix01_devicegroup ON {dbCoreSchema}.lt_devicegroup(name);

– ======= DEVICE GROUP (LINK MANY TO MANY) ======== –
CREATE TABLE {dbCoreSchema}.lt_dglink ( ID SERIAL PRIMARY KEY, device_ID INTEGER NOT NULL REFERENCES {dbCoreSchema}.lt_device(ID) ON UPDATE CASCADE ON DELETE CASCADE,
group_ID INTEGER NOT NULL REFERENCES {dbCoreSchema}.lt_devicegroup(ID) ON UPDATE CASCADE ON DELETE CASCADE, subgroup_ID INTEGER NOT NULL REFERENCES {dbCoreSchema}.lt_devicegroup(ID) ON UPDATE CASCADE ON DELETE CASCADE,
groupkey VARCHAR(100) NULL,
insert_date TIMESTAMP NOT NULL DEFAULT NOW());
CREATE UNIQUE INDEX ix01_dglink ON ${dbCoreSchema}.lt_dglink(device_ID, group_ID, subgroup_ID, groupkey);

Entity mapping

@LTPlugin
@Entity(name = “Device”)
@Table(name = “lt_device”, schema = LTDBConfig.LTSchema)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@NaturalIdCache
@LTEhcache(template = CacheTemplate.LONG_TIME)
public class Device extends LTTable implements LTEventObject {
private static final long serialVersionUID = 3028650185302870665L;

@ManyToOne
@Fetch(FetchMode.JOIN)
@JoinColumn(name = "deploymentID", nullable = false, updatable = false, insertable = false)
private Deployment deployment;

@Column(name = "deploymentID", nullable = false)
private int deploymentId;

@ManyToOne
@Fetch(FetchMode.JOIN)
@JoinColumn(name = "nodeModelId")
private NodeModel nodeModel;

@NaturalId(mutable = false)
@Column(name = "name", length = 100, nullable = false)
private String name;

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

@Column(name = "synctime", nullable = true)
private LocalDateTime synctime;

@Column(name = "onmap", nullable = false)
private boolean onmap = true;

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

@ManyToOne
@Fetch(FetchMode.SELECT)
@JoinColumn(name = "bridgeID", nullable = true)
private Bridge bridge;

@Column(name = "location", nullable = false)
private PGpoint location = new PGpoint();

@ManyToOne
@Fetch(FetchMode.SELECT)
@JoinColumn(name = "parentID", nullable = true, updatable = false)
private Device parent;

@Column(name = "deviceType", columnDefinition = LTDBConfig.LTSchema + ".lt_devicetype", nullable = true, updatable = false)
private DeviceType type;

@Column(name = "devicetypekey", length = 100, nullable = true)
private String typeKey;

@OneToMany(cascade = { 
		CascadeType.PERSIST, 
		CascadeType.MERGE,
		CascadeType.REFRESH},
		fetch = FetchType.LAZY,
		mappedBy = "device", orphanRemoval = true)
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@LTEhcache(template = CacheTemplate.LONG_TIME_SMALL)
private Set<DgLink> groups = new HashSet<>();

@ManyToMany(cascade = { 
		CascadeType.PERSIST, 
		CascadeType.MERGE,
		CascadeType.REFRESH},
		fetch = FetchType.LAZY)
@JoinTable(name = "lt_linkdevicefamily", schema = LTDBConfig.LTSchema,
	joinColumns = @JoinColumn(name = "deviceID", nullable = false, updatable = false),
	inverseJoinColumns = @JoinColumn(name = "familyID", nullable = false, updatable = false))
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@LTEhcache(template = CacheTemplate.LONG_TIME_SMALL)
private Set<Family> families = new HashSet<>();

@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "deviceID")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@LTEhcache(template = CacheTemplate.ONE_DAY, heap = @Heap(size = 10000), offHeap = @OffHeap(size = 30))
private Set<DeviceData> devicedata = new HashSet<>();

@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "deviceID")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@LTEhcache(template = CacheTemplate.LONG_TIME_SMALL)
private Set<DeviceSubGroup> subGroups = new HashSet<>();

public Deployment getDeployment() {
	return deployment;
}

public int getDepId() {
	return deployment.getId();
}

public void setDeployment(Deployment deployment) {
	this.deployment = deployment;
}

public String getName() {
	return name;
}

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

public Bridge getBridge() {
	return bridge;
}

public void setBridge(Bridge bridge) {
	this.bridge = bridge;
}

public PGpoint getLocation() {
	return location;
}

public void setLocation(PGpoint location) {
	this.location = location;
}

public Set<Family> getFamilies() {
	return families;
}

public void setFamilies(Set<Family> family) {
	this.families = family;
}

public Set<DeviceData> getDevicedata() {
	return devicedata;
}

public void setDevicedata(Set<DeviceData> devicedata) {
	this.devicedata = devicedata;
}

public boolean isOnline() {
	return online;
}

public int getDeploymentId() {
	return deploymentId;
}

public void setDeploymentId(int deploymentId) {
	this.deploymentId = deploymentId;
}

public void setOnline(boolean online) {
	this.online = online;
}

public LocalDateTime getSynctime() {
	return synctime;
}

public void setSynctime(LocalDateTime synctime) {
	this.synctime = synctime;
}

public NodeModel getNodeModel() {
	return nodeModel;
}

public void setNodeModel(NodeModel nodeModel) {
	this.nodeModel = nodeModel;
}

public Set<DgLink> getGroupsLink() {
	return groups;
}
	
public Device getParent() {
	return parent;
}

public void setParent(Device parent) {
	this.parent = parent;
}

public DeviceType getType() {
	return type;
}

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

public String getTypeKey() {
	return typeKey;
}

public void setTypeKey(String typeKey) {
	this.typeKey = typeKey;
}

public boolean isOnmap() {
	return onmap;
}

public void setOnmap(boolean onmap) {
	this.onmap = onmap;
}

public boolean isHaslist() {
	return haslist;
}

public void setHaslist(boolean haslist) {
	this.haslist = haslist;
}

public Set<DeviceSubGroup> getSubGroups() {
	return subGroups;
}

public Map<DeviceGroup, Set<DeviceGroup>> getGroups() {
	Map<DeviceGroup, Set<DeviceGroup>> groups = new HashMap<>();
	for (DgLink group : this.groups) {
		if(!groups.containsKey(group.getGroup())) {
			Set<DeviceGroup> subGroups = new HashSet<>();
			if(!group.getGroup().equals(group.getSubGroup()))
				subGroups.add(group.getSubGroup());
			groups.put(group.getGroup(),subGroups);
		} else if(!group.getGroup().equals(group.getSubGroup()))
			groups.get(group.getGroup()).add(group.getSubGroup());
	}
	return groups;
}

public void addGroups(Map<DeviceGroup, Set<DeviceGroup>> groups) {
	for (Entry<DeviceGroup, Set<DeviceGroup>> entity : groups.entrySet()) {
		DgLink dgLinkGroup = new DgLink(this, entity.getKey(), entity.getKey());
		if(!this.groups.contains(dgLinkGroup)) {
			this.groups.add(dgLinkGroup);
			entity.getKey().getDevicesLink().add(dgLinkGroup);
		}
		if(entity.getValue() != null)
			for (DeviceGroup subGroup : entity.getValue())
				if(!subGroup.equals(entity.getKey())) {
					DgLink dgLinkSubGroup = new DgLink(this, entity.getKey(), subGroup);
					if(!this.groups.contains(dgLinkSubGroup)) {
						this.groups.add(dgLinkSubGroup);
						entity.getKey().getDevicesLink().add(dgLinkSubGroup);
					}
				}
	}
}

public void removeGroups(Map<DeviceGroup, Set<DeviceGroup>> groups) {
	Set<DgLink> newSubGroups = new HashSet<>();
	Set<DgLink> newGroups = new HashSet<>();
	Set<DgLink> delGroups = new HashSet<>();
	for (Entry<DeviceGroup, Set<DeviceGroup>> entity : groups.entrySet()) {
		newSubGroups.add(new DgLink(this, entity.getKey(), entity.getKey()));
		if(entity.getValue() != null)
			for (DeviceGroup subGroup : entity.getValue())
				if(!subGroup.equals(entity.getKey()))
					newSubGroups.add(new DgLink(this, entity.getKey(), subGroup));
	}
	for (DgLink dgLink : this.groups)
		if(!newGroups.contains(dgLink) 
					|| !newSubGroups.contains(dgLink))
				delGroups.add(dgLink);
	for (DgLink dgLink : delGroups) {
		this.groups.remove(dgLink);
		dgLink.getGroup().getDevicesLink().remove(dgLink);
		dgLink.setDevice(null);
		dgLink.setGroup(null);
		dgLink.setSubGroup(null);
	}
}

public void addFamily(Family family) {
	if(families.add(family))
		family.getDevices().add(this);
}

public void removeFamily(Family family) {
	if(families.remove(family))
		family.getDevices().remove(this);
}

public ModelProperty getModelProperty(String tag) {
	for(ModelProperty modelProperty : nodeModel.getModelProperties())
		if(modelProperty.getTag().equals(tag))
			return modelProperty;
	return null;
}

public FamilyProperty getFamilyProperty(ModelProperty modelProperty) {
	if(modelProperty == null) return null;
	for(FamilyProperty familyProperty : modelProperty.getFamilyProperties())
		if(families.contains(familyProperty.getFamily()))
			return familyProperty;
	return null;
}

public FamilyProperty getFamilyProperty(String tag) {
	return getFamilyProperty(getModelProperty(tag));
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || !getClass().equals(o.getClass())) return false;
    Device device = (Device) o;
    return Objects.equals(name, device.name);
}

@Override
public int hashCode() {
    return Objects.hash(name);
}

@Override
public JsonElement toJson(User user) {
	return LTJsonModel.toJson(DeviceJson.class, this, user);
}

}
package lt.model;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import lt.LTPlugin;
import lt.controller.CacheController.CacheTemplate;
import lt.database.LTDBConfig;
import lt.ehcache.LTEhcache;

@LTPlugin
@Entity(name = “DeviceGroup”)
@Table(name = “lt_devicegroup”, schema = LTDBConfig.LTSchema)
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@LTEhcache(template = CacheTemplate.LONG_TIME_SMALL)
public class DeviceGroup extends LTTable {
private static final long serialVersionUID = -2496755204905638224L;

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

@Column(name = "description", columnDefinition = "TEXT", nullable = true)
private String description;

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

@OneToMany(cascade = { 
		CascadeType.PERSIST, 
		CascadeType.MERGE,
		CascadeType.REFRESH},
		fetch = FetchType.LAZY,
		mappedBy = "group", orphanRemoval = true)
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@LTEhcache(template = CacheTemplate.LONG_TIME)
private Set<DgLink> devices = new HashSet<>();

@ManyToMany(mappedBy = "groups")
private Set<NodeModel> models = new HashSet<>();

@OneToMany(cascade = { 
		CascadeType.PERSIST, 
		CascadeType.MERGE,
		CascadeType.REFRESH},
		fetch = FetchType.LAZY,
		mappedBy = "subGroup", orphanRemoval = true)
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@LTEhcache(template = CacheTemplate.LONG_TIME)
private Set<DeviceSubGroup> deviceSubGroups = new HashSet<>();

public String getName() {
	return name;
}

public Set<DgLink> getDevicesLink() {
	return devices;
}

public Set<Device> getDevices(){
	return this.devices.stream().map(dg -> dg.getDevice()).collect(Collectors.toSet());
}

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

public String getDescription() {
	return description;
}

public void setDescription(String description) {
	this.description = description;
}

public boolean isSubGroup() {
	return subGroup;
}

public void setSubGroup(boolean subGroup) {
	this.subGroup = subGroup;
}

public Set<NodeModel> getModels() {
	return models;
}

public void setDeviceSubGroup(Set<DeviceSubGroup> deviceSubGroups) {
	this.deviceSubGroups = deviceSubGroups;
}

public Set<DeviceSubGroup> getDeviceSubGroups() {
	return this.deviceSubGroups;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || !getClass().equals(o.getClass())) return false;
    DeviceGroup deviceGroup = (DeviceGroup) o;
    return Objects.equals(name, deviceGroup.name);
}

@Override
public int hashCode() {
    return Objects.hash(name);
}

}
package lt.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ManyToOne;
import javax.persistence.MapsId;
import javax.persistence.NamedNativeQuery;
import javax.persistence.Table;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import lt.LTPlugin;
import lt.controller.CacheController.CacheTemplate;
import lt.database.LTDBConfig;
import lt.ehcache.LTEhcache;

@LTPlugin
@NamedNativeQuery(
name = DgLink.cleanGroupsModel,
query = "SELECT " + LTDBConfig.LTSchema + “.clean_groups_model(:” + DgLink.modelID + “, :” + DgLink.subgroupsID + “)”)
@Entity(name = “DgLink”)
@Table(name = “lt_dglink”, schema = LTDBConfig.LTSchema)
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@LTEhcache(template = CacheTemplate.LONG_TIME)
public class DgLink implements Serializable {

private static final long serialVersionUID = 3928043474034772569L;
public static final String cleanGroupsModel = "cleanGroupsModel";
public static final String modelID = "_modelID";
public static final String subgroupsID = "_subgroupsIDs";

@EmbeddedId
private DgLinkId id;

@ManyToOne(fetch = FetchType.EAGER)
@MapsId("deviceId")
private Device device;

@ManyToOne(fetch = FetchType.EAGER)
@MapsId("groupId")
private DeviceGroup group;

@ManyToOne(fetch = FetchType.EAGER)
@MapsId("subGroupId")
private DeviceGroup subGroup;

@Column(name = "groupkey", length = 100, nullable = true)
private String groupKey = null;

public DgLink() {}

public DgLink(Device device, DeviceGroup group, DeviceGroup subGroup) {
	this.device = device;
	this.group = group;
	this.subGroup = subGroup;
	this.id = new DgLinkId(device.getId(), group.getId(), subGroup.getId());
}

public String getGroupKey() {
	return groupKey;
}

public void setGroupKey(String groupKey) {
	this.groupKey = groupKey;
}

public Device getDevice() {
	return device;
}

public DeviceGroup getGroup() {
	return group;
}

public DeviceGroup getSubGroup() {
	return subGroup;
}

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

public void setGroup(DeviceGroup group) {
	this.group = group;
}

public void setSubGroup(DeviceGroup subGroup) {
	this.subGroup = subGroup;
}

@Override
public boolean equals(Object o) {
	if (this == o) return true;
	if (o == null || getClass() != o.getClass()) return false;
	DgLink dgLink = (DgLink) o;
	return id.equals(dgLink.id);
}

@Override
public int hashCode() {
	return id.hashCode();
}

}
package lt.model;

import java.io.Serializable;
import java.util.Objects;

import javax.persistence.Column;
import javax.persistence.Embeddable;

@Embeddable
public class DgLinkId implements Serializable {

private static final long serialVersionUID = 3928043474034772569L;

@Column(name = "device_id")
private int deviceId;

@Column(name = "group_id")
private int groupId;

@Column(name = "subgroup_id")
private int subGroupId;

public DgLinkId() {}

public DgLinkId(int deviceId, int groupId, int subGroupId) {
	this.deviceId = deviceId;
	this.groupId = groupId;
	this.subGroupId = subGroupId;
}

@Override
public boolean equals(Object o) {
	if (this == o) return true;
	if (o == null || getClass() != o.getClass()) return false;
	DgLinkId dgLinkId = (DgLinkId) o;
	return Objects.equals(deviceId, dgLinkId.deviceId) &&
			Objects.equals(groupId, dgLinkId.groupId) &&
			Objects.equals(subGroupId, dgLinkId.subGroupId);
}

@Override
public int hashCode() {
	return Objects.hash(deviceId, groupId, subGroupId);
}

}

Table contents of lt_dglink

id . device_id group_id subgroup_id insert_date subgroupkey
34 991 2 2 “2019-07-26 11:01:00.601444” null
35 991 2 2986 “2019-07-26 11:01:00.601444” “10”
38 995 2 2 “2019-09-10 11:52:59.388448” null
39 995 2 2986 . “2019-09-10 11:53:22.582728” “5”
42 997 2 2 “2019-09-10 17:01:14.895989” null
44 997 2 2986 “2019-09-10 17:02:33.058153” “5”
45 938 2 2 “2019-09-11 17:41:32.774628” null
12 937 2 2 “2019-06-26 18:18:49.559259” null

Use instanceof instead of getClass.

if (o == null || !(o instanceof Device)) return false;