开发者

Single Table Inheritance WITHOUT Discriminator column

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.handleNew开发者_如何学运维Instance(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.


@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.


Make a view of the table with the limited set of columns and map the second class to that one. Define an interface with the limited set of columns and have both class implement the interface. That probably gets you about 95% of what you need. If you need to, create methods to define equality between the two as well as being able to convert the larger class (via a constructor?) to the smaller class.


You have to select an inheritance type for two entities.

What you are trying to do is not appropriate, because hibernate won't know what objects to instantiate.

If you simply need an object to have fewer fields, then don't map it as an entity - just provide a constructor that copies all fields from the other class.


If you don't want to add auto dtype column, you should define your own Discriminator by @DiscriminatorFormula

@Entity
@Table(name = "CS_CUSTOMER")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorFormula("case when id < 3 then 'VIP' else 'Customer' end")
public class Customer extends BaseEntity {
...
@Entity
public class VIP extends Customer {
...

The Discriminator name is default to Entity class name, if you want to change it, use

@DiscriminatorValue("VIP")  

Log from hibernate

Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: 

    create table cs_customer (
       id bigint not null,
        create_user_id bigint,
        first_name varchar(255),
        last_name varchar(255),
        primary key (id)
    )
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜