I am using Hibernate to connect to my database. I have an inheritance structure in my application.The problem is that when i do a query l开发者_如何学编程ike "from Animal", it does a left outer join for the class Animal,its sub classes and all the associations for Animal and its subclasses. How do i avoid this situation.I want to load the data only when i specify it through a fetchmode in my criteria query?
Yes, Hibernate supports polymorphic queries. From the documentation:
14.8. Polymorphic queries
A query like:
from Cat as cat
returns instances not only of
Cat
, but also of subclasses likeDomesticCat
. Hibernate queries can name any Java class or interface in the from clause. The query will return instances of all persistent classes that extend that class or implement the interface. The following query would return all persistent objects:from java.lang.Object o
The interface
Named
might be implemented by various persistent classes:from Named n, Named m where n.name = m.name
These last two queries will require more than one SQL
SELECT
. This means that the order by clause does not correctly order the whole result set. It also means you cannot call these queries usingQuery.scroll()
.
This is the default behavior (called implicit polymorphism) and Hibernate supports both implicit and explicit polymorphism:
Implicit polymorphism means that instances of the class will be returned by a query that names any superclass or implemented interface or class, and that instances of any subclass of the class will be returned by a query that names the class itself. Explicit polymorphism means that class instances will be returned only by queries that explicitly name that class. Queries that name the class will return only instances of subclasses mapped inside this
<class>
declaration as a<subclass>
or<joined-subclass>
. For most purposes, the defaultpolymorphism="implicit"
is appropriate. Explicit polymorphism is useful when two different classes are mapped to the same table This allows a "lightweight" class that contains a subset of the table columns.
This can be configured at the class level. Use polymorphism="explicit"
if you are if you are using xml mappings, see 5.1.3 Class. Use Hibernate's @Entity
annotation if you're using annotations, see 2.4.1. Entity. Below an example:
@javax.persistence.Entity
@org.hibernate.annotations.Entity(polymorphism = PolymorphismType.EXPLICIT)
@Inheritance(strategy = InheritanceType.JOINED)
public class Foo {
...
}
Assume you have a class structure as follows:
class Animal { }
class Dog : Animal { }
class Cat : Animal { }
then when you select all Animal
s, you'd expect to also load all Dog
s and Cat
s. After all they are Animal
s.
A different story are the associations. You can created you mappings such that the associations are lazy load instead of eager load.
Basically it's the default ORM inheritance design pattern used by Hibernate called class inheritance (all the classes are mapped to a single table), if you want to change that you can google:
- single class hierarhy or table per class (this will map every class to a separate table in the DB)
- concrete class hierarhy (this will map only the concrete implementations to a table).
To avoid multiple joins during class hierarchy fetching you can apply SINGLE_TABLE hierarchy mapping strategy, and then define secondary tables on subclasses with SELECT fetching strategy. However, this turns you "heavy join" model into "N+1 select" model. The example:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = Super.DISCRIMINATOR_COLUMN, discriminatorType = DiscriminatorType .STRING, length = 255)
public class Super {
public static final String DISCRIMINATOR_COLUMN = "classname";
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
protected long id;
@Column(updatable = false, insertable = false)
protected String classname;
protected String superProp = "superProp";
public long getId() {
return id;
}
public String getClassname() {
return classname;
}
public String getSuperProp() {
return superProp;
}
public void setSuperProp(String superProp) {
this.superProp = superProp;
}
}
@Entity
@SecondaryTable(name = SubA.TABLE)
@Table(appliesTo = SubA.TABLE, fetch = FetchMode.SELECT)
public class SubA extends Super {
public static final String TABLE = "SUBA";
@Column(table = TABLE)
protected String subAProp = "subAProp";
public String getSubAProp() {
return subAProp;
}
public void setSubAProp(String subAProp) {
this.subAProp = subAProp;
}
}
@Entity
@SecondaryTable(name = SubB.TABLE)
@Table(appliesTo = SubB.TABLE, fetch = FetchMode.SELECT)
public class SubB extends Super {
public static final String TABLE = "SUBB";
@Column(table = TABLE)
protected String subBProp = "subBProp";
public String getSubBProp() {
return subBProp;
}
public void setSubBProp(String subBProp) {
this.subBProp = subBProp;
}
}
And what SQL is done on from Super
HQL query:
select [...] from SUPER super0_
select super_1_.subaprop as subaprop1_83_ from SUBA super_1_ where super_1_.id=1
select super_2_.subbprop as subbprop1_84_ from SUBB super_2_ where super_2_.id=2
More about this approach and general hibernate performance hints you can read in my article.
精彩评论