EclipseLink/JPA: Multiple #DiscriminatorColumn annotations for Entity - java

I'm looking for a way in EclipseLink to have two #DiscriminatorColumns on the same entity
My PostreSQL DB table is:
Dictionary
{
id,
object_type,
attribute_type,
translation
}
And classes are:
#Entity
#Table(name = "dictionary")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="object_type",
discriminatorType=DiscriminatorType.INTEGER)
public class DictionaryRow implements Serializable;
#Entity
#DiscriminatorValue("0")
#DiscriminatorColumn(name="info_type",
discriminatorType=DiscriminatorType.INTEGER)
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class DictionaryAttribute extends DictionaryRow;
#Entity
#DiscriminatorValue("1")
public class DictionaryAttributeName extends DictionaryAttribute;
What I'm trying to achieve is that when I call for DictionaryAttributeName it will be resolved to SQL like:
select * from DICTIONARY where info_type = 1 and object_type = 0
But actually, it takes the DiscriminatorColumn from the DictionaryRow class, and DiscriminatorValue from the DictionaryAttributeName, resulting in the totally wrong SQL:
select * from DICTIONARY where object_type = 1
Is there a solution for this issue?
Thanks

According to the JPA 2.0 specification, this is not possible:
11.1.10 DiscriminatorColumn Annotation
For the SINGLE_TABLE mapping
strategy, and typically also for the
JOINED strategy, the persistence
provider will use a type discriminator
column. The DiscriminatorColumn
annotation is used to define the
discriminator column for the
SINGLE_TABLE and JOINED
inheritance mapping strategies.
The strategy and the discriminator column are only specified in the root
of an entity class hierarchy or
subhierarchy in which a different
inheritance strategy is applied.
References
JPA 2.0 Specification
Section 11.1.10 "DiscriminatorColumn Annotation"

Related

Using TYPE to select only specific class with Inheritance.table_per_class stategy

Imagine the following scenario where we use inheritance strategy TABLE_PER_CLASS and Template as superclass while Product as subclass.
Template:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Table(name = "Templates")
#NamedQuery(name = "getAllTemplates", query = "SELECT t FROM Template t")
public class Template implements Serializable { ...}
Product:
#Entity
#Table(name = "Product")
public class Product extends Template implements Serializable { ... }
In this scenario even thought i have in my DB 2 templates and 1 product. Whenever i call the Template namedQuery i retrieve both Products and Templates alike.
I tried do something like so:
SELECT t FROM Template t WHERE TYPE(t) = Template
However it returns the following error:
The class does not have a descriptor, or a descriptor that does not use inheritance or uses a ClassExtractor for inheritance
Is there a way to only get the Templates?
The TYPE operator does not work for sub classes either when using TABLE_PER_CLASS. There seems not to be explicit information about using TYPE with TABLE_PER_CLASS.
However there are lots of posts saying that this strategy is inefficient and not exactly recommended to be used.
JPA 2.1 specifications say about TABLE_PER_CLASS:
Support for the TABLE_PER_CLASS mapping strategy is optional in this release.
This means also that support might be only partial: like TYPE support not implemented.
Also there are some posts that indicate that not only Hibernate suffers from this, see (some pretty old, but still), like: this and this.
So as a conclusion:
change to SINGLE_TABLE or JOINED strategy if possible
live with it
In Hibernate you could also try to get it working with #DiscriminatorColumn and adding the column to Template entity but I personally think it is not worth the effort.

Hibernate WrongClassException for Custom Discriminators

I have a concrete JPA entity superclass mapped with the InheritanceType.JOINED using discriminator columns, and have a couple subclasses entities that extend this superclass with additional properties.
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "TYPE")
public class BaseEntity {
// . . .
}
#Entity
#DiscriminatorValue("SUBTYPE")
public class SubclassEntity extends BaseEntity {
// . . .
}
There are cases where I want to specify additional discriminator values without having to explicitly define a subclass for every type (that is, not every "BaseEntity" specifies additional properties that warrant a subclass / separate table). This strategy works fine in the database design as well as the Java class hierarchy, however, Hibernate JPA does not allow this and throws a WrongClassException because there isn't a subclass mapped to the discriminator:
Caused by: org.hibernate.WrongClassException: Object [id=entity-1] was not of the specified subclass [com.so.jpa.BaseEntity] : Discriminator: custom-1
at org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl.getConcreteEntityTypeName(EntityReferenceInitializerImpl.java:415)
at org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl.hydrateEntityState(EntityReferenceInitializerImpl.java:217)
at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.readRow(AbstractRowReader.java:90)
at org.hibernate.loader.plan.exec.internal.EntityLoadQueryDetails$EntityLoaderRowReader.readRow(EntityLoadQueryDetails.java:238)
at org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractResults(ResultSetProcessorImpl.java:112)
at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:121)
at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:85)
at org.hibernate.loader.entity.plan.AbstractLoadPlanBasedEntityLoader.load(AbstractLoadPlanBasedEntityLoader.java:167)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3954)
at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:488)
at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:453)
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:196)
at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:258)
at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:134)
at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1071)
at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:990)
at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:632)
at org.hibernate.type.EntityType.resolve(EntityType.java:424)
at org.hibernate.engine.internal.TwoPhaseLoad.doInitializeEntity(TwoPhaseLoad.java:154)
at org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:128)
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:1132)
at org.hibernate.loader.Loader.processResultSet(Loader.java:992)
at org.hibernate.loader.Loader.doQuery(Loader.java:930)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:336)
at org.hibernate.loader.Loader.doList(Loader.java:2611)
at org.hibernate.loader.Loader.doList(Loader.java:2594)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2423)
at org.hibernate.loader.Loader.list(Loader.java:2418)
at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:501)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:371)
at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:220)
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1268)
at org.hibernate.internal.QueryImpl.list(QueryImpl.java:87)
at org.hibernate.jpa.internal.QueryImpl.list(QueryImpl.java:567)
at org.hibernate.jpa.internal.QueryImpl.getResultList(QueryImpl.java:436)
...
In this case, I want Hibernate to return the concrete base entity BaseEntity rather than trying to instantiate a subclass. I don't see anything in the JPA spec (JSR 338) that indicates this shouldn't be possible (although the spec doesn't explicitly call out this scenario either).
Is there any way to allow JPA/Hibernate to allow custom discriminator types without requiring subclasses?
Unfortunately Hibernate expects exactly one discriminator value per entity type. And I guess that there is no difference to other JPA providers, as you can't define more than one DiscriminatorValue for an entity class.
Even if you define no DiscriminatorValue, there will be exactly one:
If the DiscriminatorValue annotation is not
specified and a discriminator column is used, a
provider-specific function will be used to generate a value
representing the entity type. If the DiscriminatorType is
STRING, the discriminator value default is the
entity name.
(excerpt from the JavaDoc of DiscriminatorValue)
But you can define a DiscriminatorFormula instead of a DiscriminatorColumn in Hibernate:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorFormula(
"CASE WHEN TYPE IN ('SUBTYPE', 'SUBTYPE-2', ...) THEN TYPE ELSE 'BaseEntity'")
public class BaseEntity {
// . . .
}
#Entity
#DiscriminatorValue("SUBTYPE")
public class SubclassEntity extends BaseEntity {
// ...
}
Disadvantage of that solution: You need to declare the discriminator values of all subtypes in BaseEntity.
There is even a simpler solution to this problem. You could use a #DiscriminatorValue("not null"), like this:
#Entity
#DiscriminatorValue("not null")
public class MiscSubclassEntity extends BaseEntity {
// . . .
}
This way, whenever a non-mapped discriminator value is found, a MiscSubclassEntity will be used instead. For more on this topic, check this Hibernate blog post.

JPA - What if #Table is not specified for the entity?

I referred #Table documentation and it states that:
If no Table annotation is specified for an entity class, the default
values apply.
My question is what is the default value?
The default table name is the unqualified classname of the entityclass and the default schema name is the connected schema from the database connection.
If you specify #Entity and you don't specify #Table, your class will be mapped and in the database you will get the class name as name for your table.
From Marking a POJO as persistent entity section in the documentation:
#Table is set at the class level; it allows you to define the table, catalog, and schema names for your entity mapping. If no #Table is defined the default values are used: the unqualified class name of the entity.
For example if you have:
#Entity
public class MyTest{ ...
Your table will have the name my_test in your database.
Note that PascalCase will be converted to pascal_case. Be aware of that.

Inheritance on Hibernate objects without table mapping

I want to use hibernate objects in project as defined below.
#Table(name = "Parent")
class Parent{
int id;
String name;
}
#Table(name = "Child")
class Child extends Parent{
String schoolNo;
}
But in the database;
There is no relation with these two table.
Parent tables columns are; id, name
Child tables columns are; id, name and schoolNo
If I use
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
when I send a query for Parent object, hibernate use UNION on Child and Parent tables but I want to select from only Parent table.
And if I use
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
hibernate wants a discriminator column.
I need hibernate sends select query for each class to its table.
Best regards.
TABLE_PER_CLASS is the correct strategy here.
It's odd that Hibernate generates a union query over both tables, but that should still work. The subquery over the wrong table won't find anything, so the results will be correct. This sounds like a bug in Hibernate's query generation for subclasses.
In a similar situation, I use #Inheritance(strategy = InheritanceType.JOINED) on the parent table.
See more info in the Hibernate docs: http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/entity.html#d0e1168

What's the difference between the name argument in #Entity and #Table when using JPA?

I'm using JPA2 and both #Entity and #Table have a name attribute, e. g.:
#Entity(name="Foo")
#Table (name="Bar")
class Baz
What should I use, which ones are optional?
In my specific case I have a class User and a class Group, which have additional requirements (as far as I understand) because they are reserved words in SQL.
How would a working solution look like and with which name would I refer to the entity when writing queries?
Update: I added name="GROUPS" to both annotations in Group and did the same for User, but now I get this error:
Exception Description: The table [USERS] is not present in this descriptor.
Descriptor: RelationalDescriptor(example.Group --> [DatabaseTable(GROUPS)])
and this error
Internal Exception: java.sql.SQLException: Table not found in statement [SELECT ID, MAXIMUMROLE, MEMBERSHIPS_ID FROM USERS]
#Table is optional. #Entity is needed for annotating a POJO class as an entity, but the name attribute is not mandatory.
If you have a class
#Entity
class MyEntity {}
A table with name "MyEntity" will be created and the Entity name will be MyEntity. Your JPQL query would be:
select * from MyEntity
In JPQL you always use the Entity name and by default it is the class name.
if you have a class
#Entity(name="MyEntityName")
#Table(name="MyEntityTableName")
class MyEntity {}
then a table with name MyEntityTableName is created and the entity name is MyEntityName.
Your JPQL query would be :
select * from MyEntityName
The name in #Entity is for JPA-QL queries, it defaults to the class name without package (or unqualified class name, in Java lingo), if you change it you have to make sure you use this name when building queries.
The name in #Table is the table name where this entity is saved.
#Entity is useful with model classes to denote that this is the entity or table
#Table is used to provide any specific name to your table if you want to provide any different name
Note: if you don't use #Table then hibernate consider that #Entity is your table name by default
#Entity
#Table(name = "emp")
public class Employee implements java.io.Serializable { }

Categories

Resources