4

I have the following entities:

@Entity
@Table(name = "place_revision")
public class PoiRevision {
    @OneToMany(mappedBy = "pk.revision", cascade = {CascadeType.ALL})
    private Collection<PoiRevisionCategory> categoryMapping;
    // ...
}

@Entity
@Table(name = "place_revision__category")
@AssociationOverrides({
    @AssociationOverride(name = "pk.revision",
        joinColumns = @JoinColumn(name = "place_revision_id")),
    @AssociationOverride(name = "pk.category",
        joinColumns = @JoinColumn(name = "category_id"))
})
public class PoiRevisionCategory {
    @EmbeddedId
    private PoiRevisionCategoryId pk = new PoiRevisionCategoryId();
    // ...
}

@Embeddable
public class PoiRevisionCategoryId implements Serializable {
    @ManyToOne
    private PoiRevision revision;
    @ManyToOne
    private Category category;
    // ...
}

@Entity
@Table(name = "category")
public class Category {
    @ManyToMany(targetEntity = Section.class, cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
    @JoinTable(
        name = "category__section",
        joinColumns = @JoinColumn(name = "category_id"),
        inverseJoinColumns = @JoinColumn(name = "section_id")
    )
    private Collection<Section> sections;
    // ...
}

And want to select PoiRevisions that have Categories that have some Sections. I'm using Spring-data Specification to query the database for these entities.

My intent is to write something like:

Specification<PoiRevision> spec = new Specification<PoiRevision>() {
    @Override
    public Predicate toPredicate(Root<PoiRevision> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
        List<Predicate> conditions = new ArrayList<>(CONDITION_COUNT);

        CollectionJoin<PoiRevision, PoiRevisionCategory> mapping = root.join(PoiRevision_.categoryMapping);
        // BROKEN here as we cannot use nested path for joins
        Join<PoiRevisionCategory, Category> categories = mapping.join("pk.category"); 
        conditions.add(categories.get("sections").in(sections));

        // ...

        return cb.and(conditions.toArray(new Predicate[] {}));
    }
};

But we cannot use nested path for such joins as JPA provider (Hibernate, in my case) looks only for direct properties of PoiRevisionCategory class. And we cannot "join" embedded Id to our result set because it's not a manageable entity.

I'm really stuck with this issue which seems to be far from complicated when translated into SQL yet it has some complexity on the ORM-side.

Any idea is much appreciated.

Alexander Nyrkov
  • 445
  • 1
  • 5
  • 12
  • In [this thread](http://stackoverflow.com/a/8657852/1267257) @gustavo mentions some `persistence.xml` setting to enable `@Embedded` entities management by hibernate. Sounds like this may help but probably can have some downsides as well. But I cannot find this parameter yet. – Alexander Nyrkov Mar 26 '14 at 13:59

1 Answers1

8

After switching completely to metamodel API it became clearer and I was actually able to join embedded entity just like I tried and failed with string api.

So the correct way is just to join like one would normally do

Join<PoiRevisionCategory, PoiRevisionCategoryId> pk = mapping.join(PoiRevisionCategory_.pk);
Join<PoiRevisionCategoryId, Category> cats = pk.join(PoiRevisionCategoryId_.category);
CollectionJoin<Category, Section> sec = cats.join(Category_.sections);
conditions.add(sec.get(Section_.id).in(sections));

And it does the thing just fine!

What a relief.

Alexander Nyrkov
  • 445
  • 1
  • 5
  • 12
  • How did you make it generate a metamodel for your embeddedID? No metamodel exists for mine... – Alkanshel Feb 20 '17 at 19:36
  • @Amalgovinus, Hi, it's been a while since I've written this and I no longer have access to the code itself, but I do not remember any issues with metamodel generation. – Alexander Nyrkov Feb 22 '17 at 07:38
  • I wound up just doing away with the embeddedID and adding a surrogate key, it's too much of a pain to manage a natural key with the criteria API – Alkanshel Feb 22 '17 at 19:29
  • Cannot blame you for that :) It's often much easier to go with surrogate keys when dealing with ORMs. I was just obsessed with a database-purist point of view back in the day. – Alexander Nyrkov Feb 27 '17 at 15:45
  • @Amalgovinus Metamodel generation can be done using hibernate-jpamodelgen; which then needs to be configured/added to your maven pom, so it e.g. runs during maven 'generate sources' phase. Please consult [JPA-metamodels with maven](https://jinahya.wordpress.com/2014/03/29/jpa-metamodels-with-maven/) for some guidelines. – Stijn V Aug 21 '19 at 10:32