views:

362

answers:

3

Suppose a Table per subclass inheritance relationship which can be described bellow (From wikibooks.org - see here)

Notice Parent class is not abstract

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Project {

    @Id
    private long id;

    // Other properties

}

@Entity
@Table(name="LARGEPROJECT")
public class LargeProject extends Project {

    private BigDecimal budget;

}

@Entity
@Table(name="SMALLPROJECT")
public class SmallProject extends Project {

}

I have a scenario where i just need to retrieve the Parent class. Because of performance issues, What should i do to run a HQL query in order to retrieve the Parent class and just the Parent class without loading any subclass ???

+2  A: 
Pascal Thivent
@Pascal Thivent Thank you for your reply but The first approach shown by yourself "left inner join" **all of subclasses** (You can test if you want). And i just need to retrieve **just** The Parent class - select ... from Project where p.id = :id without no "left inner join" with any subclass
Arthur Ronald F D Garcia
@Arthur I thought I tested this but I believe you. Will take a second look later in the day...
Pascal Thivent
A: 

I have tried the first apprach propposed by @Pascal Thivent

Here is what i have done (You can test if you want)

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Parent implements Serializable {

    private Integer id;

    private String parentField;

    @Id
    @GeneratedValue
    public Integer getId() {
        return id;
    }

    public String getParentProperty() {
        return parentField;
    }

}

Its Child subclass

@Entity
public class Child extends Parent {

    private String childField;

    public String getChildProperty() {
        return childField;
    }

}

And AnotherChild subclass

@Entity
public class AnotherChild extends Parent {

    private String anotherChildField;

    public String getAnotherChildProperty() {
        return anotherChildField;
    }

}

In order to test the first approach propposed by @Pascal Thivent, i have done as follows

public class LoadingParentObjectWhitoutAnySubclassTest {

    private static final String QUERY_AGAINST_PARENT_CLASS = "from Parent p where p.id = :id and p.class = Parent";

    private HibernateTemplate hibernateTemplate;

    private Serializable savedParentId;

    @BeforeClass
    public void setUp() {
        ApplicationContext appContext = new ClassPathXmlApplicationContext("app-persistence.xml");

        hibernateTemplate = (HibernateTemplate) appContext.getBean("hibernateTemplate");
    }

    @BeforeMethod
    public void createChild() {
        Child child = new Child();
        child.setParentProperty("something related to parent class");
        child.setChildProperty("something related to Child class");

        savedParentId = hibernateTemplate.save(child);
    } 

    @Test
    public void loadingParentWithoutAnySuclass {
        Parent parent = (Parent) hibernateTemplate
                                     .findByNamedParam(
                                         QUERY_AGAINST_PARENT_CLASS, 
                                         "id", 
                                         savedParentId).get(0);
    }

}

Hibernate logs

select
    parent0_.id as id1_,
    parent0_.parentProperty as parentPr2_1_,
    parent0_1_.anotherChildProperty as anotherC2_2_,
    parent0_2_.childProperty as childPro2_3_,
    case 
        when parent0_1_.id is not null then 1 
        when parent0_2_.id is not null then 2 
        when parent0_.id is not null then 0 
    end as clazz_ 
from
    Parent parent0_ 
/**
  * Neither
  */
left outer join
    AnotherChild parent0_1_ 
        on parent0_.id=parent0_1_.id 
/**
  * Nor
  */
left outer join
    Child parent0_2_ 
        on parent0_.id=parent0_2_.id 
/**
  * is what i want to
  */
where
    parent0_.id=? 
    and case 
        when parent0_1_.id is not null then 1 
        when parent0_2_.id is not null then 2 
        when parent0_.id is not null then 0 
    end=0

Therefore, i get the following Exception when i query against the Parent class

java.lang.IndexOutOfBoundsException: Index: 0, Size: 0

So, what have i done ?

I had to remodel inheritance as delegation See other drawbacks right here

@Entity
public class Parent implements Serializable {

    private MutableInt id = new MutableInt();

    private String parentField;

    private ParentType parentType;

    @Id
    public Integer getId() {
        return this.id.intValue();
    }

    public void setId(Integer id) {
        this.id.setValue(id);
    }

    public setIdAsMutableInt(MutableInt id) {
        this.id = id;
    }

    public String getParentProperty() {
        return parentField;
    }

    @Enumerated(EnumType.STRING)
    public ParentType getParentType() {
        return parentType;
    }

    /**
      * A marker enum to help to get our "subclass"
      */
    public static enum ParentType {
        CHILD,
        ANOTHER_CHILD;
    }
}

And Child class refactored

@Entity
public class Child implements Serializable {

    private MutableInt id = new MutableInt();

    private String childField;
    private Parent parent;

    @Transient 
    public MutableInt getIdAsMutableInt() {
        return this.id;
    }

    @Id
    @GeneratedValue
    public Integer getId() {
        return this.id.intValue();
    }

    public void setId(Integer id) {
        this.id.setValue(id);
    }

    public String getChildProperty() {
        return childField;
    }

    @OneToOne(fetch=fetch=FetchType.LAZY)
    @PrimaryKeyJoinColumn
    @Cascade(CascadeType.SAVE_UPDATE)
    public Parent getParent() {
        return this.parent;
    }

}

To save our refactored code,

Child child = new Child();

Parent parent = new Parent();
parent.setParentType(ParentType.CHILD);

/**
  * Workaround as a way both Parent and Child class share the same id
  */ 
parent.setIdAsMutableInt(child.getIdAsMutableInt());

Serializable savedId = hibernateTemplate.save(child);

Now it works fine!

Parent parent = hibernateTemplate.get(Parent.class, savedId);
Arthur Ronald F D Garcia
+2  A: 

An workaround is described as bellow

Define your Parent class as MapedSuperClass. Let's suppose Parent class is mapped To PARENT_TABLE

@MapedSuperClass
public abstract class AbstractParent implements Serializable {

    @Id
    @GeneratedValue
    private long id;

    @Column(table="PARENT_TABLE")        
    private String someProperty;

    // getter's and setter's

}

For each subclass, extends AbstractParent class and define its SecondaryTable. Any persistent field defined in Parent class will be mapped To Table defined by SecondaryTable. And finally, use AttributeOverrides if needed

@Entity
@SecondaryTable("PARENT_TABLE")
public class Child extends AbstractParent {

    private String childField;

    public String getChildProperty() {
        return childField;
    }

}

And define another Entity with the purpose of retrieving just The parent class

@Entity
@Table(name="PARENT_TABLE")
@AttributeOverrides({
    @AttributeOverride(name="someProperty", column=@Column(name="someProperty"))
})
public class Parent extends AbstractParent {}

Nothing else. See as shown above i have used just JPA specific annotations

Arthur Ronald F D Garcia
Pretty neat idea!
calvinkrishy