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