Hibernate inheritance and multiple tables with same class
I'm pretty new to Hibernate. In my situation I have a concrete table with records containing join IDs to a multitude of other tables - all with the same structure. What I'd like to achieve is get something like
SELECT *
FROM main_records mr, ref1 r1, ref2 r2
WHERE r1.id = mr.id_ref1
AND r2.id = mr.id_ref2;
The main idea would be to reuse the class for all the join references.
SQL
CREATE TABLE main_records
(
id integer NOT NULL,
id_ref1 integer NOT NULL,
id_ref2 integer NOT NULL
)
CREATE TABLE ref1
(
id integer NOT NULL,
value character varying
)
CREATE TABLE ref2
(
id integer NOT NULL,
value character varying
)
I've setup the base POJO classes
JAVA classes
public class MainRecord {
private Integer id;
private Ref ref1;
private Ref ref2;
...
// getters and setters
}
public class Ref {
private Integer id;
private String value;
...
// getters and setters
}
My idea is to define the Hibernate mappings in following manner:
Define an abstract super class
<hibernate-mapping package="test">
<class abstract="true" name="Ref">
<id name="id" type="java.lang.Integer" column="ID">
<generator class="native" />
</id>
<property name="value" type="java.lang.String" column="VALUE" />
</class>
</hibernate-mapping>
Map main entity, extend the super class but use individual tables
<hibernate-mapping package="test">
<union-subclass name="Ref1" table="REF1" extends="Ref" />
<union-subclass name="Ref2" table="REF2" extends="Ref" />
<class name="MainRecord" table="MAIN_RECORDS">
<id name="id" column="ID" type="java.lang.Integer" />
<many-to-one name="ref1" class="Ref1" col开发者_开发知识库umn="ID_REF1" fetch="join" unique="true" />
<many-to-one name="ref2" class="Ref2" column="ID_REF2" fetch="join" unique="true" />
</union-subclass>
</class>
</hibernate-mapping>
I do manually include mapping files in the configuration, loading seems ok but then an error occurs, without any detaild explanation:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.apache.cxf.transport.servlet.ServletTransportFactory' defined in class path resource [META-INF/cxf/cxf-servlet.xml]: Error setting property values;
nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (2) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'bus' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in ServletContext resource [/WEB-INF/spring/database.xml]: Invocation of init method failed; nested exception is org.hibernate.HibernateException: Unable to instantiate default tuplizer [org.hibernate.tuple.entity.PojoEntityTuplizer]
PropertyAccessException 2: org.springframework.beans.MethodInvocationException: Property 'transportIds' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in ServletContext resource [/WEB-INF/spring/database.xml]: Invocation of init method failed; nested exception is org.hibernate.HibernateException: Unable to instantiate default tuplizer [org.hibernate.tuple.entity.PojoEntityTuplizer]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1361)
The system is a mix of Spring 2.5, Hibernate 3.2, Cxf 2.3.4, Javassist 3.11,
My questions are:
(a) Is this the correct approach?
(b) The error occurs as soon as I introduce
<union-subclass name="Ref1" table="REF1" extends="Ref" />
<union-subclass name="Ref2" table="REF2" extends="Ref" />
so I guess this is not the best way of doing it?
(c) Can it be written with annotations? I cannot fathom how to define the Ref1 and Ref2 classes without actually creating a POJO class for them. (d) Can I user more than 1 level of inheritance? I'd like, for example, use an abstract superclass for all my concrete tables, that cover the auditing fields they all have in common? Let's say class Ref extends an abstract AuditTable class, both in Java and Hibernate mappings.
If i understand the question correctly, You have a master record table with multiple foreign key to a lot of table that have the same colums each other ?
(a) Is this the correct approach?
No, You are trying to do inheritance by using the Table per class strategy witch i think is not appropriate to use here because you have one reference to each for each type object(field Ref1 and field Ref2 in your case). The use case that can be appropriate to use inheritance is if you have one polymorphic association to Ref in the MasterRecord object
Example of polymorphic association to Ref
public class MasterRecord{
Long id;
Ref anyObjectRef1OrRef2; <-- Polymorphic association
}
The association anyObjectRef1OrRef2
would have been map with the <any ... />
element in the mapping of MasterRecord. It would have needed two column one with the className and one for the foreignKey
(b)The error occurs as soon as I introduce
<union-subclass
... so I guess this is not the best way of doing it?
What you should do is inherit properties from a superclass (no specific table for this superclass)
(C)Can it be written with annotations?
Yes use @MappedSuperclass annotation.
Hibernate reference Implementation (Using Annotation)
- Mapping inheritance see section 2.2.4.4. Inherit properties from superclasses
(C-a)I cannot fathom how to define the Ref1 and Ref2 classes without actually creating a POJO class for them?
It can't be done without creating Ref1 and Ref2 pojo classes.
(d)Can I use more than 1 level of inheritance? I'd like, for example, use an abstract superclass for all my concrete tables, that cover the auditing fields they all have in common? Let's say class Ref extends an abstract AuditTable
- Section 2.2.4.4. Inherit properties from superclasses
Example with annotation
Base Class Ref.java
@MappedSuperclass
public abstract class Ref {
String value;
}
Class Ref1.java
@Entity
public class Ref1 extends Ref {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
}
Class Ref2.java
@Entity
public class Ref2 extends Ref {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
}
Class MainRecord.java
@Entity
public class MainRecord {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
@ManyToOne(cascade=CascadeType.ALL, targetEntity=Ref1.class)
Ref ref1;
@ManyToOne(cascade=CascadeType.ALL, targetEntity=Ref2.class)
Ref ref2;
}
Create and save entity as follow
..begin transaction
MainRecord mainRecord = new MainRecord();
Ref1 ref1 = new Ref1();
ref1.setValue("tettset");
Ref2 ref2 = new Ref2();
ref2.setValue("tettset");
mainRecord.setRef2(ref2);
mainRecord.setRef1(ref1);
entitymanager.persist(mainRecord);
..commit transaction.
to use hibernate maping file you would need to remove all annotation use the mapping below.
Not that when using a mapping file, the abstract Ref.java
super class is not mapped and all properties of the super class is in each child mapping file.
<class name="MainRecord" table="MAIN_RECORDS">
<id name="id" column="uid" type="long">
<generator class="identity"/>
</id>
<many-to-one name="ref1" class="Ref1" column="ID_REF1" fetch="join" unique="true" cascade="all" />
<many-to-one name="ref2" class="Ref2" column="ID_REF2" fetch="join" unique="true" cascade="all" />
</class>
<class name="Ref1" table="REF1">
<id name="id" column="uid" type="long">
<generator class="identity"/>
</id>
<property name="value" type="java.lang.String" column="VALUE" />
</class>
<class name="Ref2" table="REF2">
<id name="id" column="uid" type="long">
<generator class="identity"/>
</id>
<property name="value" type="java.lang.String" column="VALUE" />
</class>
Just one question.... if you use the same class to access several different tables... To which table will be stored new instances that you save? How will Hibernate know?
AFAIK, this cannot be done.
Shortcuts such as these, even if they can be made to work, often lead down a road of eventual confusion for developers who inherit your code. Before you ask "can it be done", ask "should it be done".
Think down the road: is it better to be more "efficient" at development time by creating fewer classes and using a somewhat obscure feature, or is it better to just make the separate classes and make things more comprehensible. Also, you might box yourself into a corner because this specific feature might cause unintended consequences, the classic "why is it doing that" situation.
I've inherited many apps where previous generations of developers wanted to do things in "cool" or "elegant" ways and ended up causing more confusion than if they had kept things concrete and simple.
Just create the separate classes.
精彩评论