title: how to map Many to Many with composite key
body: <p>I have the following tables</p>
  
  <pre><code>Trainingplan
      TrainingplanID int(11) AI PK
      Trainer int(11)
      Client int(11)
      validFrom date
      validTo date
      type int(11)
  
  TrainingplanExercises
      trainingplan int(11) PK
      exercise int(11) PK
      parameter int(11) PK
      value varchar(45)
  </code></pre>
  
  <p>No I have problems connecting them with Hibernate. I did the following:
  package beans;</p>
  
  <pre><code>@Entity
  @Table(name = "Trainingplan")
  public class Training {
  
      private IntegerProperty id;
      private ObjectProperty&lt;Person&gt; client;
      private ObjectProperty&lt;Person&gt; trainer;
      private ObjectProperty&lt;Date&gt; validFrom;
      private ObjectProperty&lt;Date&gt; validTo;
      private ObjectProperty&lt;TrainingplanType&gt; type;
      private List&lt;TrainingplanExercise&gt; exercises;
  
      public Training(int id, Person client, Person trainer, Date validFrom, Date validTo, TrainingplanType type) {
          this.id = new SimpleIntegerProperty(id);
          this.client = new SimpleObjectProperty&lt;&gt;(client);
          this.trainer = new SimpleObjectProperty&lt;&gt;(trainer);
          this.validFrom = new SimpleObjectProperty&lt;&gt;(validFrom);
          this.validTo = new SimpleObjectProperty&lt;&gt;(validTo);
          this.type = new SimpleObjectProperty&lt;&gt;(type);
          exercises = FXCollections.observableArrayList();
      }
  
      public Training(Person client, Person trainer, Date validFrom, Date validTo, TrainingplanType type){
          this(0, client, trainer, validFrom, validTo, type);
      }
  
      public Training(){
          this(0, null,null,null,null, null);
      }
  
      @OneToOne
      @JoinColumn(name = "client")
      public Person getClient() {
          return client.get();
      }
  
      public ObjectProperty&lt;Person&gt; clientProperty() {
          return client;
      }
  
      public void setClient(Person client) {
          this.client.set(client);
      }
  
      @OneToOne
      @JoinColumn(name = "trainer")
      public Person getTrainer() {
          return trainer.get();
      }
  
      public ObjectProperty&lt;Person&gt; trainerProperty() {
          return trainer;
      }
  
      public void setTrainer(Person trainer) {
          this.trainer.set(trainer);
      }
  
      @Column
      public Date getValidFrom() {
          return validFrom.get();
      }
  
      public ObjectProperty&lt;Date&gt; validFromProperty() {
          return validFrom;
      }
  
      public void setValidFrom(Date validFrom) {
          this.validFrom.set(validFrom);
      }
  
      @Column
      public Date getValidTo() {
          return validTo.get();
      }
  
      public ObjectProperty&lt;Date&gt; validTillProperty() {
          return validTo;
      }
  
      public void setValidTo(Date validTill) {
          this.validTo.set(validTill);
      }
  
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      @Column(name = "TrainingplanID")
      public int getId() {
          return id.get();
      }
  
      public IntegerProperty idProperty() {
          return id;
      }
  
      public void setId(int id) {
          this.id.set(id);
      }
  
      @OneToOne
      @JoinColumn(name = "type")
      public TrainingplanType getType() {
          return type.get();
      }
  
      public ObjectProperty&lt;TrainingplanType&gt; typeProperty() {
          return type;
      }
  
      public void setType(TrainingplanType type) {
          this.type.set(type);
      }
  
      @ManyToMany()
      @JoinTable(name="TrainingplanExercises",
              joinColumns={@JoinColumn(name="trainingplan")},
              inverseJoinColumns={@JoinColumn(name="trainingplan"), @JoinColumn(name="exercise"), @JoinColumn(name="parameter")})
      public List&lt;TrainingplanExercise&gt; getExercises() {
          return exercises;
      }
  
      public void setExercises(List&lt;TrainingplanExercise&gt; exercises) {
          this.exercises = exercises;
      }
  
      @Override
      public String toString() {
          return "Training{" +
                  "id=" + getId() +
                  ", client=" + getClient() +
                  ", trainer=" + getTrainer() +
                  ", validFrom=" + getValidFrom() +
                  ", validTill=" + getValidTo() +
                  ", type=" + getType() +
                  '}';
      }
  
      @Override
      public boolean equals(Object o) {
          if (this == o) return true;
          if (o == null || getClass() != o.getClass()) return false;
  
          Training training = (Training) o;
  
          return id != null ? id.equals(training.id) : training.id == null;
  
      }
  
      @Override
      public int hashCode() {
          return id != null ? id.hashCode() : 0;
      }
  }
  </code></pre>
  
  <p>TrainingplanExercise.java</p>
  
  <pre><code>@Entity
  @Table(name = "TrainingplanExercises")
  @IdClass(TrainingplanExerciseId.class)
  public class TrainingplanExercise {
  
      private ObjectProperty&lt;Exercise&gt; exercise;
      private ObjectProperty&lt;Training&gt; training;
      private ObjectProperty&lt;String&gt; value;
      private ObjectProperty&lt;Parameter&gt; parameter;
  
      public TrainingplanExercise(Exercise exercise, Training training, String value, Parameter parameter){
          this.exercise = new SimpleObjectProperty&lt;&gt;(exercise);
          this.training = new SimpleObjectProperty&lt;&gt;(training);
          this.value = new SimpleObjectProperty&lt;&gt;(value);
          this.parameter = new SimpleObjectProperty&lt;&gt;(parameter);
      }
  
      public TrainingplanExercise(){
          this(null,null,null,null);
      }
  
      @Id
      @OneToOne
      @JoinColumn(name = "parameter")
      public Parameter getParameter() {
          return parameter.get();
      }
  
      public ObjectProperty&lt;Parameter&gt; parameterProperty() {
          return parameter;
      }
  
      public void setParameter(Parameter parameter) {
          this.parameter.set(parameter);
      }
  
      @Id
      @OneToOne
      @JoinColumn(name = "exercise")
      public Exercise getExercise() {
          return exercise.get();
      }
  
      public ObjectProperty&lt;Exercise&gt; exerciseProperty() {
          return exercise;
      }
  
      public void setExercise(Exercise exercise) {
          this.exercise.set(exercise);
      }
  
      @Id
      @OneToOne
      @JoinColumn(name = "trainingplan")
      public Training getTraining() {
          return training.get();
      }
  
      public ObjectProperty&lt;Training&gt; trainingProperty() {
          return training;
      }
  
      public void setTraining(Training training) {
          this.training.set(training);
      }
  
      @Column(name = "value")
      public String getValue(){
          return value.get();
      }
  
      public ObjectProperty&lt;String&gt; valueProperty() {
          return value;
      }
  
      public void setValue(String value) {
          this.value.set(value);
      }
  
      @Override
      public String toString() {
          return "TrainingplanExercise{" + "exercise=" + exercise + ", training=" + training + ", value=" + value + '}';
      }
  
  }
  
   class TrainingplanExerciseId implements Serializable{
       protected ObjectProperty&lt;Exercise&gt; exercise;
       protected ObjectProperty&lt;Training&gt; training;
       protected ObjectProperty&lt;Parameter&gt; parameter;
  
       public TrainingplanExerciseId() {
           if(exercise == null)
               exercise = new SimpleObjectProperty&lt;&gt;(null);
  
           if(training == null)
               training = new SimpleObjectProperty&lt;&gt;(null);
  
           if(parameter == null)
               parameter = new SimpleObjectProperty&lt;&gt;(null);
       }
  
       public TrainingplanExerciseId(ObjectProperty&lt;Exercise&gt; exercise, ObjectProperty&lt;Training&gt; training, ObjectProperty&lt;Parameter&gt; parameter) {
           this.exercise = exercise;
           this.training = training;
           this.parameter = parameter;
       }
  
       @Override
       public boolean equals(Object o) {
           if (this == o) return true;
           if (o == null || getClass() != o.getClass()) return false;
  
           TrainingplanExerciseId that = (TrainingplanExerciseId) o;
  
           if (exercise != null ? !exercise.equals(that.exercise) : that.exercise != null) return false;
           if (training != null ? !training.equals(that.training) : that.training != null) return false;
           return parameter != null ? parameter.equals(that.parameter) : that.parameter == null;
  
       }
  
       @Override
       public int hashCode() {
           int result = exercise != null ? exercise.hashCode() : 0;
           result = 31 * result + (training != null ? training.hashCode() : 0);
           result = 31 * result + (parameter != null ? parameter.hashCode() : 0);
           return result;
       }
  
       public Exercise getExercise() {
           return exercise.get();
       }
  
       public ObjectProperty&lt;Exercise&gt; exerciseProperty() {
           return exercise;
       }
  
       public void setExercise(Exercise exercise) {
           this.exercise.set(exercise);
       }
  
       public Training getTraining() {
           return training.get();
       }
  
       public ObjectProperty&lt;Training&gt; trainingProperty() {
           return training;
       }
  
       public void setTraining(Training training) {
           this.training.set(training);
       }
  
       public Parameter getParameter() {
           return parameter.get();
       }
  
       public ObjectProperty&lt;Parameter&gt; parameterProperty() {
           return parameter;
       }
  
       public void setParameter(Parameter parameter) {
           this.parameter.set(parameter);
       }
   }
  </code></pre>
  
  <p>Now when I want to save a new Training, I get this error:</p>
  
  <pre><code>Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'TrainingplanID' in 'field list'
  </code></pre>
  
  <p>Because of this SQL: </p>
  
  <pre><code>Hibernate: insert into TrainingplanExercises (TrainingplanID, trainingplan, exercise, parameter) values (?, ?, ?, ?)
  </code></pre>
  
  <p>How do I fix this?
  If I change the joinColumn to "trainingplan" I get the error that there are two same columns. If I remove "trainingplan" from the reversed columns, I get an error that one is missing because the foreign constraint requires 3 columns</p>
  
  <p>EDIT:
  Try something from the comments. I did try OneToMany/ManyToOne:</p>
  
  <pre><code>@Id
  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "trainingplan", nullable = false)
  public Training getTraining() {
      return training.get();
  }
  
  @OneToMany(fetch = FetchType.EAGER, mappedBy = "training")
  public List&lt;TrainingplanExercise&gt; getExercises() {
      return exercises;
  }
  </code></pre>
  
  <p>If I try saving a training to the DB now, it works. 
  Let's say I want to get a Trainingplan from the database, and add new TrainingplanExercises. I would use this code:</p>
  
  <pre><code>Exercise ex = (Exercise) db.getAll(Exercise.class).get(1);
  
  
  Training t = (Training) db.getAll(Training.class).get(0);
  
  
  TrainingplanExercise te = new TrainingplanExercise(ex, t, "asdf", ex.getParameters().get(0));
  TrainingplanExercise te1 = new TrainingplanExercise(ex, t, "asdf", ex.getParameters().get(1));
  TrainingplanExercise te2 = new TrainingplanExercise(ex, t, "asdf", ex.getParameters().get(2));
  TrainingplanExercise te3 = new TrainingplanExercise(ex, t, "asdf", ex.getParameters().get(3));
  
  
  
  t.getExercises().clear();
  t.getExercises().add(te);
  t.getExercises().add(te1);
  t.getExercises().add(te2);
  t.getExercises().add(te3);
  
  db.updateObj(t);
  </code></pre>
  
  <p>I get this exception:</p>
  
  <pre><code>Exception in thread "main" org.hibernate.exception.LockTimeoutException: could not execute statement
      at org.hibernate.dialect.MySQLDialect$1.convert(MySQLDialect.java:447)
      at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
      at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126)
      at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:112)
      at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:211)
      at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:62)
      at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3124)
      at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3581)
      at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:104)
      at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:465)
      at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351)
      at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
      at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
      at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1258)
      at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)
      at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
      at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)
      at db.Database.updateObj(Database.java:100)
      at db.Database.main(Database.java:171)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:497)
      at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
  Caused by: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction
      at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:998)
      at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3835)
      at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3771)
      at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2435)
      at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2582)
      at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2535)
      at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1911)
      at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2145)
      at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2081)
      at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2066)
      at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:208)
      ... 19 more
  </code></pre>