How to implement two entities having Composite Key?

We have 3 entities: Event, EventDetail and Company.
Both Event and EventDetail have a Composite Primary Key comprising of Sequence and Company.
We have this because we are using Postgres and these are partitioned tables partitioned on Company, so that has to be part of the Primary Key.

Our entities looks like this, but we are getting the following exception.
Error:

Caused by: java.lang.ClassCastException: org.hibernate.mapping.Formula cannot be cast to org.hibernate.mapping.Column

What is the right way to model these entities in Hibernate?

Event.java

@Entity
@Access(AccessType.PROPERTY)
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Event implements Serializable {

    private Long id;
    private Company company;
    private Set<EventDetail> eventDetailSet;
    @Id
    @Column(name = "EVENT_SEQ")
    public Long getId() {
        return id;
    }

    @JoinColumn(name = "COMPANY_FK")
    @ManyToOne
    @Id
    public Company getCompany() {
        return company;
    }

    @OneToMany(cascade =  CascadeType.MERGE  , mappedBy = "event" )
    Set<EventDetail> getEventDetailSet()
    {
        return eventDetailSet;
    }
}

EventDetail.java

@Entity
@Access(AccessType.PROPERTY)
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class EventDetail implements Serializable {

    private Long id;
    private Company company;
    private Event event;
    @Id
    @Column(name = "EVENT_DETAIL_SEQ")
    public Long getId() {
        return id;
    }


    @JoinColumn(name = "COMPANY_FK")
    @ManyToOne
    @Id
    public Company getCompany() {
        return company;
    }

    @ManyToOne(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
    @JoinColumnsOrFormulas(
            value = {
                    @JoinColumnOrFormula(column = @JoinColumn(referencedColumnName = "EVENT_SEQ", name = "EVENT_FK")),
                    @JoinColumnOrFormula(formula = @JoinFormula(referencedColumnName = "COMPANY_FK", value = "COMPANY_FK"))
            })
    public Event getEvent() {
        return event;
    }
}

Why are you using formula = @JoinFormula(referencedColumnName = "COMPANY_FK", value = "COMPANY_FK") instead of column = @JoinColumn(referencedColumnName = "COMPANY_FK", name = "COMPANY_FK"))? It looks to me like that should work fine, you don’t need any formulas here.

I suggest the following mapping instead:

@Entity
@Access(AccessType.PROPERTY)
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class EventDetail implements Serializable {

    private Long id;
    private Company company;
    private Long eventId;
    private Event event;

    @Id
    @Column(name = "EVENT_DETAIL_SEQ")
    public Long getId() {
        return id;
    }

    @JoinColumn(name = "COMPANY_FK")
    @ManyToOne
    @Id
    public Company getCompany() {
        return company;
    }

    @JoinColumn(name = "EVENT_FK")
    public Long getEventId() {
        return eventId;
    }

    void setEventId(Long eventId) {
        this.eventId = eventId;
    }

    @ManyToOne(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
    @JoinColumn(referencedColumnName = "EVENT_SEQ", name = "EVENT_FK", insertable = false, updatable = false),
    @JoinColumn(referencedColumnName = "COMPANY_FK", value = "COMPANY_FK", insertable = false, updatable = false)
    public Event getEvent() {
        return event;
    }

    public void setEvent(Event event) {
        if ( event == null ) {
            this.event = null;
            this.eventId = null;
        } else {
            this.event = event;
            this.eventId = event.getId();
        }
    }

}

@mbladel We can’t directly use JoinColumn as EventDetail already has a Company field which also uses the JoinColumn COMPANY_FK. So Hibernate won’t allow COMPANY_FK as another JoinColumn.

@beikov Thanks for this mapping. We also moved to this mapping, but doesn’t hibernate have any other better way to map this kind of relationships?
With more people moving to Postgres, and Postgres asking Partition Key to be part of Primary Key. We will see more such scenarios.

Also, we saw another bug in these cases in Hibernate 5.6:

Issue: When an Entity is having 2 JoinColumns for a @ManyToOne relationsip, even if one of the JoinColumn is null, Hibernate is creating a Proxy Object for the One side Entity.
And if it later tries to resolve this proxy, an ObjectNotFoundException exception is thrown.

Issue Repo: GitHub - Akshit97/hibernate-test-case-templates: Templates and examples to report issues to Hibernate

@beikov I have created a JIRA for this issue: [HHH-17915] - Hibernate JIRA

I think this change should fix this issue, can you please review this?
Fix PR: HHH-17915 - Fix Proxy Creation in case of null property by Akshit97 · Pull Request #8087 · hibernate/hibernate-orm · GitHub

We also moved to this mapping, but doesn’t hibernate have any other better way to map this kind of relationships?

Unfortunately, there is no better way to model this right now.

Also, we saw another bug in these cases in Hibernate 5.6:

Hibernate ORM 5.6 only receives limited support at this time, so according to our maintenance policy, there will most probably not be another release for that version family.

I suggest you to upgrade to Hibernate ORM 6.4 or later.

@beikov We have a legacy monolithic application running on Java 8, so upgrading to Hibernate 6 is not very straight-forward for us. We are working on the upgrade part.

But for the time-being can you please review the fix:

Even if there won’t be a release for 5.6, we can create an aspect and solve the issue for us, just want your review once.
Do you see any problems with this fix? Your review will be helpful here.

@beikov Looks like this is a known issue in Hibernate 5.6

We have a legacy monolithic application running on Java 8, so upgrading to Hibernate 6 is not very straight-forward for us. We are working on the upgrade part.
Even if there won’t be a release for 5.6, we can create an aspect and solve the issue for us, just want your review once.
Do you see any problems with this fix? Your review will be helpful here.

I understand that, but I can’t really spend time on 5.6 unless you are a Red Hat customer and request a fix through a support ticket.