Java 将主键映射为外键的@OneToMany关系
我试图映射JPA中下图中描述的一对多表关系: 可以看出,Java 将主键映射为外键的@OneToMany关系,java,hibernate,jpa,Java,Hibernate,Jpa,我试图映射JPA中下图中描述的一对多表关系: 可以看出,“activity\u属性”表使用了一个复合主键,(id,name),列“id”是表“activity”中列“id”的外键 我当前的实体是这样映射的(为了清晰起见,已经使用了一些辅助方法): Activity.java @实体 @吸气剂 @塞特 公共课堂活动{ @身份证 @GeneratedValue(策略=GenerationType.SEQUENCE,generator=“act\u id\u generator”) @Sequenc
“activity\u属性”
表使用了一个复合主键,(id,name)
,列“id”
是表“activity”
中列“id”
的外键
我当前的实体是这样映射的(为了清晰起见,已经使用了一些辅助方法):
Activity.java
@实体
@吸气剂
@塞特
公共课堂活动{
@身份证
@GeneratedValue(策略=GenerationType.SEQUENCE,generator=“act\u id\u generator”)
@SequenceGenerator(name=“act\u id\u generator”,sequenceName=“activity\u id\u seq”,allocationSize=1,initialValue=1)
私有整数id;
私有字符串名称;
@OneToMany(mappedBy=“id.activityId”,cascade=CascadeType.ALL,orphanRemoving=true)
私有财产清单;
}
ActivityProperty.java
@实体
@吸气剂
@塞特
@表(name=“活动\财产”)
公共类活动属性{
@嵌入ID
私有活动属性id;
私有字符串值;
@枚举(EnumType.STRING)
私有财产类型;
}
ActivityPropertyId.java
@可嵌入
@吸气剂
@塞特
@托斯特林
公共类ActivityPropertyId实现可序列化{
@列(name=“id”)
私有整数活动ID;
私有字符串名称;
@凌驾
公共布尔等于(对象o){
如果(this==o)返回true;
如果(o==null | | getClass()!=o.getClass())返回false;
ActivityPropertyId,该值=(ActivityPropertyId)o;
返回activityId.equals(即.activityId)&&
name.equals(that.name);
}
@凌驾
公共int hashCode(){
返回Objects.hash(activityId,name);
}
}
当我尝试以这种方式保持活动时:
Activity-Activity=createActivity(“Activity_1”);
activity.addProperty(ActivityProperty.from(“Prop1”,“value1”,PropertyType.PARAMETER));
坚持(活动);
我可以在Hibernate中看到以下跟踪:
Hibernate: call next value for activity_id_seq
Hibernate: insert into activity (name, id) values (?, ?)
Hibernate: insert into activity_property (type, value, id, name) values (?, ?, ?, ?)
05-05-2019 12:26:29.623 [main] WARN o.h.e.jdbc.spi.SqlExceptionHelper.logExceptions - SQL Error: 23502, SQLState: 23502
05-05-2019 12:26:29.624 [main] ERROR o.h.e.jdbc.spi.SqlExceptionHelper.logExceptions - NULL not allowed for column "ID"; SQL statement:
insert into activity_property (type, value, id, name) values (?, ?, ?, ?) [23502-199]
活动
的自动生成Id似乎未在活动属性
的第二次插入中使用
我不知道如何正确映射这种关系。我的JPA批注中是否缺少任何内容?属性
mappedBy
用于指示此关系不用于持久化,而mappedBy
中的属性用于此目的
因此,要么创建一个从ActivityProperty
到Activity
的反向引用,并使用映射的反向引用,要么必须使用@JoinColumn
来声明外键
那看起来像。属性name
指向目标表中外键的名称
@JoinColumn(name = "id", nullable = false)
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private List<ActivityProperty> properties;
在这里阅读有关关系映射的更多信息:据我所知,JPA不支持通过
cascade
操作进行映射。你对JPA的要求太高了。我并没有一个具体的参考来说明这一点,但您可以通过下面的代码尽可能接近它。它不会运行,因为hibernate(至少)不知道如何自动将activity.id
映射到ActivityProperty
类的PK
@Entity
@Getter
@Setter
@Table(name = "activity_property")
@IdClass(ActivityPropertyId.class)
public class ActivityProperty {
@ManyToOne
@Id
@JoinColumn(name="id", referencedColumnName="id")
private Activity activity;
@Id
private String name;
private String value;
}
@Getter
@Setter
@ToString
public class ActivityPropertyId implements Serializable {
public ActivityPropertyId() {}
@Column(name = "id")
private Integer activity;
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ActivityPropertyId that = (ActivityPropertyId) o;
return activity.equals(that.activity) &&
name.equals(that.name);
}
@Override
public int hashCode() {
return Objects.hash(activity, name);
}
}
这将是一个例外
原因:java.lang.IllegalArgumentException:无法将java.lang.Integer字段model.ActivityPropertyId.activity设置为model.activity
我还没有找到解决这个问题的好办法。IMHO
cascade
是傻瓜的天堂,您应该自己保存ActivityProperty
实例,并且只对查询使用双向映射。这就是它的真正用途。如果您通过级联对OneToMany
列表执行一些简单的保存操作,并查看生成的SQL,您将看到可怕的事情。恐怕这与@JoinColumn
注释没有区别,插入属性时id仍然为空:(,我们真的需要使其具有双向性并保留对完整活动对象的引用吗?似乎有点开销。将nullable=false添加到JoinColumn中。我在添加nullable=false
时更新了我的答案,现在我发现无法构建Hibernate SessionFactory;嵌套异常为org.Hibernate.MappingException:Re实体映射中的重复列:com.gmv.epssg.oas.model.entity.db.ActivityProperty列:id(应使用insert=“false”update=“false”进行映射)
我忘记了必须将activityId设置为只读。我已将此添加到我的答案中。很抱歉,已尝试将这些属性添加到activityId中,但出现相同错误:(好的,非常感谢您的建议。我想我将排除级联并手动保留该关系。非常感谢!我按照您的建议删除级联插入并保留@OneToMany
注释仅用于查询目的。
@Entity
@Getter
@Setter
@Table(name = "activity_property")
@IdClass(ActivityPropertyId.class)
public class ActivityProperty {
@ManyToOne
@Id
@JoinColumn(name="id", referencedColumnName="id")
private Activity activity;
@Id
private String name;
private String value;
}
@Getter
@Setter
@ToString
public class ActivityPropertyId implements Serializable {
public ActivityPropertyId() {}
@Column(name = "id")
private Integer activity;
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ActivityPropertyId that = (ActivityPropertyId) o;
return activity.equals(that.activity) &&
name.equals(that.name);
}
@Override
public int hashCode() {
return Objects.hash(activity, name);
}
}