Reputation: 1695
Summary Question: Do different instances of a sub-class inherit the same parent class instance?
I would have thought that two instances of a sub-class also have different parent class instances, but perhaps I am not understanding something about inheritance. Hopefully someone can explain why I am seeing this behavior.
Here is the class where I see the "problem":
@Entity
@Table(name="inventory.parts_fstnr_capscrews")
public class FastenerCapScrew implements PartInterface {
...
private Dimension length;
private Dimension threadLength;
...
@ManyToOne
@JoinColumn(name="fk_lengthid")
@JsonView(View.CommodityPartPOView.class)
public Dimension getLength() {
return length;
}
public void setLength(Dimension length) {
this.length = length;
}
@ManyToOne
@JoinColumn(name="fk_threadlengthid")
@JsonView(View.CommodityPartPOView.class)
public Dimension getThreadLength() {
return threadLength;
}
public void setThreadLength(Dimension threadLength) {
this.threadLength = threadLength;
}
@Override
@Transient
public List<FiltersInterface> getFilters() {
List<FiltersInterface> filters = new ArrayList<>();
LOGGER.debug(filters.toString());
LOGGER.debug(length.toString());
LOGGER.debug(threadLength.toString());
if (length!=null) {
length.setDbColumnName("FK_LengthID");
filters.add(length);
}
LOGGER.debug(filters.toString());
LOGGER.debug(length.toString());
LOGGER.debug(threadLength.toString());
if (threadLength!=null) {
threadLength.setDbColumnName("FK_ThreadLengthID");
filters.add(threadLength);
}
LOGGER.debug(filters.toString());
LOGGER.debug(length.toString());
LOGGER.debug(threadLength.toString());
return filters;
}
}
And here is the Dimension class:
@Entity
@Table(name="utilities.dimensions")
public class Dimension extends FiltersExtension implements FiltersDimensionInterface {
...
}
And the extended class:
public class FiltersExtension {
protected String dbColumnName;
public String getDbColumnName() {
return dbColumnName;
}
public void setDbColumnName(String dbColumnName) {
this.dbColumnName = dbColumnName;
}
}
When I call the getFilters()
method in FastenersCapScrew
, the initial output for length
and threadLength
is as expected, and both have dbColumnName=null
. Then it runs length.setDbColumnName("FK_LengthID");
, but both length
and threadLength
are changed and both show dbColumnName=FK_LengthID
. Then it runs threadLength.setDbColumnName("FK_ThreadLengthID");
, and again both items are changed so that dbColumnName=FK_ThreadLengthID
.
Initially, I thought it must have something to do with the hashCode and equals methods in Dimension
, so I changed them to include dbColumnName
as below:
@Override
public int hashCode() {
LOGGER.debug("First compare hashCode with dbColumnName="+this.dbColumnName);
int hash = 3;
hash = 37 * hash + this.dimID;
hash = 37 * hash + Objects.hashCode(this.dbColumnName);
return hash;
}
@Override
public boolean equals(Object obj) {
LOGGER.debug("Now compare equals with dbColumnName="+this.dbColumnName);
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Dimension other = (Dimension) obj;
if (this.dimID != other.dimID) {
return false;
}
LOGGER.debug("Now compare the column name: "+this.dbColumnName+" vs. "+other.dbColumnName);
if (!Objects.equals(this.dbColumnName,other.dbColumnName)) {
return false;
}
return true;
}
Can anyone explain to me why changing one Dimension
instance changes the other one as well? And what would be the way to fix this so that I do have two totally separate instances? Thanks!
For what it is worth, I am using Java 8 and Spring Boot 2.0.3 with Hibernate, but I don't think that has any bearing on this problem.
Upvotes: 0
Views: 701
Reputation: 2006
Definitely two instances of a sub-class do not share memory for their parent fields. Maybe the cause of this behavior is just a Hibernate's cache. Hibernate does create a new instance of Dimension
for one of the fields of FastenerCapScrew
class loading it from cache instead. Try to enable logging of SQL-queries to investigate what happens when you call getFilters
method.
EDIT The simplest way to get different instances of essentially the same entity is to use defensive copying in setters. As long as you do not apply this technique to collections Hibernate should still be able to perform dirty checking since it compares objects by value. In contrast collections are compared by identity and dirty checking will not work for them.
Upvotes: 2