SingleTable Strategy, if table name not specified in subclass, cast exception? - java

I have a single table hierarchy shown below:
#MappedSuperclass
#Table(name = "v_contract_account", schema = "SAP")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#XmlRootElement
#XmlSeeAlso({CurrencyAccount.class, ProgramAccount.class})
#XmlAccessorType(XmlAccessType.FIELD)
public abstract class AbstractContractAccount implements Serializable {
....
}
#Entity
#Table(name = "v_contract_account", schema = "SAP")
#Immutable
#DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.INTEGER)
#DiscriminatorValue("0")
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class CurrencyAccount extends AbstractContractAccount {
...
}
#Entity
#Table(name = "v_contract_account", schema = "SAP")
#Immutable
#DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.INTEGER)
#DiscriminatorValue("1")
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ProgramAccount extends AbstractContractAccount {
...
}
Right now it works as it is (except the discriminator stuff), but why is it that if I remove the table annotation from the subclass, Hibernate will throw an exception?
org.jboss.resteasy.spi.UnhandledException: java.lang.ClassCastException: org.jboss.resteasy.specimpl.BuiltResponse cannot be cast to [Lcom.zanox.internal.billingmasterdata.domain.entity.CurrencyAccount;
And the strange thing is, if I don't put the table and inheritance annotation in the abstract super class, everything still works fine. Does this mean MappedSuperClass doesn't care about the table and inheritance annotation? If the annotation #Inheritance(strategy = InheritanceType.SINGLE_TABLE) is not needed anywhere, then where do I specify it?
Btw, in my case here, Hibernate doesn't create the table, the table is there already and I just want to map it.

You probably want to remove the #MappedSuperClass annotation from your parent Entity and make it a normal Entity.
http://docs.oracle.com/javaee/5/api/javax/persistence/MappedSuperclass.html
If you wanted to query across AbstractContractAccount then it has to be an Entity. You can cannot do this when it is a MappedSuperclass.
You would use #MappedSuperClass when you wanted to defined some common mapings but where there was no actual 'database inheritance'.
http://en.wikibooks.org/wiki/Java_Persistence/Inheritance
Mapped superclass inheritance allows inheritance to be used in the
object model, when it does not exist in the data model. It is similar
to table per class inheritance, but does not allow querying,
persisting, or relationships to the superclass. Its main purpose is to
allow mappings information to be inherited by its subclasses. The
subclasses are responsible for defining the table, id and other
information, and can modify any of the inherited mappings. A common
usage of a mapped superclass is to define a common PersistentObject
for your application to define common behavior and mappings such as
the id and version. A mapped superclass normally should be an abstract
class. A mapped superclass is not an Entity but is instead defined
though the #MappedSuperclass annotation or the
element.

Related

search though all tables extended from #MappedSuperclass

I have and issue with searching in entities that are extended from #MappedSuperclass. I created a class PhoneBook and extended 2 entities from it: FirstPhoneBook and SecondPhoneBook. The structure looks the following:
#MappedSuperclass
public abstract class PhoneBook {
...
#Entity
#Table(name = "first_phone_book")
public class FirstPhoneBook extends PhoneBook {
...
#Entity
#Table(name = "second_phone_book")
public class SecondPhoneBook extends PhoneBook {
...
These tables are absolutely similar. I discribe all fields in PhoneBook class, childs have only default constructor in it. External system sends a phone number as a parameter. Depending on whether tables contain such number or not my system responds with a word.
The question is: how can I search separately in each table that is extended from #MappedSuperclass without hardcoding each child class name?
I could only find variants of search by value like that:
currentSession.get(Employee.class, theId);
but there is explicit call to entity class. I want this to be extendable without need to write new DAO for each new entity added. Current method signature looks the following:
public <T extends PhoneBook> T findByNumber(String number);
What you describe is polymorphic queries, i.e. queries that reference the parent class. The Hibernate documentation says this is not well supported when using #MappedSuperclass inheritance:
Because the #MappedSuperclass inheritance model is not mirrored at the database level, it’s not possible to use polymorphic queries referencing the #MappedSuperclass when fetching persistent objects by their base class.
If polymorphic queries are frequently used, it's better to use the table per class inheritance strategy:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class PhoneBook {
...
#Entity
#Table(name = "first_phone_book")
public class FirstPhoneBook extends PhoneBook {
...
#Entity
#Table(name = "second_phone_book")
public class SecondPhoneBook extends PhoneBook {
...
You can then fetch an entity using the superclass:
PhoneBook phoneBook = currentSession.get(PhoneBook.class, theId);
and Hibernate would typically use a UNION to do the query with both tables.
This being said, even with #MapperSuperclass, Hibernate can still query all tables for classes that extend the parent class. You can use the following JPA query (note that it uses the fully qualified class name of the parent class):
Query<PhoneBook> query = currentSession.createQuery("from " + PhoneBook.class.getName() +
" where id = :id", PhoneBook.class);
query.setParameter("id", theId);
The difference is that here it's not querying an entity, but just all classes that extend a parent class. Also in this case, unlike with the table-per-class strategy, Hibernate will not use a UNION, but send a query to each table, in this case two separate SQL queries instead of one.

Any way to inherit an entity which is not #MappedSuperclass?

Let's suppose I have a JPA entity:
#Entity
#Table(name = "PARENT")
public class Parent {
// ...
}
Is there any way, maybe Hibernate-specific, to create subclass of Parent in a separate table?
#Entity
#Table(name = "CHILD")
public class Child extends Parent {
// ...
}
The main idea is to have set of common entity classes in a base package shared among projects, and extend them only if some project-specific properties are required.
Hibernate Inheritance: Annotation Mapping
Annotate the parent class with #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS). The Hibernate docs (for version 4.3, the new 5.0 docs seem to be missing this section so far) cover this in Chapter 10 and section 5.1.6 of the manual.
In One Table per Concrete class scheme, each concrete class is mapped as normal persistent class. Thus we have 3 tables; PARENT, CHILD to persist the class data. In this scheme, the mapping of the subclass repeats the properties of the parent class.
Following are the advantages and disadvantages of One Table per Subclass scheme.
Advantages
This is the easiest method of Inheritance mapping to implement.
Following is the example where we map Parent and Child entity classes using JPA Annotations.
#Entity
#Table(name = "Parent")
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Parent {
// Getter and Setter methods,
}
#Inheritance – Defines the inheritance strategy to be used for an entity class hierarchy. It is specified on the entity class that is the root of the entity class hierarchy.
#InheritanceType – Defines inheritance strategy options. TABLE_PER_CLASS is a strategy to map table per concrete class.
#Entity
#Table(name="Child")
public class Child extends Parent {
// Getter and Setter methods,
}
Annotate the parent class with #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS). The Hibernate docs (for version 4.3, the new 5.0 docs seem to be missing this section so far) cover this in Chapter 10 and section 5.1.6 of the manual.

mixing jpa inheritance strategies - inheritanceType.JOINED with inheritanceType.SINGLE_TABLE

My class structure looks like this... I have two separate strategies being implemented here but the inheritance strategy of the root class i.e. InheritanceType.JOINED is being used throughout the hierarchy...
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "typeName", discriminatorType = DiscriminatorType.STRING, length = 100)
#Table(name="table_A")
public abstract class A{
...
}
#Entity
#Table(name = "table_B")
#PrimaryKeyJoinColumn(name = "ID_B")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorValue("SVC")
public abstract class B extends A {
...
}
#Entity
#DiscriminatorValue("C")
public class C extends B {
...
}
#Entity
#DiscriminatorValue("D")
public class D extends B {
...
}
When, I am creating an instance of 'D' and trying to persist it, hibernate is looking for a table named 'D' ...
I found a couple of people asking the same questions... but answers didn't help me...
mixing joined and single table inheritance and querying for all objects - same issue..
How to mix inheritance strategies with JPA annotations and Hibernate? - mixing single_table with joined.. this is not helpful in my case..
The JPA spec does not allow you to mix strategies in an inheritance tree; it requires you to set the inheritance strategy in the root Entity. JDO is the only spec allowing mixed strategies. You may find a JPA implementation that allows it, but it is non-portable

dao, tx, service structure: where to place a method querying an abstract entity?

I have an abstract entity that 4 other entities inherit from. This relationship works well, however I want to query the abstract entity so that I get all entities regardless of their types. I have no idea where to place such a method since the parent entity dao is also abstract.
EntityParent (abstract) -> EntityType1, EntityType2, EntityType3, EntityType4
DAOs look like this:
EntityParentDAO (abstract) -> EntityType1DAO, EntityType2DAO, EntityType3DAO, EntityType4DAO
TX also look like this:
EntityParentTx (abstract) -> EntityType1Tx, EntityType2Tx, EntityType3Tx, EntityType4Tx
My project structure goes as follows:
Entities -> DAO for each entity -> TX for each DAO -> Service combining several TXs
There is Service which uses all of the *TX*s that's within the scope of my project. Is this where a criteria/HQL query should be placed? That doesn't sound quite right.
For example let's say I have a Car parent entity and that I have children entities Coupe, Sedan, Minivan and so on and I want a list of cars given a property that is common to all and therefore in the entity (and it its table) Car. Where would I place this query/method given the structure I'm following?
I'm not sure I follow the transaction inheritance, but why not make the parent dao concrete and add it there? As long as the parent is an Entity, and it has the field, you can query on it. The return type will be a list of the base type, but it will be instances of the actual type.
Ex:
#Entity
#Table(name = "table")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.INTEGER)
public abstract class ParentImpl implements Parent{}
#Entity
#DiscriminatorValue("1")
public class Entity1Impl extends ParentImpl {}
public interface AbstractDao<T extends Parent> {}
public interface ConcreteParentDao<Parent> {}

JPA #OneToOne throws Error when mapped to an abstract #Entity with subclasses

I have a problem when an Entity is mapped to another Entity which has a direct implementation on its subclasses. See sample mapping below:
#Entity
class Location {
#OneToOne
#JoinColumn(...)
private Person person;
}
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="person_type",discriminatorType=DiscriminatorType.STRING)
abstract class Person {
}
#Entity
#DiscriminatorValue("M")
class Man extends Person {
...
}
#Entity
#DiscriminatorValue("W")
class Woman extends Person {
...
}
Now, this is what I have on my database table:
location-table:
id=1, person_id=1
person-table:
id=1,person_type="M"
When I retrieve the Location using entity manager, hibernate throws an exception saying that i cannot instantiate an abstract class or an interface.
Location location = entityManager.find(Location.class, 1L);
Hibernate throws this error:
javax.persistence.PersistenceException: org.hibernate.InstantiationException: Cannot instantiate abstract class or interface: Person
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:630)
at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:195)
at ......
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59)
at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98)
at org.unitils.UnitilsJUnit4TestClassRunner$TestListenerInvokingMethodRoadie.runTestMethod(UnitilsJUnit4TestClassRunner.java:174)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87)
at org.unitils.UnitilsJUnit4TestClassRunner$TestListenerInvokingMethodRoadie.runBeforesThenTestThenAfters(UnitilsJUnit4TestClassRunner.java:156)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)
at org.unitils.UnitilsJUnit4TestClassRunner.invokeTestMethod(UnitilsJUnit4TestClassRunner.java:95)
at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
at org.unitils.UnitilsJUnit4TestClassRunner.access$000(UnitilsJUnit4TestClassRunner.java:44)
at org.unitils.UnitilsJUnit4TestClassRunner$1.run(UnitilsJUnit4TestClassRunner.java:62)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
at org.unitils.UnitilsJUnit4TestClassRunner.run(UnitilsJUnit4TestClassRunner.java:68)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
This did the trick for me, using hibernate as persistence provider.
#OneToOne(cascade = {CascadeType.PERSIST,CascadeType.MERGE})
From The Java EE 6 Tutorial - Entity Inheritence:
Any mapping or relationship
annotations in non-entity superclasses
are ignored.
So you seem to be correct that you have to annotate the Person class with #Entity to associate it with a Location via #OneToOne.
From the #MappedSuperclass javadoc
A class designated with the
MappedSuperclass annotation can be
mapped in the same way as an entity
except that the mappings will apply
only to its subclasses since no table
exists for the mapped superclass
itself.
So you couldn't use #MappedSuperclass on Person, then map it with the #OneToOne, since there would be no Person table.
Seems like the JPA annotations you are using are correct. Have you tried #Martin Klinke's suggestion of a dummy discriminator value for the Person class?
I found this kind of problem solves itself if the Entity classes implement Serializable.
I had a similar error message with the following structure:
#Entity
#Table(name = "PERSON")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.STRING)
public abstract class Person {
#Id
#GeneratedValue
private Long id;
}
with a concrete child instance
#Entity
#DiscriminatorValue("REALPERSON")
public class RealPerson extends Person{ etc... }
and a class that has a field referencing the abstract class:
public class SomeClass() {
#OneToOne
private Person person;
}
The following changes fixed the problem for me:
Change the #OneToOne to #OneToOne(cascade = CascadeType.ALL)
Change the #GeneratedValue to #GeneratedValue(strategy = GenerationType.TABLE)
UPDATE: I also found I was not saving the RealPerson object before linking it to SomeClass. So now that I first save the instance first, the cascade attribute is no longer required
As indicated in my comment above, I have tried the same with EclipseLink and it works.
After creating some test data, I've cleared the discriminator value of a person entry in the DB and now I get a similar exception when trying to load the associated location. EclipseLink's message is a little bit more descriptive:
Exception Description: Missing class for indicator field value [] of type [class java.lang.String].
Descriptor: RelationalDescriptor(com.mklinke.webtest.domain.Person --> [DatabaseTable(PERSON)])
at org.eclipse.persistence.exceptions.DescriptorException.missingClassForIndicatorFieldValue(DescriptorException.java:937)
at org.eclipse.persistence.descriptors.InheritancePolicy.classFromValue(InheritancePolicy.java:355)
at org.eclipse.persistence.descriptors.InheritancePolicy.classFromRow(InheritancePolicy.java:342)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:485)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:456)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.buildObject(ObjectLevelReadQuery.java:723)
at org.eclipse.persistence.queries.ReadObjectQuery.registerResultInUnitOfWork(ReadObjectQuery.java:766)
at org.eclipse.persistence.queries.ReadObjectQuery.executeObjectLevelReadQuery(ReadObjectQuery.java:451)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1080)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:808)
...
The mapping seems to work, so unless the data is "corrupt" (which it isn't in your case as you said), it's probably a bug or at least different behavior in Hibernate.
I run similar code to this, with a couple of differences. Firstly, I place the Abstract class behind an interface. Secondly, I explicitly define the targetEntity for the #OnetoOne mapping. An example would be:
#Entity
class Location {
#OneToOne(targetEntity = AbstractPerson.class)
#JoinColumn(...)
private Person person;
}
public interface Person {
}
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="person_type",discriminatorType=DiscriminatorType.STRING)
abstract class AbstractPerson implements Person {
}
Here I've renamed your Person abstract class to 'AbstractPerson' for clarity. Took quite a bit of playing around with to get it working, I hope it solves your issue.
Try adding #ForceDiscriminator to Person. Without this annotation Hibernate often tries to instantiate the parent class, ignoring the discriminator that should tell it which child class to instantiate.

Categories