EclipseLink返回具有null主键的实体

时间:2012-09-14 17:25:14

标签: java-ee jpa jpa-2.0 eclipselink java-ee-6

简介

我有一个问题,我发现它非常神秘。我在谷歌和StackOverflow上搜索过,没有发现任何人有类似的问题。我尝试将持久性提供程序切换到Hibernate,但是我们的代码过于依赖EclipseLink特性来使其成为调试的实用选项。如果这个问题持续存在(ha,ha; Java EE双关语),我可能会在它有帮助的偶然事件中为Hibernate重写所有持久性代码。

短版:

  1. 我的实体ExperimentBlock正在被正确保存到数据库中,并且正在正确生成其自动增量主键。但是,当从数据库中读取时,EclipseLink为我提供了具有空主键字段的实体。这在生产软件中造成了毁灭性的错误。
  2. ExperimentBlock从其超类PeriodResident继承其带有注释,getter和setter的主键字段。 PeriodResident的其他几个子类也是如此,但是EclipseLink正确加载了它们的主键。这是这个错误的谜团:完全相同的字段/ getter / setter代码用于PeriodResident的每个子类,但是ExperimentBlock是EclipseLink返回的唯一一个带有明确的主键的!
  3. 运行选择ExpermentBlock的JPQL查询会返回空主键。 (Sadface。)但是,运行专门选择ExperimentBlock的主键字段的JPQL查询可以正常工作,并返回整数主键列表!这可以在下面的查询ExperimentBlock.findWithIdsBySchedule中看到。
  4. 详细

    GlassFish 3.1.2,EclipseLink 2.4.0,Java EE 6,MySQL 5.0.95-log

    我没有得到任何例外。远程和本地会话bean中会出现此问题。

    代码如下,如果我需要提供更多详细信息,请告诉我。

    PeriodResident.java

    (问题的超类ExperimentBlock。这是定义主键字段 - uniqueID - 的地方)

    package ohno;
    
    import ohno.ShiftDateConverter;
    import ohno.Shift;
    import ohno.Facility;
    import java.io.Serializable;
    import java.util.Date;
    import javax.persistence.Basic;
    import javax.persistence.Column;
    import javax.persistence.DiscriminatorColumn;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Inheritance;
    import javax.persistence.InheritanceType;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne;
    import javax.persistence.Table;
    import org.eclipse.persistence.annotations.Convert;
    import org.eclipse.persistence.annotations.Converter;
    
    @Converter(converterClass=ShiftDateConverter.class, name="ShiftDateConverter")
    @Entity
    @Inheritance(strategy = InheritanceType.JOINED)
    @Table(name="bs_blocks")
    @DiscriminatorColumn(name="block_type")
    public abstract class PeriodResident implements Serializable {
        private static final long serialVersionUID = 1L;
    
        public PeriodResident() {
        }
    
        /**
         * Sets the database unique id.
         *
         * @param uniqueID the new database unique id
         */
        public void setUniqueID(Integer uniqueID) {
            this.uniqueID = uniqueID;
        }
    
        /**
         * Provides a useful super-constructor handling initialization of the first
         * and last Shifts of the PeriodResident's time block, and the Facility
         * where it takes place.
         *
         * @param startShift The first Shift in the PeriodResident's time block.
         * @param endShift The last Shift in the PeriodResident's time block.
         * @param facility The Facility where the PeriodResident takes place.
         * @param uniqueID the unique id
         */
        public PeriodResident(Shift startShift, Shift endShift, Facility facility,
                int uniqueID) {
            this.startShift = startShift;
            this.endShift = endShift;
            this.facility = facility;
            this.uniqueID = uniqueID;
        }
    
        /**
         * Instantiates a new period resident.
         *
         * @param startShift The first Shift in the PeriodResident's time block.
         * @param endShift The last Shift in the PeriodResident's time block.
         * @param facility The Facility where the PeriodResident takes place.
         */
        public PeriodResident(Shift startShift, Shift endShift, Facility facility) {
            this(startShift, endShift, facility, null);
        }
    
        /**
         * Gets the facility.
         *
         * @return the Facility where the PeriodResident takes place.
         *
         * @see #setFacility(Facility)
         */
    
        public Facility getFacility() {
            return facility;
        }
    
        /**
         * Gets the shifts.
         *
         * @return the total number of Shifts in the PeriodResident's time block.
         *
         * @see #getStartShift()
         * @see #getEndShift()
         */
    
        public int getShifts() {
            return endShift.getShiftsDifference(startShift);
        }
    
        /**
         * Gets the start shift.
         *
         * @return the first <code>Shift</code> in the PeriodResident's time
         * block.
         *
         * @see #setStartShift(Shift)
         * @see #getShifts()
         */
        public Shift getStartShift(){
            return startShift;
        }
    
        /**
         * Gets the end shift.
         *
         * @return the last <code>Shift</code> in the PeriodResident's time block.
         *
         * @see #setEndShift(Shift)
         * @see #getShifts()
         */
        public Shift getEndShift(){
            return endShift;
        }
    
        /**
         * Gets the database table unique ID for this particular PeriodResident, or null if
         * it hasn't one.
         *
         * @return this PeriodResident's unique ID in the database.
         */
    
        public Integer getUniqueID() {
            return uniqueID;
        }
    
        /**
         * sets this PeriodResident's facility location with the given Facility.
         *
         * @param facility the PeriodResident's new Facility.
         *
         * @see #getFacility()
         */
        public void setFacility(Facility facility) {
            this.facility = facility;
        }
    
        /**
         * sets this PeriodResident's time block's new starting shift with the given
         * Shift.
         *
         * @param startShift the new starting shift.
         *
         * @see #getStartShift()
         */
        public void setStartShift(Shift startShift) {
            this.startShift = startShift;
        }
    
        /**
         * sets this PeriodResident's time block's new ending shift with the given
         * Shift.
         *
         * @param endShift the new ending shift.
         *
         * @see #getEndShift()
         */
        public void setEndShift(Shift endShift) {
            this.endShift = endShift;
        }
    
    
        public Schedule getSchedule() {
            return schedule;
        }
    
        public void setSchedule(Schedule schedule) {
            this.schedule = schedule;
        }
    
        protected void copyCharacteristics(PeriodResident original, PeriodResident clone) {
            clone.setStartShift(original.getStartShift().clone());
            clone.setEndShift(original.getEndShift().clone());
            clone.setFacility(original.getFacility());
            clone.setSchedule(original.getSchedule());
        }
    
    
        /* (non-Javadoc)
         * @see java.lang.Object#toString()
         */
        @Override
        public String toString() {
            return "PeriodResident  (Facility: " + facility + ") from " + startShift + " to " + endShift;
        }
    
        @Override
        public abstract Object clone();
    
        public abstract String getResidentType();
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
    
            final PeriodResident other = (PeriodResident) obj;
            if ((uniqueID == null && other.uniqueID != null) || (this.uniqueID != null && !this.uniqueID.equals(other.uniqueID))) {
                return false;
            }
            return true;
        }
    
        @Override
        public int hashCode() {
            if (uniqueID != null) {
                return uniqueID;
            }
            int hash = 7;
            hash = 73 * hash + (this.uniqueID != null ? this.uniqueID.hashCode() : 0);
            hash = 73 * hash + (this.startShift != null ? this.startShift.hashCode() : 0);
            hash = 73 * hash + (this.endShift != null ? this.endShift.hashCode() : 0);
            hash = 73 * hash + (this.facility != null ? this.facility.hashCode() : 0);
            hash = 73 * hash + (this.schedule != null ? this.schedule.hashCode() : 0);
            hash = 73 * hash + getClass().hashCode();
            return hash;
        }
    
        /* (non-Javadoc)
         * @see java.lang.Object#equals(java.lang.Object)
         */
        public boolean equivalent(PeriodResident other) {
            if (this == other) {
                return true;
            }
            if (other == null) {
                return false;
            }
            if (getClass() != other.getClass()) {
                return false;
            }
            if (endShift == null) {
                if (other.endShift != null) {
                    return false;
                }
            } else if (!endShift.equals(other.endShift)) {
                return false;
            }
            if (facility == null) {
                if (other.facility != null) {
                    return false;
                }
            } else if (!facility.equals(other.facility)) {
                return false;
            }
            if (startShift == null) {
                if (other.startShift != null) {
                    return false;
                }
            } else if (!startShift.equals(other.startShift)) {
                return false;
            }
            if (schedule == null) {
                if (other.getSchedule() != null) {
                    return false;
                }
            } else if (!schedule.equals(other.getSchedule())) {
                return false;
            }
            return true;
        }
    
    
        public Date getStartShiftDate() {
            return getStartShift().getTime();
        }
    
        public Date getEndShiftDate() {
            return getEndShift().getTime();
        }
    
        /** The database unique id. */
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Basic(optional = false)
        @Column(name = "id")
        Integer uniqueID;
    
        /** The start shift of the PeriodResident. */
        @Column(name = "start_time")
        @Convert(value="ShiftDateConverter")
        Shift startShift;
    
        /** The end shift of the PeriodResident.*/
        @Column(name = "end_time")
        @Convert(value="ShiftDateConverter")
        Shift endShift;
    
        /** The facility where the PeriodResident lives. */
        @ManyToOne
        @JoinColumn(name = "facility", referencedColumnName = "ID")
        Facility facility;
        @ManyToOne
        @JoinColumn(name = "schedule", referencedColumnName = "ID")
        private Schedule schedule;
    }
    

    ExperimentBlock.java

    (问题类...注意最后一个JPQL查询,它选择ExperimentBlock及其uniqueID。运行此查询时,ExperimentBlock.uniqueId为空,但是{{ 1}}手动选择是正确的。)

    uniqueID

    &安培; C。

    所以我在会话bean中运行查询package ohno; import ohno.Experiment; import ohno.Role; import java.io.Serializable; import javax.persistence.DiscriminatorValue; import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; import javax.persistence.Transient; /** * * @author Nick */ @Entity @Table(name = "bs_blocks_experiment") @NamedQueries({ @NamedQuery(name = "ExperimentBlock.findAll", query = "SELECT e FROM ExperimentBlock e"), @NamedQuery(name = "ExperimentBlock.findActualByExperiment", query = "SELECT e FROM ExperimentBlock e WHERE e.experiment = :experiment AND e.schedule.scheduleProperties.revision = :active ORDER BY e.schedule.id DESC, e.uniqueID DESC"), @NamedQuery(name = "ExperimentBlock.findByTime", query = "SELECT e FROM ExperimentBlock e WHERE e.startShift < :endTime AND e.endShift > :startTime and e.schedule = :schedule"), @NamedQuery(name = "ExperimentBlock.findWithIdsBySchedule", query = "SELECT e, e.uniqueID FROM ExperimentBlock e WHERE e.schedule = :schedule")}) @DiscriminatorValue(value = "EXPERIMENT_BLOCK") public class ExperimentBlock extends PeriodResident implements Serializable { private static final long serialVersionUID = 1L; // ######### // Some columns are inherited from PeriodResident! // ######### @Embedded protected Equipment equipment; @ManyToOne @JoinColumn(name = "experiment", referencedColumnName = "exp_id") private Experiment experiment; public ExperimentBlock() { } public Equipment getEquipment() { return equipment; } public void setEquipment(Equipment equipment) { this.equipment = equipment; } public Experiment getExperiment() { return experiment; } public void setExperiment(Experiment experiment) { this.experiment = experiment; } @Override public boolean equivalent(PeriodResident pr) { if (!super.equivalent(pr)) { return false; } ExperimentBlock other = (ExperimentBlock) pr; if (!getExperiment().equals(other.getExperiment())) { return false; } if (!getEquipment().equals(other.getEquipment())) { return false; } return true; } @Override public ExperimentBlock clone() { ExperimentBlock clone = new ExperimentBlock(); copyCharacteristics(this, clone); clone.setExperiment(getExperiment()); clone.setEquipment(equipment.clone()); return clone; } @Override public String getResidentType() { return RESIDENT_TYPE; } @Transient public static final String RESIDENT_TYPE = "ExperimentBlock"; @Override public String toString() { return "Experiment " + experiment + "\nChannel: " + getFacility() + "\nEquipment: " + getEquipment() + "\nFrom " + getStartShift() + "\nto " + getEndShift(); } /** * Gets an HTML string representation of this. * * @return the string */ public String toFancyString() { return "<html>Experiment <b>" + (experiment.isNoExperiment() ? "N/A" : experiment) + "</b><br>Channel: " + getFacility() + "<br>Equipment: " + getEquipment() + "<br>From: <b>" + getStartShift().toString(Role.BEGINNING) + "</b><br>To: <b>" + getEndShift().toString(Role.ENDING) + "</b><br>Shift Count: <b>" + getShifts() + "</b></html>"; } } 。我调试,在运行该查询后立即放入断点,并检查结果。在查询结果中,我得到了[ExperimentBlock.getWithIdsByScheduleExperimentBlock]的列表 - Integer s'ExperimentBlock都是空的;但是,整数 - uniqueID的手动查询uniqueID - 都已正确填充。

    结论

    有什么想法吗?这真的可以帮助我摆脱困境。再次,如果我能提供更多信息,请告诉我。

    附加物

    • Hackish(即“cludge”)这个问题的解决方案绝对是受欢迎的,也是优雅的解决方案。
    • 我检查了数据库(表定义,外键等),找不到有关ExperimentBlock的任何特殊内容可以解释我的问题。
    • (来自以下评论:)正确加载ExperimentBlock的所有其他持久属性。使用ExperimentBlock加载ExperimentBlock会导致同样的问题。 (上面,我从JPQL查询加载它,并作为不同类的属性em.find()加载。)

1 个答案:

答案 0 :(得分:4)

奇。兄弟姐妹必须与这个对象有所不同。

您可以尝试缩小问题范围,

  • 对id列(以及所有其他列/表)使用大写“ID”
  • 尝试禁用编织,或“eclipselink.weaving.internal”
  • 尝试删除你的clone / equals / hashCode方法

还启用日志记录,并包括为查询生成的SQL和完整的异常堆栈跟踪。