4

介绍

我有一个我觉得很神秘的问题。我在 Google 和 StackOverflow 上搜索过,没有发现任何人有类似的问题。我尝试将持久性提供程序切换到 Hibernate,但我们的代码过于依赖 EclipseLink 特性,无法使其成为调试的实用选项。如果这个问题仍然存在(哈,哈;Java EE 双关语),我很可能会为 Hibernate 重新编写所有持久性代码,因为它有帮助。

精简版:

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

细节

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

实验块.java

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

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

&C。

所以我ExperimentBlock.getWithIdsBySchedule在会话 bean 中运行查询。我调试,在运行该查询后立即设置断点,并检查结果。在查询结果中,我得到一个 [ ExperimentBlock, Integer] 列表—— ExperimentBlocksuniqueID都是空的;但是,整数(手动查询uniqueID的 s 的ExperimentBlocks)都已正确填充。

结论

有任何想法吗?这真的可以帮助我摆脱困境。再次,让我知道我是否可以提供更多信息。

附加物

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

1 回答 1

4

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

你可以尝试一些事情来缩小问题的范围,

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

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

于 2012-09-17T14:43:38.653 回答