Sei sulla pagina 1di 4

30th August 2012 Recursive Polymorphic Queries with JPA

Introduction
The following post describes how to use and combine some of the advanced ORM concepts such as Object
Polymorphism and Recursive Queries using Java Persistence API. Usage of those approaches is shown on a relatively
simple, but functioning domain model of pet owners. The point is to demonstrate how to model a directed acyclic graph
(DAG) of diverse entities using relational database and query those using JPA. []

Implementation

Imagine the following Domain Model of Pet Owners. Now there are two complex things about it. Our Domain Model will
be enhanced with other Animals on regular basis so we need to handle all animals uniformly so they have a common
Animal class. Every concrete animal would have specific attributes like teeth and tail. Second thing is that we need to
support full family history of Pets and query for undefined depth of parent-child relationships.

Let's create JPA mapped objects now. Note that getters and setters are omitted in order to save space.

Let's start implementation with Person class. This one is pretty straightforward and contains a Set of Animals, which
could be either Cat or Dog or something else. There is a special convenience method adopt(), which maintains bi-
directional relationships between linked objects. This is actually a best practice in JPA not to run in the situation when link
has been defined only from one end.

@Entity
@Table(name = "OWNERS")
public class Person {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private long id;

@Column
String name;

@OneToMany
Set<Animal> animals = new HashSet<>();

public void adopt(Animal animal) {


getAnimals().add(animal);
animal.setOwner(this);
}
}

Animal is an abstract class and defines @DiscriminatorColumn, which would be used to determine the exact type of
each record. This column will hold a fully qualified name of the exact class representing this animal type (Do or Cat in our
example). In this case table per object polymorphism approach is used so there are separate tables per each animal type
in addition to common Animal table.

@Entity
@DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.STRING, length = 512)
@Table(name = "ANIMAL")
public abstract class Animal {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;

@Column String name;

@ManyToOne Person owner;

@OneToMany(mappedBy = "parent")
final Set<Relation> relations = new HashSet<>();
}

Relation is a simple class used for enabling querying family tree of animals. If this requirement would not be present it
would be possible to simply use @JoinTable annotation on Animal class property and not to create a separate Java class
at all. But in this case having a separate entity is handy as you will see.

@Entity
@Table(name = "RELATIONS")
public class Relation {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Long id;

@ManyToOne(optional = false)
Animal parent;

@ManyToOne(optional = false)
Animal child;
}

You could have many child objects, which extend Animal class and enrich the model with additional attributes. Let's add
our Cat and Dog classes. Note that @SecondaryTable annotation should define where the animal-specific data is stored.

@Entity
@SecondaryTable(name = "CAT")
public class Cat extends Animal {
@Column(table = "CAT")
String tail;
}

@Entity
@SecondaryTable(name = "DOG")
public class Dog extends Animal {
@Column(table = "DOG")
String teeth;
}

The following ER diagram depicts the solution from tables perspective. Table structure is auto-generated by Hibernate on
start-up, so just set the proper parameter in your persistence.xml.
[http://1.bp.blogspot.com/-A_xFN4tR9Kc/UD-
E_hkp60I/AAAAAAAAAEc/WPWckYewydo/s1600/ER.png]

Now the task is to get all children of certain animal recursively. It turns out that besides Oracle specific CONNECT BY
[http://www.adp-gmbh.ch/ora/sql/connect_by.html] syntax there is more portable way to do the recursive queries. Here it is.

WITH RECURSIVE TTT(id, parent_id, child_id) AS (


SELECT id, parent_id, child_id FROM relations WHERE parent_id = :id
UNION ALL
SELECT r.id, r.parent_id, r.child_id FROM relations r, TTT t WHERE r.parent_id = t.child_id
) SELECT * FROM TTT;

The first line defines a virtual table named TTT with three columns. It will be populated and referenced along query
execution. The first SELECT statement gets the starting point of the recursion. The second SELECT statement joins
existing data from previous iteration and table relations. This part is actually performing inside a loop, which stops as
long as no hits are returned by the query. With this query it is possible to walk parent-child hierarchy for indefinite depth
in relational databases supporting this syntax (PostgreSQL, MSSQL, Sybase are just some of them). This is just a
simplest example and you could define much more powerful "traversal" rules using WHERE condition and joining
additional tables.

This SQL could be passed via standard EntityManager API in order to get a list of Relation objects. As long the columns
returned corresponds to relation class properties it is possible to map results automatically. The cool thing is that there
could be Cats and Dogs relations returned and JPA handles that properly. Like in cross-breeding or crazy genetic
experiments...

Query query = entityManager.createNativeQuery("...", Relation.class);


query.setParameter("id", parentId);
List relations = query.getResultList();
for (Relation relation : resultList) {
System.out.println(relation.getParent().getName() + " is parent of " + relation.getChild().getName());
}

Now we have the capability to drill down through the graph of different types of objects using plain SQL and JPA. For
populated test dataset the code would return something like the following.

> Dog is parent of Smaller Dog


> Smaller Dog is parent of Cub
> Smaller Dog is parent of Strange Cat

Conclusions

There is no direct support in JPA for recursive queries, however you could fallback to native queries and do the trick. The
approach described in the post is actually quite well-performing and could scale beyond this simple example.

There are of course limitations to this approach. With a very diverse data model you will start hitting the boundaries of
your RDBMS SQL engine concerning the number of columns being able to be returned in single result set and maximum
number of joins performed in one SQL. But those will start to appear when you will have dozens of different animals in
place. More complex solutions will require usage of specialized graph databases (Neo4j or OrientDB for example) to
work around the stated limitations. But before hit that limit the demonstrated approach is quite elegant solution of a
common problem.

There are several different approaches you could sue to represent object hierarchy in relational database. Check a great
summary on graph DAG representations here [https://communities.bmc.com/communities/docs/DOC-9902] .

Word of warning: H2 [http://www.h2database.com/] database documentation claims to support recursive queries in


experimental mode, but I did not manage to make it return anything even though the parser accepts and executes the
SQL.

Posted 30th August 2012 by Dmitry Buzdin


Labels: java, jpa

0 Add a comment

Enter your comment...

Comment as: Equipo (Google) Sign out

Publish Preview Notify me

Potrebbero piacerti anche