Introduction
I have a problem that I find very mysterious. I searched on Google and StackOverflow, and did not find anyone who had a similar problem. I tried switching our persistence provider to Hibernate, but our code is too dependent on the features of EclipseLink to make it a practical debugging option. If this problem persists (ha, ha; Java EE pun), I can very well rewrite all the persistence code for Hibernate in case this helps.
Short version:
- My
ExperimentBlock entity is correctly stored in the database, and its primary auto-increment key is created correctly. However, when it is read from the database, EclipseLink provides me with an object with a zero primary key field. This causes a devastating error in production software. ExperimentBlock inherits its primary key field with annotations, the receiver and the installer from its superclass PeriodResident . Several other subclasses of PeriodResident do the same, but EclipseLink loads its primary key correctly. This is the secret of this error: for each subclass of PeriodResident , the same field / getter / setter code is used, but ExperimentBlock is the only one that returns an EclipseLink with a zero primary key!- Running a JPQL query that selects
ExpermentBlock returns them with zero primary keys. (Sadface.) However, executing a JPQL query that specifically selects the ExperimentBlock primary key field works correctly and returns a list of integer primary keys! This can be seen in the ExperimentBlock.findWithIdsBySchedule request below.
More details
GlassFish 3.1.2, EclipseLink 2.4.0, Java EE 6, MySQL 5.0.95-log
I do not get any exceptions. This issue occurs in a remote and local beans session.
The code should, please let me know if I should provide more details.
PeriodResident.java
(The superclass of the ExperimentBlock task. Here the primary key field is defined - 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() { } public void setUniqueID(Integer uniqueID) { this.uniqueID = uniqueID; } public PeriodResident(Shift startShift, Shift endShift, Facility facility, int uniqueID) { this.startShift = startShift; this.endShift = endShift; this.facility = facility; this.uniqueID = uniqueID; } public PeriodResident(Shift startShift, Shift endShift, Facility facility) { this(startShift, endShift, facility, null); } public Facility getFacility() { return facility; } public int getShifts() { return endShift.getShiftsDifference(startShift); } public Shift getStartShift(){ return startShift; } public Shift getEndShift(){ return endShift; } public Integer getUniqueID() { return uniqueID; } public void setFacility(Facility facility) { this.facility = facility; } public void setStartShift(Shift startShift) { this.startShift = startShift; } 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()); } @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; } 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(); } @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "id") Integer uniqueID; @Column(name = "start_time") @Convert(value="ShiftDateConverter") Shift startShift; @Column(name = "end_time") @Convert(value="ShiftDateConverter") Shift endShift; @ManyToOne @JoinColumn(name = "facility", referencedColumnName = "ID") Facility facility; @ManyToOne @JoinColumn(name = "schedule", referencedColumnName = "ID") private Schedule schedule; }
ExperimentBlock.java
(Problem class ... note the last JPQL query that selects ExperimentBlock and its uniqueID . When this query is run, ExperimentBlock.uniqueId is null, and uniqueID is manually selected.)
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; @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;
&. WITH
So, I run the ExperimentBlock.getWithIdsBySchedule request in a bean session. I debug, set a breakpoint immediately after running this query and check the results. In the query results, I get the list [ ExperimentBlock , Integer ] - ExperimentBlock s' uniqueID - all null values; however, Integers - the manually requested uniqueID ExperimentBlock - are populated correctly.
Conclusion
Any ideas? That would really help me get rid. Again, let me know if there is more information that I could provide.
Addenda
- Hackish (namely, "cludge") solutions to this problem are certainly welcome, as well as elegant solutions.
- I checked the database (table definitions, foreign keys, etc.) and did not find anything special in
ExperimentBlock that could explain my problem. - (From the comment below :) All other persistent
ExperimentBlock properties load correctly. Loading ExperimentBlock with em.find() leads to the same problem. (Above, I loaded it from a JPQL query and as a property of another class, Schedule .)