Polymorphic associations
Polymorphic associations are used in cases when there are several one-to-many relationships that you could aggregate because for all the parents, the children are similar.
In other words, a polymorphic association allows for a table to have multiple types of parents.
Lets say you have products and reviews in the system . Both have to be tagged. A naive implementation would be to create two one to many relationships:
- Product has many ProductTag(s)
- Review has many ReviewTag(s)
This will work? kind of, but it will violate a DRY principle because
the PRODUCT_TAG
and REVIEW_TAG
tables will be
identical (except for names!).
A better approach would be to use Polymorphic associations. In the
Polymorphic associations , you would create one table called
TAGS
, and add two columns to this table, besides the ones that
you need:
PARENT_ID
PARENT_TYPE
After this, you will need to provide one last bit of information to the framework by specifying relationships:
public class Product extends Model{}
public class Review extends Model{}
@BelongsToPolymorphic(parents = {Product.class, Review.class})
public class Tag extends Model{}
This annotation tells ActiveJDBC that Product has many Tags as well as Review has many tags too. The annotation itself is easy to understand if you read it aloud.
Operations are the same as with One-to-many associations
Adding and searching for polymorphic children
Product p = Product.findById(100);
p.add(Tag.create("tag", "basket"));
p.add(Tag.create("tag", "toy"));
List<Tag> tags = p.getAll(Tag.class);
...iterate
Review customerReview = Review.findById(2024);
customerReview.add(Tag.create("tag", "fun"));
customerReview.add(Tag.create("tag", "useful"));
List<Tag> tags = customerReview.getAll(Tag.class);
... iterate
The table TAG content might look like this after operations above:
id | tag | parent_id | parent_type |
---|---|---|---|
1 | toy | 100 | com.acme.Product |
2 | basket | 100 | com.acme.Product |
3 | fun | 2024 | com.acme.Review |
4 | useful | 2024 | com.acme.Review |
Conditional search for polymorphic children
While the getAll(type)
method returns all relations, the
get(type)
method allows for a selection criteria on the
child table:
Removing polymorphic children
Removing children is as easy as expected;
This will remove a child record from the TAGS
table.
Deleting polymorphic parents
When deleting a record that is a parent to polymorphic children, you have two options:
- Only delete the parent itself. This will leave orphan children:
- Delete parent along with all the children:
The latter will delete the parent along with all associated polymorphic children. See Delete cascade for more information.
Finding polymorphic parent
Navigate from children to parents in relationships:
Override standard parent type values
In some cases, it is not possible to have a fully qualified class
name in the parent_type
column. This is usually a case when the
same table backs a different ORM which also supports polymorphic
associations (Ruby on Rails ActiveRecord for example).
When faced with this problem, you can use annotation to override default behavior:
@BelongsToPolymorphic(
parents = { Vehicle.class, Mammal.class},
typeLabels = {"Vehicle", "Mammal"} )
public class Classification extends Model {}
This defines polymorphic associations between models Classification,
Mammal and Vehicle, such that the parent_type
column of
CLASSIFICATIONS
table will contain values
Vehicle
and Mammal
for corresponding parent
records from VEHICLE
and MAMMAL
tables. The
order of parent classes and type labels is important, they must
correspond to one another.
Here is an example of CLASSIFICATIONS table:
id | name | parent_id | parent_type |
---|---|---|---|
1 | four wheeled | 100 | Vehicle |
2 | sedan | 23 | Vehicle |
3 | four legged | 2024 | Mammal |
4 | furry | 2023 | Mammal |
How to comment
The comment section below is to discuss documentation on this page.
If you have an issue, or discover bug, please follow instructions on the Support page