9

Consider this scenario.

There are plots, some are residential plots and some are commercial plots.

There are also owners. But an owner can buy only a plot and it can be residential or commercial.

So, here is my code.

@Entity
@Table(name = "PLOT")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Plot {
  private int id;
  private String number;
  private List<Owner> owners = new ArrayList<>();

  // getters and setters...
}


@Entity
@Table(name = "RESIDENTIAL_PLOT")
@PrimaryKeyJoinColumn(name = "PLOT_ID")
public class ResidentialPlot extends Plot {
  // Some fields
}


@Entity
@Table(name = "COMMERCIAL_PLOT")
@PrimaryKeyJoinColumn(name = "PLOT_ID")
public class CommercialPlot extends Plot {
  // Some fields
}


@Entity
@Table(name = "OWNER")
public class Owner {
  private int id;
  private String name;
  private Plot plot;

  // getters and setters
}

All works well, but when I call owner.getPlot() I was expecting ResidentialPlot or CommercialPlot instance So, I can apply appropriate operation by using instanceof operator. But it does not satisfy both conditions!

What am I doing wrong?

4
  • What instance you are getting?? Commented Nov 20, 2013 at 10:37
  • What type of operations are you applying? Commented Nov 20, 2013 at 10:39
  • Of course, it is of Plot class and it is not null, contains all valid information like number field. Commented Nov 20, 2013 at 10:39
  • @KevinBowersox Operation like I need to put label "Plot No." for ResidentialPlot and "Shop No." for Commercial Plot! Commented Nov 20, 2013 at 10:40

1 Answer 1

13

You're not using polymorphism, and it hurts even more in a JPA environment.

The reason you observe this behavior is because of the usage of proxies by Hibernate. When Hibernate loads an Owner, it can't tell if the plot is a commercial or residential plot. It would have to do an additional query to know it. So it initializes the plot field with a lazy proxy, which is an instance of a dynamically generated class which extends Plot, but is neither a CommercialPlot nor a ResidentialPlot.

When a method is called on the proxy, the proxy initializes itself by getting the Plot data from the database, and it delegates to an instance of CommercialPlot or ResidentialPlot.

The solution is to use ploymorphism. Add an abstract method to the Plot class (getType(), or isResidential(), for example), and implement it in both subclasses. If that isn't possible because you need business logic that depends on the entity type but shouldn't be in the entity itself, use the visitor pattern.

I wrote a blog article on this subject if you need more details, but it's in French. Maybe Google Translate could help you.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, learned new thing today. I think an abstract method would be fine for now :D
Nice answer, went to your blog but it's in French only, do you have an english version?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.