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;
}