Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/394.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使用复合键映射多对多_Java_Hibernate_Many To Many - Fatal编程技术网

Java 如何使用复合键映射多对多

Java 如何使用复合键映射多对多,java,hibernate,many-to-many,Java,Hibernate,Many To Many,我有下列表格 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 v

我有下列表格

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)
不,我无法将它们与Hibernate连接。我做了以下工作: 包装豆

@Entity
@Table(name = "Trainingplan")
public class Training {

    private IntegerProperty id;
    private ObjectProperty<Person> client;
    private ObjectProperty<Person> trainer;
    private ObjectProperty<Date> validFrom;
    private ObjectProperty<Date> validTo;
    private ObjectProperty<TrainingplanType> type;
    private List<TrainingplanExercise> exercises;

    public Training(int id, Person client, Person trainer, Date validFrom, Date validTo, TrainingplanType type) {
        this.id = new SimpleIntegerProperty(id);
        this.client = new SimpleObjectProperty<>(client);
        this.trainer = new SimpleObjectProperty<>(trainer);
        this.validFrom = new SimpleObjectProperty<>(validFrom);
        this.validTo = new SimpleObjectProperty<>(validTo);
        this.type = new SimpleObjectProperty<>(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<Person> clientProperty() {
        return client;
    }

    public void setClient(Person client) {
        this.client.set(client);
    }

    @OneToOne
    @JoinColumn(name = "trainer")
    public Person getTrainer() {
        return trainer.get();
    }

    public ObjectProperty<Person> trainerProperty() {
        return trainer;
    }

    public void setTrainer(Person trainer) {
        this.trainer.set(trainer);
    }

    @Column
    public Date getValidFrom() {
        return validFrom.get();
    }

    public ObjectProperty<Date> validFromProperty() {
        return validFrom;
    }

    public void setValidFrom(Date validFrom) {
        this.validFrom.set(validFrom);
    }

    @Column
    public Date getValidTo() {
        return validTo.get();
    }

    public ObjectProperty<Date> 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<TrainingplanType> 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<TrainingplanExercise> getExercises() {
        return exercises;
    }

    public void setExercises(List<TrainingplanExercise> 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;
    }
}
由于此SQL:

Hibernate: insert into TrainingplanExercises (TrainingplanID, trainingplan, exercise, parameter) values (?, ?, ?, ?)
我该如何解决这个问题? 如果我将joinColumn更改为“trainingplan”,我会得到一个错误,即有两个相同的列。如果从反向列中删除“trainingplan”,则会出现一个错误,即缺少一个,因为外部约束需要3列

编辑: 从评论中尝试一些东西。我确实试过一次了

@Id
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "trainingplan", nullable = false)
public Training getTraining() {
    return training.get();
}

@OneToMany(fetch = FetchType.EAGER, mappedBy = "training")
public List<TrainingplanExercise> getExercises() {
    return exercises;
}
我得到一个例外:

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

您是否尝试使用多对一映射,因为它是外键的映射。然后,您可以尝试以下方法:

@Id
@ManyToOne( cascade = {CascadeType.PERSIST}, targetEntity=Trainingplan.class )
@JoinColumn(name = "trainingplan")
public Training getTraining() {}

好的,看。你遇到的是一个设计问题,而不是一般问题。首先,据我所知,您需要制作一套独特的
培训计划练习。为此,您有一个
实体

@Entity
public class TrainingplanExercise implements Serializable {
    @EmbeddedId private TrainingplanExerciseId trainingplanExerciseId;

    public TrainingplanExercise() {}        
    public TrainingplanExercise(TrainingplanExerciseId trainingplanExerciseId) {
        this.trainingplanExerciseId = trainingplanExerciseId;
    }
    ... other fields ...   
}
@Entity
public class TrainingPlan implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    @ManyToMany(fetch=FetchType.EAGER)
    private List<TrainingplanExercise> trainingplanExercises = new ArrayList<TrainingplanExercise>();

    ... getters, setters, 
}
上述
实体
与您原始的
实体
之间的区别在于,我已将
ID
设置为
embeddedableid
。为了确保只有唯一的练习被放入
培训计划练习
,您有一个定义为单独类的
组合键

@Embeddable
public class TrainingplanExerciseId implements Serializable {
    private String exercise;
    private String parameter;

    public TrainingplanExerciseId() {}
    public TrainingplanExerciseId(String exercise, String parameter) {
        this.exercise = exercise;
        this.parameter = parameter;
    }

    ... getters, setters, hashCode, and equals
}
在这里,我将类
设为可嵌入的
,这样它就可以用作
ID
。您试图声明一个
compositeKey
的方式没有任何意义;您试图将
培训计划练习
实体
中的每个字段声明为
ID
,但您只能有一个
ID

模型
的不同之处在于
培训计划练习ID
组合键
不包括对
培训计划
的引用。如果您试图获取使用任何特定
培训计划练习的
培训计划
,则您需要一个,但这是另一个问题。否则,我不知道您为什么要从
培训计划练习
返回到
培训计划
。此外,您将对
培训计划
的引用放入
培训计划执行Id
组合键
,这将要求对
培训计划
进行序列化,而这实际上不能作为唯一Id工作

现在,您可以将单个练习放入表格中:

public TrainingplanExercise createExercise(String exercise, String parameter) {
    TrainingplanExercise trainingplanExercise = new TrainingplanExercise(new TrainingplanExerciseId(exercise, parameter));
    em.persist( trainingplanExercise );
    return trainingplanExercise;
}
在此之后,您希望有任意数量的
培训计划
,它们使用可能的
培训计划练习
,您可以使用此
实体

@Entity
public class TrainingplanExercise implements Serializable {
    @EmbeddedId private TrainingplanExerciseId trainingplanExerciseId;

    public TrainingplanExercise() {}        
    public TrainingplanExercise(TrainingplanExerciseId trainingplanExerciseId) {
        this.trainingplanExerciseId = trainingplanExerciseId;
    }
    ... other fields ...   
}
@Entity
public class TrainingPlan implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    @ManyToMany(fetch=FetchType.EAGER)
    private List<TrainingplanExercise> trainingplanExercises = new ArrayList<TrainingplanExercise>();

    ... getters, setters, 
}
如果您将其声明为一个
OneToMany
关系,那么
JPA
提供者将在
链接表上添加一个额外的
约束
,以确保
培训计划练习
不能链接到多个
培训计划
,因此您不需要这样做。举个例子,这就是约束的样子

alter table TrainingPlan_TrainingplanExercise 
    add constraint UK_t0ku26ydvjkrme5ycrnlechgi  unique (trainingplanExercises_exercise, trainingplanExercises_parameter);
创建和更新培训计划非常简单:

public TrainingPlan createTrainingPlan() {
    TrainingPlan trainingPlan = new TrainingPlan();
    em.persist(trainingPlan);
    return trainingPlan;
}
public TrainingPlan updateTrainingPlan(TrainingPlan trainingPlan) {
    return em.merge(trainingPlan);
}
现在,您可以创建
培训计划练习
培训计划
,并将练习添加到培训计划中并进行更新

TrainingplanExercise squats20 = trainingService.createExercise("Squats", "20");
TrainingplanExercise lifts10 = trainingService.createExercise("Lifts", "10");
TrainingplanExercise crunches50 = trainingService.createExercise("Crunches", "50");

TrainingPlan trainingPlan = trainingService.createTrainingPlan();
trainingPlan.getTrainingplanExercises().add( squats20 );
trainingPlan.getTrainingplanExercises().add( lifts10 );
trainingService.updateTrainingPlan(trainingPlan);

trainingPlan = trainingService.createTrainingPlan();
trainingPlan.getTrainingplanExercises().add( lifts10 );
trainingPlan.getTrainingplanExercises().add( crunches50 );
trainingService.updateTrainingPlan(trainingPlan);
还请注意,您的应用程序面临的挑战是确保用户只创建唯一的
培训计划练习。如果试图创建具有重复的
练习
参数
培训计划练习
,您将获得
唯一索引或主键冲突
异常,事务将回滚

编辑:对于阅读
培训计划
,可以使用以下内容:

public List<TrainingPlan> listTrainingPlans() {
    CriteriaQuery<TrainingPlan> criteria = em.getCriteriaBuilder().createQuery(TrainingPlan.class);
    criteria.select(criteria.from(TrainingPlan.class));
    List<TrainingPlan> trainingPlans = em.createQuery(criteria).getResultList();
    return trainingPlans;
}
公共列表培训计划(){
CriteriaQuery criteria=em.getCriteriaBuilder().createQuery(TrainingPlan.class);
选择(criteria.from(TrainingPlan.class));
List trainingPlans=em.createQuery(criteria).getResultList();
返回培训计划;
}

请注意,由于
列表trainingplanExercises
设置为
FetchType.EAGER
,因此此特定查询将拉入整个数据库
FetchType.EAGER
对于阅读单个
培训计划
可能不是问题,但是如果您只想要一个
培训计划的列表,而没有获得所有详细信息,然后,您需要确定如何实现
FetchType.LAZY

当您将“trainingPlan”声明为joinColumn并将其从inverseJoinColumn中删除时,是否介意发布@ManyToMany声明代码和相应的错误消息,如您在上一段中所述?我猜问题就在这里,为什么是多对多?从数据模型中,一个培训计划练习只能引用一个培训计划。它应该是OneToMany/ManyTone,不是吗?我现在确实尝试使用OneToMany/ManyTone,因为我也认为这是正确的方法。还有问题,请看上面多谢!我能插入一些东西似乎很有效。虽然如果我想从数据库中读取,我会得到一个错误
错误:表'bla.TrainingplanExercise'不存在
。我做了如上所述的一切,并在阅读中添加了编辑。如果该表不存在,那么我不知道如何将其插入,因此您的错误肯定比我容易看到的更多。请注意,我在表注释中没有使用name=,因此您需要确保使用的表名正确,可能是
TrainingplanExercises
。如果答案有帮助,请接受。谢谢。我已经创建了表
TrainingPlan\u TrainingplanExercise
这是非常有用的,并且保存了值。当我想阅读它时,hibernate想从
TrainingplanExercise
中进行选择。我已经添加了name=annotation并将其设置为
@Table(name=“TrainingplanExercise”)
。然而,现在它想从
Trainingplan\u TrainingplanExercise
中读取对我来说没有意义的内容
TrainingplanExercise squats20 = trainingService.createExercise("Squats", "20");
TrainingplanExercise lifts10 = trainingService.createExercise("Lifts", "10");
TrainingplanExercise crunches50 = trainingService.createExercise("Crunches", "50");

TrainingPlan trainingPlan = trainingService.createTrainingPlan();
trainingPlan.getTrainingplanExercises().add( squats20 );
trainingPlan.getTrainingplanExercises().add( lifts10 );
trainingService.updateTrainingPlan(trainingPlan);

trainingPlan = trainingService.createTrainingPlan();
trainingPlan.getTrainingplanExercises().add( lifts10 );
trainingPlan.getTrainingplanExercises().add( crunches50 );
trainingService.updateTrainingPlan(trainingPlan);
public List<TrainingPlan> listTrainingPlans() {
    CriteriaQuery<TrainingPlan> criteria = em.getCriteriaBuilder().createQuery(TrainingPlan.class);
    criteria.select(criteria.from(TrainingPlan.class));
    List<TrainingPlan> trainingPlans = em.createQuery(criteria).getResultList();
    return trainingPlans;
}