0

I have an entity for storing Workshops where I am using a composite primary key.

I do not like the concept of auto-generated keys and until now have mostly used business derived keys, such as email id etc for entities. However over here, the workshop entity did not appear to have a natural candidate for primary key, so I went for a composite key.I created a WorkshopIdType which is an enum containing the three possible workshops that will be organised

public enum WorkshopIdType implements Serializable {

  FOUNDATION("FND"), INTERMEIDATE("IMT"), ADVANCED("ADV");

  private final String name;

  private WorkshopIdType(String name) {
    this.name = name;
  }

  @Override
  public String toString() {
    return this.name;
  }

  public boolean equals(String otherName) {
    return (otherName == null) ? false : name.equals(otherName);
  }
}

And then I have an Embeddable class for primary key; the combination of workshop type and date appears to me as the best fit for primary key in this scenario

@Embeddable
@Access(AccessType.FIELD)
public class WorkshopId implements Serializable {

  private static final long serialVersionUID = -7287847106009163526L;

  private String workshopIdType;

  private Date date;

  @Column(name = "id", nullable = false)
  public String getWorkshopIdType() {
    return workshopIdType;
  }

  public void setWorkshopIdType(WorkshopIdType workshopIdType) {
    this.workshopIdType = workshopIdType.toString();
  }

  @Temporal(TemporalType.DATE)
  @Column(name = "date", nullable = false)
  public Date getDate() {
    return date;
  }

  public void setDate(Date date) {
    this.date = date;
  }
}

The entity also has a ManyToOne relationship with Venue, here again, the Venues are actually 5 of the pre-designated centres across three cities

@Entity
public class Workshop implements Serializable {

  private static final long serialVersionUID = -5516160437873476233L;

  private WorkshopId id;

  private Venue venue;

  private Integer seatsAvailable;

  @EmbeddedId
  public WorkshopId getId() {
    return id;
  }

  public void setId(WorkshopId id) {
    this.id = id;
  }

  @ManyToOne
  @JoinTable(name = "workshop_venue", joinColumns = { @JoinColumn(name = "workshop_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "venue_name", referencedColumnName = "name") })
  public Venue getVenue() {
    return venue;
  }

  public void setVenue(Venue venue) {
    this.venue = venue;
  }

  @Column(name = "seats_available", nullable = false)
  public Integer getSeatsAvailable() {
    return seatsAvailable;
  }

  public void setSeatsAvailable(Integer seatsAvailable) {
    this.seatsAvailable = seatsAvailable;
  }
}

Problem is mapping this ManyToOne with a JoinTable in case of a composite key

@ManyToOne
  @JoinTable(name = "workshop_venue", joinColumns = { @JoinColumn(name = "workshop_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "venue_name", referencedColumnName = "name") })
  public Venue getVenue() {
    return venue;
  }

This won't work as I had suspected, it cannot find a column with logical name "id". I am going with ManyToOne with JoinTable because there can be a scenario where users should get to know is there a training scheduled for the given Venue. How do I specify the referencedColumnName in this case?

Either that or I got it all wrong in the way I am modelling this?

Anadi Misra
  • 1,925
  • 4
  • 39
  • 68
  • Not quite sure, but it looks like this http://stackoverflow.com/questions/6405746/mapping-manytomany-with-composite-primary-key-and-annotation – Observer Jul 29 '15 at 14:48
  • "*I do not like the concept of auto-generated keys*" - that's your call obviously, but it sure would be awful if you misspelt one of your natural id column values and then had to change it once your application goes live, maybe like `INTERMEIDATE` – Andy Brown Jul 29 '15 at 15:03
  • I really have no clues what the guy is up-to in that question, looks all cluttery and overcomplicated. – Anadi Misra Jul 29 '15 at 15:28
  • @AndyBrown good point, usually these keys would be something which is invariably not null like a value provided through a form which user chooses, or social login where we are sure we get that key, or building name, department name etc etc. In this case I in a way enforcing the same through an `Enum` and date, it would be compulsory for the admin to choose what kind of workshop & the date when creating a workshop so misspelt names are out of question. – Anadi Misra Jul 29 '15 at 15:34
  • oops! I see the typo here now :-), point taken! – Anadi Misra Jul 29 '15 at 15:36

1 Answers1

0

You have a composite Primary Key represented by the embeddable WorkshopId embedded in Workshop. Both fields of the Primary Key need to be joined to the target entity in the link table – therefore you need 2 joincolumns and one inversejoin. You just need to add the missing join;

  @ManyToOne
  @JoinTable(name = "workshop_venue", 
            joinColumns = 
                  { @JoinColumn(name = "workshop_id", referencedColumnName = "id"),
                    /* Add this joincolumn */ 
                    @JoinColumn(name = "date_id", referencedColumnName = "date") }, 
            inverseJoinColumns = 
                  { @JoinColumn(name = "venue_name", referencedColumnName = "name") })
  public Venue getVenue() {
    return venue;

Of course you need to ensure that you link table has these three fields. I guess you are missing the date_id column.

NickJI
  • 466
  • 3
  • 12