0

I am trying to work with JPA inheritance in a tree structure in Java.

I have a concrete entity (Product) that has two concrete children (ProductA, ProductB)

Product has a Set<Products> (parents) , meant to contain any combination of ProductA and ProductB

Product has a Set<Products> (children), meant to contain any combination of ProductA and ProductB

ProductA and ProductB have the inherited children/parents, and some other properties.

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="product_type", discriminatorType = DiscriminatorType.STRING)
@Table(name = "PRODUCT")
class Product //CANNOT be an interface, abstract, or static
{
    @Id
    UUID id;

    @Column(name = "product_type", insertable = false, updatable = false)
    String productType;

    @ManyToMany
    @JsonManagedReference("product_children")
    Set<Product> children;

    @ManyToMany
    @JsonBackReference("product_children")
    Set<Product> parents;
}

@Entity
class ProductA extends Product
{
    String prodAProperty;
}

@Entity
class ProductB extends Product
{
    String prodBProperty;
}

This is all working fine in-code, and things are moving along well. They serialize, deserialize, and go into the DB without an issue, pivot tables are created, everything is saved correctly.

The trouble comes in when taking them back out. Let's say, we have the following:

rootProduct (ProductA)
    parents: null
    prodAProp: I am product A
    children: 
        child01 (ProductA)
            prodAProp: I am product A
            parents:
                rootProduct
            children: 
                child 03 (ProductB)
                    prodBProp: I am product B
                    parents:
                        child01 (ProductA)
                    children: null
        child02 (ProductB)
            childBProp: I am productB
            parents:
                rootProduct
            children: null

When using the following:

@Repository
public interface ProductRepository
        extends JpaRepository<Product, UUID> {

}

And running the following:

ProductRepository repo;
Product theProduct = repo.findById("UUID goes here");

What I end up with:

rootProduct (ProductA)
    parents: null
    prodAProp: I am product A
    children: 
        child01 (ProductA)
            prodAProp: I am product A
            parents:
                rootProduct
            children: 
                child 03 (Product)
                    parents:
                        child01 (ProductA)
                    children: null
        child02 (ProductB)
            childBProp: I am productB
            parents:
                rootProduct
            children: null

The difference is subtle, but the child-of-child (or any deeper down the tree) is not being cast into the sub-class, instead it is being set as base class. It also cannot be down-cast: (ProductB) child03 gives the error: Cannot convert Product into ProductB

I have tried various inheritance strategies, such as:

@MappedSuperclass
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Inheritance(strategy = InheritanceType.JOINED)

with various discriminator types

Thank you for your time in advance!

1 Answers1

0

If I grasp it right, then that's a problem of the types defined for your repository - if you want to persist different types (even if they are derived from a mutual super type), you have to create different repositories for different types. While deserializing, the JPA implementation uses reflective processes on the repository for type conversion - type conversion that's not capable of recognizing sub types.

See this answer for further information on a similar problem.