eclipselink asking for class indicator field when using InheritanceType.TABLE_PER_CLASS

1k Views Asked by At

I already spend a few hours in trying to get that work. I'm using the history policy to create a full historization for some of my tables. This is defined in an abstract class. Then I have an normal entity implementing this class and defining it's field. Then I wanted to use a class which is inherting from the upper class, but it's table is set to the history table. That worked perfectly, the only problem was, that when I did a query on the historized table(lasso_warehandling_entry), it always returned me results from the history entity (lasso_warehandling_entry_hist). So I added the line descriptor.getInheritancePolicy().setShouldReadSubclasses(false); which i read somewhere, that it should solve my problem. Unfortunately it doesn't. Now I always get following message:

Exception Description: The descriptor [RelationalDescriptor(dao.LassoWarehandlingEntry --> [DatabaseTable(lasso_warehandling_entry)])] has been set to use inheritance, but a class indicator field has not been defined. 
When using inheritance, a class indicator field or class extraction method must be set. 
Parent Descriptor: [RelationalDescriptor(org.rorotec.lasso.dao.LassoWarehandlingEntry --> [DatabaseTable(lasso_warehandling_entry)])]
Descriptor: RelationalDescriptor(dao.LassoWarehandlingEntry --> [DatabaseTable(lasso_warehandling_entry)])

Since we use a seperate table for each entity, it doesn't make a lot of sense to use an indicator field. Anyway, I just don't manage to get this message away. Anybody has any idea how I should solve that? The code looks like that:

@MappedSuperclass
@Customizer(abstractDao.HistoryCustomizer.class)
public abstract class AbstractAuditedOzlEntity {
// defined the columns all the autited classes have
...
}

public class HistoryCustomizer implements DescriptorCustomizer {

    public void customize(ClassDescriptor descriptor) {
        HistoryPolicy policy = new HistoryPolicy();
        policy.addHistoryTableName(descriptor.getTableName()  + "_hist");
        policy.addStartFieldName("start_date");
        policy.addEndFieldName("end_date");
        descriptor.setHistoryPolicy(policy);

        // This here I added afterwards, as described in the text, and is the reason for the error message i get
        descriptor.getInheritancePolicy().setShouldReadSubclasses(false);
     }
}

@Entity
// when i define the inhertiancetype already in the abstract class, it doesn't seem to have any influence, so I added it here.
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
@Table(name = "lasso_warehandling_entry")
public class LassoWarehandlingEntry extends AbstractAuditedOzlEntity implements Serializable {
// define the specific stuff of this table
... 

}

@Entity
@Table(name = "lasso_warehandling_entry_hist")
@AttributeOverride(name="id", column=@Column(name="hist_id"))
public class LassoWarehandlingEntryHist extends LassoWarehandlingEntry {
...
// add the columns which only exist in the history tables like end_date
}
2

There are 2 best solutions below

0
On

I am facing the same issue as yours.

The error message is saying that EclipseLink is unable to differentiate whether you are querying LassoWarehandlingEntry or LassoWarehandlingEntryHist. (a class indicator field or class extraction method must be set.) Hence, if you query LassoWarehandlingEntry, EclipseLink will query both LassoWarehandlingEntry and LassoWarehandlingEntryHist. So I guess that you are trying to use

descriptor.getInheritancePolicy().setShouldReadSubclasses(false);

Unfortunately, since we are using InheritanceType.TABLE_PER_CLASS, it seems that we cannot use discriminator annotations (@DiscriminatorColumn and @DiscriminatorValue). These 2 annotations are only for InheritanceType.SINGLE_TABLE and InheritanceType.JOINED. ClassExtractor does not work either. More info at https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Entities/Inheritance

Anyway, I solved this issue by creating an abstract class which will be inherited by both concrete classes. In your case:


@MappedSuperclass
public abstract class AbstractLassoWarehandlingEntry extends AbstractAuditedOzlEntity implements Serializable {
// define the specific stuff of this table
... 

}


@Entity
@Table(name = "lasso_warehandling_entry")
public class LassoWarehandlingEntry extends AbstractLassoWarehandlingEntry implements Serializable {
// Empty class as the content is already defined in AbstractLassoWarehandlingEntry

}


@Entity
@Table(name = "lasso_warehandling_entry_hist")
@AttributeOverride(name="id", column=@Column(name="hist_id"))
public class LassoWarehandlingEntryHist extends AbstractLassoWarehandlingEntry {
...
// add the columns which only exist in the history tables like end_date
}

By using this approach, you can remove the following line from your HistoryCustomizer class.


descriptor.getInheritancePolicy().setShouldReadSubclasses(false);

Now, if you query LassoWarehandlingEntry, EclipseLink will just execute 1 query to LassoWarehandlingEntry table.

Hope that this helps!

0
On

This should help:

if (descriptor.hasInheritance()) {
    descriptor.getInheritancePolicy().setShouldReadSubclasses(false);
}

From getInheritancePolicy() documentation:

Caution must be used in using this method as it lazy initializes an inheritance policy.

Calling this on a descriptor that does not use inheritance will cause problems, hasInheritance() must always first be called.