Reputation: 31
I have 3 classes extending an abstract base class, all with the same formal structure. They were all constructed and working properly with a Hibernate/Spring data harness and have been for a few months now in a production system. However, Hibernate does not support discriminator column multitenancy out of the box, so I have been working on the transition to EclipseLink 2.5.2, which supposedly does.
I've spent at least a day trying to get the following to work - and apologies if I can't list all of the configurations I've tried in that time, as they were legion. I'm hoping that this present configuration will at least target what's wrong for someone more familiar with EclipseLink than I am. Base class, minus accessors and constructors:
@Converters(
@Converter(name = "UUIDConverter", converterClass = UUIDToStringConverter.class)
)
@MappedSuperclass
@Table(name = "label_type_definitions")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "scope", columnDefinition = "varchar(20) not null")
public abstract class LabelTypeDefinition implements LabelType {
@Id
@Convert("UUIDConverter")
@Column(name = "id", length = 40, columnDefinition = "VARCHAR(40)")
@GeneratedValue(generator = "uuid")
@UuidGenerator(name = "uuid")
protected final UUID id;
@Column(name = "name", nullable = false)
protected String name;
@Column(name = "description")
protected String description;
...
}
The subclasses. Tree:
@Entity
@DiscriminatorValue(value = "TREE")
public class LabelTreeDefinition extends LabelTypeDefinition implements LabelTree {
....
}
Scenario:
@Entity
@DiscriminatorValue(value = "SCENARIO")
public class LabelScenarioDefinition extends LabelTypeDefinition implements LabelScenario {
...
}
Node:
@Entity
@DiscriminatorValue(value = "NODE")
public class LabelNodeDefinition extends LabelTypeDefinition implements LabelNode {
...
}
The LabelType interface extends Serializable and my own Identifiable and describes the accessor contract for the model (interfaces for models = not my design, a consequence of a separate Maven project for API baked in by my predecessor). The interfaces LabelTree/Node/Scenario all extend LabelType without adding to the contract.
The problem: when this configuration constructs my database, it ignores the table specification and does not build a scope column.
LABELTREEDEFINITION
id
name
description
LABELNODEDEFINITION
id
name
description
LABELSCENARIODEFINITION
id
name
description
To me this means it is completely ignoring the @Inheritance annotation and is doing so silently. I can force it to build all of that in and make it look superficially correct by adding @Table annotations to each subclass and including a scope column (the latter was how it was built when using Hibernate), but then of course all of my Spring Data queries ignore the discriminator.
Could all of this be a consequence of implementing the different interfaces? If so, that worries me as it seems extremely fragile. It seems to me this is all otherwise identical to what's described eg. here (http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Entities/Inheritance). Is there something I have missed or some quirk of EclipseLink I have overlooked?
Upvotes: 0
Views: 1073
Reputation: 31
OK, so I figured out the problem by painfully running through EclipseLink's code in my debugger. I happened to notice that when it was trying to assign the inheritance in org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor, it wasn't finding the superclass LabelTypeDefinition. This was not a problem for Hibernate, so it didn't occur to me that the abstract base class also had to be included in persistence.xml, ie.
<persistence-unit name="[MYPROJECT]" transaction-type="RESOURCE_LOCAL">
...
<class>com.[MYPROJECT].persistence.labeltype.LabelNodeDefinition</class>
<class>com.[MYPROJECT].persistence.labeltype.LabelTreeDefinition</class>
<class>com.[MYPROJECT].persistence.labeltype.LabelScenarioDefinition</class>
<class>com.[MYPROJECT].persistence.labeltype.LabelTypeDefinition</class>
...
</persistence-unit>
However, when I ran it through a second time I discovered it still could not find the class. As a Hail Mary, I replaced @MappedSuperclass with @Entity in my parent class and it all worked.
Now the first part of the fix was a little bit of a headslapper, ie. I should have thought of the possibility EclipseLink required more explicit configuration than Hibernate. However the second strikes me as a bug, particularly since I've found documentation in numerous places (including the eclipse wiki link I posted above) that suggests it should work. And because semantically it seems more correct: my abstract base class is not, in fact, an "entity" in that no LabelType objects ever get persisted as such. But it is a superclass that's "mapped" by my entities.
Upvotes: 2