代码之家  ›  专栏  ›  技术社区  ›  Tim

Java更改类的一个实例的属性也会更改另一个实例

  •  0
  • Tim  · 技术社区  · 7 年前

    摘要问题:子类的不同实例是否继承相同的父类实例?

    我本以为一个子类的两个实例也有不同的父类实例,但也许我对继承不太了解。希望有人能解释我为什么看到这种行为。

    这是我看到“问题”的课堂:

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

    下面是维度类:

    @Entity
    @Table(name="utilities.dimensions")
    public class Dimension extends FiltersExtension implements FiltersDimensionInterface {
        ...
    }
    

    扩展类:

    public class FiltersExtension {
        protected String dbColumnName;
    
        public String getDbColumnName() {
            return dbColumnName;
        }
    
        public void setDbColumnName(String dbColumnName) {
            this.dbColumnName = dbColumnName;
        }
    }
    

    当我呼唤 getFilters() 方法在 FastenersCapScrew ,的初始输出 length threadLength 如预期,两者都有 dbColumnName=null . 然后运行 length.setDbColumnName("FK_LengthID"); 但是 二者都 长度 线程长度 都变了 dbColumnName=FK_LengthID . 然后运行 threadLength.setDbColumnName("FK_ThreadLengthID"); 又一次 二者都 项目已更改,因此 dbColumnName=FK_ThreadLengthID .

    最初,我认为它必须与中的哈希代码和equals方法有关。 Dimension ,所以我把它们改为 dbColumnName 如下:

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

    有人能告诉我为什么要换一个吗 维数 实例也更改了另一个?如果我有两个完全独立的实例,那么解决这个问题的方法是什么?谢谢!

    对于它的价值,我使用Java 8和Spring Bug 2.0.3与Hibernate,但我不认为这个问题有任何关系。

    1 回复  |  直到 7 年前
        1
  •  2
  •   briarheart    7 年前

    子类的两个实例肯定不会为其父字段共享内存。可能导致这种行为的原因只是休眠的缓存。Hibernate确实创建了 Dimension 其中一个领域 FastenerCapScrew 类从缓存加载它。尝试启用SQL查询日志记录以调查调用 getFilters 方法。

    编辑 获得本质上相同实体的不同实例的最简单方法是在setter中使用防御性复制。只要不将此技术应用于集合,Hibernate应该仍然能够执行脏检查,因为它按值比较对象。相反,集合是按标识进行比较的,脏检查对它们不起作用。