I have an object with a field that can be a number of object types. This object is encoded in a single table with a discriminator column for the field's subtypes. Each of these subtypes have their fields mapped to a column in the parent objects table. I cannot seem to model this in hibernate. The code bellow will return null for getSubfield() regardless of what subtype data is in the table.
Schema
id type whosit whatsit
+----+------+--------+---------+
| 1 | "A" | "test" | null |
| 2 | "B" | null | "test" |
+----+------+--------+---------+
Domain Objects
#Entity
public class Parent {
protected #Id #GeneratedValue int id;
protected Subfield subfield;
public Subfield getSubfield() {return subfield;}
}
#Embeddable
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="type", discriminatorType=DiscriminatorType.STRING)
public abstract class Subfield {}
#DiscriminatorValue("A")
public class TypeA extends Subfield {
public String whosit;
}
#DiscriminatorValue("B")
public class TypeB extends Subfield {
public String whatsit;
}
"SELECT p FROM parent p"
{id=1,subfield=null}
{id=2,subfield=null}
Is it possible to accomplish what I want with this object model, or do I need to get a bit more creative (this is a legacy database, changing the schema is not preferred)
As pointed by the asker, hibernate does not support inheritance of embeddable classes (https://hibernate.atlassian.net/browse/HHH-1910).
According to the Eclipse wiki, the JPA does not specify this behavior, but EclipseLink does support it (http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Entities/Embeddable#Inheritance).
My suggestion is to smash the class hierachy entirely inside Subfield. Horrible, but should work until this is solved.
Ok you can't easily change the schema, but how about adding a couple of views?
I know this is old.
One way around this is as specified above. Create a view. You say you don't want to change the schema. Then don't. You can create a new schema which maps the old schema and does what you want. (Possibly depending on database)
Related
Is it possible to make inheritance in JPA/Hibernate without id?
My use case:
I have multiple entities and I want every time change is being done, timestamp being recorded. So I created AbstractResource and want each derived class inherit properties and logic (to avoid writing same stuff over and over again in each class).
My problem that hibernate wants an ID to entity, and I do not care about id, since my only concern is additional properties. And each entity can have whatever id it wants (String, int, long, different name, etc.).
I tried with Embeddable, but looks like hibernate does not support inheritance for Embeddable. Do you have any ideas, how my task can be achieved?
My parent class from which "Audited" entities are derived:
#Embeddable
#EntityListeners(AbstractResourceListener.class)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class AbstractResource {
private long modifyTimestamp;
#Column(name = "_MODIFY_TIMESTAMP", nullable = true)
public long getModifyTimestamp() {
return modifyTimestamp;
}
public void setModifyTimestamp(long modifyTimestamp) {
this.modifyTimestamp = modifyTimestamp;
}
}
#MappedSuperclass is an annotation for super classes that you can extend and use in audit. Please see example
I have an entity named LocationType (BaseEntity is a #MappedSuperclass):
#Entity
public class LocationType extends BaseEntity
The table name generated for this entity is location_type. I understand the default naming strategy works like this.
What I cannot understand is why I cannot force Hibernate to use literal name, locationtype. No matter what I do:
#Entity(name = "LocationType")
public class LocationType
or
#Entity
#Table(name = "LocationType")
public class LocationType
or
#Entity(name = "LocationType")
#Table(name = "LocationType")
public class LocationType
the table name always ends up as location_type. Hibernate just knows better!
If I use any other name
#Entity(name = "wtf")
then table name becomes wtf as well.
Is this documented behaviour? Looks like a bug to me.
Similar question: Hibernate ignores #Table(name = "...") for extended classes - created tablenames are all lower case (it refers to inheritance mapping, though).
have a look here in docs.
ImprovedNamingStrategy
Implementing a NamingStrategy.
It is the behavior of the org.hibernate.cfg.ImprovedNamingStrategy , which will convert the mixed case names to the embedded underscores name . http://docs.jboss.org/hibernate/core/3.5/api/org/hibernate/cfg/ImprovedNamingStrategy.html . So if you explicitly use the name "EventLog" , it will convert to the "event_log" .
If you simply want to use the name explicitly specified in the #Table , you should use the org.hibernate.cfg.DefaultNamingStrategy . By default it is used when you instantiate your org.hibernate.cfg.Configuration object
If you would like to use the ImprovedNamingStrategy for all tables except those which specify a name explicitly you can use the subclass below. The columnName and tableName methods are the ones called when a name is explicitly specified, this subclass leaves the specified names unmolested.
public class RespectfulImprovedNamingStrategy extends ImprovedNamingStrategy
{
#Override
public String columnName(String columnName)
{
return columnName;
}
#Override
public String tableName(String tableName)
{
return tableName;
}
#Override
public String classToTableName(String className) {
return addUnderscores( StringHelper.unqualify(className) );
}
}
more links to refer.
link1
link2
It seemed strange to me that Hibernate does not obey what is specified in the #Table(name="...") annnotation, so I dug up this bug report, 9 years old as of now:
NamingStrategy should not be used when table or column name is supplied
The bug was rejected, God and Gavin King know why (or maybe it's just God now). This contradicts JPA 1/2 specifications. The final answer, thus is: it's just how Hibernate works out of the box; if you hold your annotation names dear (or JPA compliance), you should implement your own naming strategy to fix this quirk.
I'm starting a new project using JPA 2 + Hibernate 4.2.6 for data access.
I have two tables in my DB representing two different kinds of Answer, so I have answer_type_a and answer_type_b tables.
The are identical except for one field.
Now I'm creating my model classes and I'd like to inherit my AnswerA and AnswerB entities from a commom super-class or interface Answer.
I read some docs about entity inheritance:
http://docs.oracle.com/javaee/6/tutorial/doc/bnbqn.html
http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/inheritance.html#inheritance-tablepersubclass
but I have not a clear view on how to structure my DB and my entity classes to achieve this. Can you help me?
You need table Answer - that is super for particular others, where you should place all common columns for answer_type_a and answer_type_b. Assume Answer.a_id is primary key, then answer_type_a.a_id answer_type_b.a_id are simultaneously PRIMARY KEY and FOREIGN KEY.
Of course don't forget to place distinguished columns to answer_type_a and answer_type_b
+---------+
| Answer |
+---------+ ----------------------->--+--------------+
| a_id +------) | answer_type_b|
---->-+--------------+ +--------------+
... | answer_type_a| | a_id |
+--------------+ ...
| a_id |
....
You need to create one class named Answer with the common fields and you will annotate it with #MappedSuperclass. Additionally, you will have 2 entity classes, named AnswerA and AnswerB which will only hold the "extra" fields.
If you have 2 tables, then you need to use the TABLE_PER_CLASS inheritance type. So add #Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) to the Answer class, and probably a #Table annotation to AnswerA and AnswerB.
But if I were you, I would use one table for all answers, with all the needed columns + one discriminator column so you can distinguish if a row is answer type a or type b.
Well, as I understand your question is about mapping:
You need a java class Answer which is actually your super class. As there is no corresponding DB table for that, it must be annotated with #MappedSuperclass
#MappedSuperclass
public class Answer {
//fields&properties
}
And now the two classes come, which are entities, as they are persisted in DB tables:
#Entity
#Table("answer_type_a")
public class AnswerA extends Answer {
//here you add the field that is not common with AnswerB
}
#Entity
#Table("answer_type_b")
public class AnswerB extends Answer {
//here you add the field that is not common with AnswerA
}
PS: IMHO you don't need any inheritance annotations, as the mapped super class is not an entity.
I recently was in a similar situation like you. The best solution found was using an abstract class as a super type, and extending that class to the concrete types (which will represent your entity).
So for your situation you would model something like this.
#MappedSuperClass
public abstract class Answer{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Integer id;
#Column
String commonFieldOne;
#Column
String commonFieldTwo;
#Column
String commonFieldThree;
...
}
Note that I used the #MappedSuperClass annotation. This tells JPA that this is a super class that will not be instantiated and should not have a database table associated with it.
Also note that the Id which will serve as the primary key for both child tables is defined in this class
Now for you concrete classes (which will be mapped to tables in you database), you would use the #Entity annotation.
#Entity
#Table("answer_type_a")
public class AnswerA extends Answer{
//All common fields from Answer table will be included in the DB tables as columns automatically
#Column
String uniqueFieldOne;
....
}
You can do the same for #Table("answer_type_b")
Hope this helps!
Just create one table in your database e.g answer which contains both fields. You must have three classes Class 1 : Answer, Class 2: Answer_Type_A extends Answer, Class 3: Answer_Type_B extends Answer. There will be an hbm file for Answer (superclass) and two hbm for each Answer_Type_A and Answer_Type_B. In sybclass hbm files include:
<hibernate-mapping> <subclass name="com.test.Answer_Type_A"
extends="com.test.Answer">
<property name="fieldA" />
</subclass>
</hibernate-mapping>
[This scenario is for illustration]
I have certain products, and each product can have comments. Comments are of two types, general comments(GC) and product comments (PC).
Sample Tables
PRODUCT
ID NAME
1 APPLE
--
PRODUCT_COMMENTS
COMMENT_ID | COMMENT | COMMENT_TYPE
1 | IPHONE IS GOOD | PC
2 | IPAD LOOKS GOOD |GC
3 | GENERAL COMMENT| GC
4 | PRODUCT COMMENT |PC
Assume that there is a foreign key from Product table to Product_comment table.
And I have a couple of classes mapping to these tables.
Product class and ProductComment class
Product class has a one-to-may relationship with ProductComment class.
public class Product {
private Long id;
private String productName;
private List<ProductComments> productComments;
private List<ProductComments> generalComments;
.....
}
Now my problem is, there are two separate list of comments (differentiated by the comment_type).
When I say
Product p = (Product)session.load(Product.class, new Long(1));
Is it possible to fetch the generalComments and productComments properly ? In the above example
generalComments list should contain ['IPAD LOOKS GOOD','GENERAL COMMENT'] and the productComments list should contain ['IPHONE IS GOOD','PRODUCT COMMENT'].
What sort of mapping should be done to achieve the above thing ?
Edit :
We use Hibernate 3.0 and hbm mapping files(not annotations).
Use #Where annotation. You can give SQL condition to limit which ones will be chosen. In your example it goes roughly as follows:
#Where(clause="COMMENT_TYPE = 'PC'")
private List<ProductComments> productComments;
#Where(clause="COMMENT_TYPE = 'GC'")
private List<ProductComments> generalComments;
Be aware that this limit values loaded from database, but not the values written to these lists. You have still via program logic control which kind of comments are written to these lists.
In this case I would choose to use inheritance for the sake of readability and type safety.
That means, create Comment.class GeneralComment.class and ProductComment.class.
You can choose 2 ways for dealing with the hierarchy :
Actual inheritance. each class represents an equivalent table joined by PK.
Object inheritance. discriminate the Comment.class values into the two other entities using a discriminator column :
on the Comment.class :
<discriminator column="discriminator_column" type="discriminator_type" force="true|false" insert="true|false" formula="arbitrary sql expression" />
on each subclass :
<subclass name="ClassName" discriminator-value="discriminator_value" proxy="ProxyInterface" lazy="true|false" dynamic-update="true|false" dynamic-insert="true|false" entity-name="EntityName" node="element-name" extends="SuperclassName"> <property .... /> ..... </subclass>
Now your code can look like :
public class Product {
private Long id;
private String productName;
private List<ProductComments> productComments;
private List<GeneralComments> generalComments;
.....
}
I need to create a relation in Hibernate, linking three tables: Survey, User and Group.
The Survey can be visible to a User or to a Group, and a Group is form of several Users.
My idea was to create a superclass for User and Group, and create a ManyToMany relationship between that superclass and Survey.
My problem is that Group, is not map to a table, but to a view, so I can't split the fields of Group among several tables -which would happen if I created a common superclass-.
I thought about creating a common interface, but mapping to them is not allowed.
I will probably end up going for a two relations solution (Survey-User and Survey-Group), but I don't like too much that approach.
I thought as well about creating a table that would look like:
Survey Id | ElementId | Type
ElementId would be the Group or UserId, and the type... the type of it.
Does anyone know how to achieve it using hibernate annotations? Any other ideas?
Thanks a lot
I posted a very similar answer yesterday. To summarize, you can't use a mapped superclass because a mapped superclass is not an entity and can't be part of an association (which is what you want) but you can use an abstract Entity with a TABLE_PER_CLASS inheritance strategy to obtain a similar result.
Something like this (not tested):
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractEntity {
#Id #GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
#ManyToMany(mappedBy="entities")
private Set<Survey> surveys = new HashSet<Survey>();
...
}
#Entity
public class User extends AbstractEntity {
...
}
#Entity
public class Group extends AbstractEntity {
...
}
#Entity
public class Survey {
#Id #GeneratedValue
private Long id;
#ManyToMany
private Set<AbstractEntity> entities = new HashSet<AbstractEntity>();
...
}
References
Annotations, inheritance and interfaces
using MappedSuperclass in relation one to many
Polymorphic association to a MappedSuperclass throws exception
You can use the table per concrete class inheritance strategy, hibernate will replicate all properties for each subclass, this will work with a view.
I would also suggest the composite pattern for users/groups (which is close to your first option).
http://en.wikipedia.org/wiki/Composite_pattern
This is possible. Such an 'inherited properties' approach can be achieved by defining the superclass as a MappedSuperclass.
EDIT:
There is also some alternatives listed in section 2.2.4 in the hibernate annotations reference doc, section 2.2.4.4 covers MappedSuperclass.