Good Morning, my Dear comrades,
This is starting to be come annoying - a simple thing, but hours of struggle, am I getting old??
I am trying to map two Classes to a single table using JPA by Hibernate. The idea is to have only a small subset of columns in parent Class, and bigger/full set in the child Class. There is NO TABLE inheritance involved, only class inheritance. How can this be accomplished??
Doing this will not work:
@Entity @Table(name = "the_table") class Parent implements Serializable { } @Entity @Table(name = "the_table") class Child extends Parent implements Serializable { }
Hibernate assumes default inheritance strategy InheritanceType.SINGLE_TABLE, and is looking for discriminator column - DTYPE by default. But wait - there is no table inheritance, having the discriminator column does not make sence.
I have also taken a look at PolymorphismType.EXPLICIT which did not make any difference. The stack trace is:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'apprentice0_.DTYPE' in 'where clause' at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) at java.lang.reflect.Constructor.newInstance(Constructor.java:513) at com.mysql.jdbc.Util.handleNewInstance(Util.java:411) at com.mysql.jdbc.Util.getInstance(Util.java:386) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1052) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3597) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3529) at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1990) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2151) at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2625) at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2119) at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2281) at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeQuery(NewProxyPreparedStatement.java:76) at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:208) at org.hibernate.loader.Loader.getResultSet(Loader.java:1808) at org.hibernate.loader.Loader.doQuery(Loader.java:697) at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259) at org.hibernate.loader.Loader.loadEntity(Loader.java:1881)
yeah, one more thing:
The @MappedSuperclass
and @Embeddable
are of no use as these can not be used in conjunction with @Entity
- the parent class has to be an @Entity
itself as it is being used for persistence elsewhere.
Only SINGLE_TABLE inheritance hierarchies require a discriminator column and values. JOINED hierarchies can use a discriminator to make some operations more efficient, but do not require one. TABLE_PER_CLASS hierarchies have no use for a discriminator.
The SINGLE_TABLE strategy maps records from the same database table to different entity classes of an inheritance hierarchy. If you want to use this strategy with JPA, your database table needs to have a discriminator column. The value in this column identifies the entity class to which each record shall be mapped.
Single Table Inheritance. Single Table Inheritance is an inheritance mapping strategy where all classes of a hierarchy are mapped to a single database table. In order to distinguish which row represents which type in the hierarchy a so-called discriminator column is used.
Annotation Type DiscriminatorColumnSpecifies 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.
@MappedSuperclass
is the annotation that must be used. If the same class is both a mapped super class and an entity, then simply split it into two classes:
@MappedSuperclass public class Parent { // ... } @Entity public class ParentEntity extends Parent { // no code at all here } @Entity public class Child extends Parent { // additional fields and methods here }
There are a couple of ways each with their own caveats.
1) Add annotations as following:
@DiscriminatorFormula("0") @DiscriminatorValue("0") class BaseClass{ } @DiscriminatorValue("00") class SubClass extends BaseClass{ }
where the subclasses discriminator value must be different to the base class' but also evaluate to the same value when passed into an Integer.valueOf(String s) method.
The caveat - if you return an object from Hibernate of the base class and then again when calling for the subclass type you will get an error complaining the loaded object was of the wrong class. If you call the subclass query first however the base class call will return the subclass.
2) Use a view in the database to map the table and use this as the table of the sub class. In fact it can be any other class that matches the column mappings as Hibernate thinks its a completely separate table.
Caveat - You will potentially have the same row instantiated as two different objects that will not be synchronised and could lead to conflicting/lost database updates.
It's probably better to stick with one type for a session and that could be handled without the runtime risks by using an entity mapping xml file that overrides the DiscriminatorValue of the desired class to match the constant Discriminator'Formula' value which you can pass into the initial configuration.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With