Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle JPA CollectionTable with Inheritance?

I have to create a hierarchy of classes where a superclass has a @CollectionTable representing a map. I tried to implement it but it works only with one child class.

I want to do the following. I have the current structure on the left, and the desired structure on the right. UML diagram of the structure

The stable (working) code looks like this:

@MappedSuperclass
public class Animal {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", updatable = false, nullable = false)
    private Long id;
}

@Entity(name = "cats")
@Audited
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.CHAR)
@DiscriminatorValue(value = Cat.PET_TYPE)
public abstract class Cat extends Animal {
    public static final String PET_TYPE = "C";

    @ElementCollection(fetch = FetchType.EAGER)
    @MapKeyColumn(name = "name")
    @Column(name = "value")
    @CollectionTable(name = "cat_properties", joinColumns = @JoinColumn(name = "cat_id"))
    private Map<String, String> properties = new HashMap<>();
}

@Audited
@Entity(name = "persiancats")
@DiscriminatorValue(value = PersianCat.PERSIAN_CAT_TYPE)
public class PersianCat extends Cat {
    public static final String PERSIAN_CAT_TYPE = "P";
}

This is how I tried to achieve the modification:

@MappedSuperclass
public class Animal {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", updatable = false, nullable = false)
    private Long id;
}

@MappedSuperclass
public abstract class Pet extends Animal {
    @ElementCollection(fetch = FetchType.EAGER)
    @MapKeyColumn(name = "name")
    @Column(name = "value")
    @CollectionTable(name = "pet_properties", joinColumns = @JoinColumn(name = "pet_id"))
    private Map<String, String> properties = new HashMap<>();
}

@Entity(name = "cats")
@Audited
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.CHAR)
@DiscriminatorValue(value = Cat.PET_TYPE)
public class Cat extends Pet {
    public static final String PET_TYPE = "C";
}

@Entity(name = "dogs")
@Audited
public class Dog extends Pet {
}

@Audited
@Entity(name = "persiancats")
@DiscriminatorValue(value = PersianCat.PERSIAN_CAT_TYPE)
public class PersianCat extends Pet {
    public static final String PERSIAN_CAT_TYPE = "P";
}

Hibernate creates the pet_properties table but it only references either dogs or cats. My intention was to create a common table for both dog and cat (and persiancat) properties.

What am I missing or doing wrong?

like image 639
Csuki Avatar asked Nov 09 '22 07:11

Csuki


1 Answers

I guess what you're trying to achieve is logically impossible, and here's why (I stumbled upon the same problem, although in another domain). To reference a table from your pet_properties table Hibernate creates a foreign key from pet_properties to the target table (dogs or cats in your case). A foreign key essentially means to every entry in this table should correspond an entry from another table, and that another table is only one table. Therefore, if you said that your pet_properties entry references both dogs and cats tables, then it will immediately break the foreign key properties, because if it references dogs, then it doesn't reference cats, and vice versa.

So the auto-generated option that Hibernate gave you (separate tables dogs_properties and cats_properties) seems to be the correct way.

like image 103
Scadge Avatar answered Nov 14 '22 23:11

Scadge