我有一个问题,我发现它非常神秘。我在谷歌和StackOverflow上搜索过,没有发现任何人有类似的问题。我尝试将持久性提供程序切换到Hibernate,但是我们的代码过于依赖EclipseLink特性来使其成为调试的实用选项。如果这个问题持续存在(ha,ha; Java EE双关语),我可能会在它有帮助的偶然事件中为Hibernate重写所有持久性代码。
ExperimentBlock
正在被正确保存到数据库中,并且正在正确生成其自动增量主键。但是,当从数据库中读取时,EclipseLink为我提供了具有空主键字段的实体。这在生产软件中造成了毁灭性的错误。ExperimentBlock
从其超类PeriodResident
继承其带有注释,getter和setter的主键字段。 PeriodResident
的其他几个子类也是如此,但是EclipseLink正确加载了它们的主键。这是这个错误的谜团:完全相同的字段/ getter / setter代码用于PeriodResident
的每个子类,但是ExperimentBlock
是EclipseLink返回的唯一一个带有明确的主键的!ExpermentBlock
的JPQL查询会返回空主键。 (Sadface。)但是,运行专门选择ExperimentBlock
的主键字段的JPQL查询可以正常工作,并返回整数主键列表!这可以在下面的查询ExperimentBlock.findWithIdsBySchedule
中看到。GlassFish 3.1.2,EclipseLink 2.4.0,Java EE 6,MySQL 5.0.95-log
我没有得到任何例外。远程和本地会话bean中会出现此问题。
代码如下,如果我需要提供更多详细信息,请告诉我。
(问题的超类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;
}
(问题类...注意最后一个JPQL查询,它选择ExperimentBlock
及其uniqueID
。运行此查询时,ExperimentBlock.uniqueId
为空,但是{{ 1}}手动选择是正确的。)
uniqueID
所以我在会话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.getWithIdsBySchedule
,ExperimentBlock
]的列表 - Integer
s'ExperimentBlock
都是空的;但是,整数 - uniqueID
的手动查询uniqueID
- 都已正确填充。
有什么想法吗?这真的可以帮助我摆脱困境。再次,如果我能提供更多信息,请告诉我。
ExperimentBlock
的任何特殊内容可以解释我的问题。ExperimentBlock
的所有其他持久属性。使用ExperimentBlock
加载ExperimentBlock
会导致同样的问题。 (上面,我从JPQL查询加载它,并作为不同类的属性em.find()
加载。)答案 0 :(得分:4)
奇。兄弟姐妹必须与这个对象有所不同。
您可以尝试缩小问题范围,
还启用日志记录,并包括为查询生成的SQL和完整的异常堆栈跟踪。