0

I'm trying to implement inheritance with a joined subclass strategy, while keeping track of the order of the list of combined entities.

I've got a SHOP table, with an ID and some metadata.

A SHOP has to maintain an ordered list of several types of entities, which have some metadata in common.

This is what I have at the moment in terms of entities and mappings:

@Entity
public class Shop extends PersistedEntity {

    // Persistend entity is a common class that declares the @Id column as:
    // @Id
    // @GeneratedValue(strategy = GenerationType.AUTO)
    // public int getId() {
    //  return this.id;
    // }

    // ... some generic fields

    private List<ShopAssets> shopAssets = new ArrayList<ShopAssets>();

    @ManyToMany(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
    @JoinTable(name = "ShopAssets", joinColumns = { @JoinColumn(name = "shop_id") }, inverseJoinColumns = { @JoinColumn(name = "id") })
    @OrderColumn(name="position")
    public List<ShopAssets> getShopAssets() {
        return shopAssets;
    }
}


@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING, name = "assetType")
public class ShopAsset extends PersistedEntity {

    // Again, the id integer column is inherited from PersistedEntity

    private Shop shop;
    private int position;
    private ShopFieldAsset keyAsset;

    @ManyToOne
    @JoinColumn(name = "shop_id")
    public Shop getShop() {
        return shop;
    }

    public int getPosition() {
        return position;
    }

    @ManyToOne
    @JoinColumn(name = "keyAsset_id")
    public ShopFieldAsset getKeyAsset() {
        return keyAsset;
    }

    // ... setters not included
}


@Entity
@DiscriminatorValue("field")
@PrimaryKeyJoinColumn(name = "id")
public class ShopFieldAsset extends ShopAsset {

    // ... bunch of standard string properties

}

@Entity
@DiscriminatorValue("common")
@PrimaryKeyJoinColumn(name = "id")
public class ShopCommonAsset extends ShopAsset {

    // ... bunch of standard string properties

}

@Entity
@DiscriminatorValue("expired")
@PrimaryKeyJoinColumn(name = "id")
public class ShopExpiredAsset extends ShopAsset {

    // ... bunch of standard string properties

}

Database tables are as follows (grossly):

Shop: id(PK). name, location
ShopAsset: id(PK), shop_id, assetType, position, keyAsset_id(FK on ShopFieldAsset[id])
ShopFieldAsset: id(PK and also FK to ShopAsset[id]), name, type, other standard columns);
ShopCommonAsset: id(PK and also FK to ShopAsset[id]), name, type, other standard columns);
ShopExpiredAsset: id(PK and also FK to ShopAsset[id]), name, type, other standard columns);

All I'm trying to do in my test is to create a shop and persist it with a collection of assets, maintaining the order in which they were added to the shop.

What I'm getting is:

org.hibernate.MappingException: Foreign key (FK_ep3g3p25oddon8apvq8kgji6w:ShopFieldAsset [id])) must have same number of columns as the referenced primary key (ShopAsset [shop_id,position])

And I just don't get where that primary key definition comes from...

I realised that derived entities may be getting in the way (the fact that any ShopAsset has a foreign key on the ShopFieldAsset subclass...), but I'm stumped as to how I should be defining the mappings.

As if that wasn't enough, if/when I get that to work, is there a clever way to have getters on a Shop to retrieve a collection of assets of a specific type, e.g. getShopCommonAssets() ? I briefly looked into using @Where, but obviously I left that one the side until I get the basics going!

Any help greatly appreciated.

Khain
  • 875
  • 2
  • 8
  • 11

1 Answers1

0

Amazing what a good night sleep will do.

I solved most of my problems:

  1. By getting rid of the @JoinTable annotation on the parent Shop entity. Looks like that's what was confusing the insert statements when trying to add a ShopAsset.

  2. By changing the subclasses to have this:

    @PrimaryKeyJoinColumn(name = "id", referencedColumnName = "id")

Instead of just

@PrimaryKeyJoinColumn(name = "id")

Stuff now seems happy, even the @OrderColumn is working ok.

I found an answer to help me with the last part of my question:

How to map collection of each subclass from same hierarchy onto one owning class?

But when I try that:

@OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER, mappedBy = "shop", targetEntity = TornadoLabel.class)
public List<ShopCommonAssets> getShopCommonAssets() {
    return shopCommonAssets;
}

I'm getting

org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: com.shopping.ShopCommonAsset.shop in com.shopping.Shop.shopCommonAssets

Which I guess is because my 'shop' property is on the parent class, unlike the example in the answer I've linked to.

Community
  • 1
  • 1
Khain
  • 875
  • 2
  • 8
  • 11