我有两个对象Mat和MatImage,父和子resp。 MatImage的主键是Mat的id,它们通过一对一的关系加入。
如果我正确理解双向关系,那么如果我执行像matImage.setMat(mat)这样的操作,子对象就会知道父对象。我认为主键在这一点上会被填充,但事实并非如此。我知道这是因为sql在尝试使用#0作为matId插入MatImage时抛出异常。
另一个问题是n + 1问题。我想懒洋洋地加载子对象,因为并非所有的垫子都有matimage。我可以尝试将@OneToOne更改为@ManyToOne,但不确定如何双向完成。任何帮助,将不胜感激。谢谢。
以下是实体:
// Mat
@Entity
@Table(name="mat")
public class Mat implements Serializable {
@Id
@GeneratedValue(generator="SeqMat")
@SequenceGenerator(name="SeqMat", sequenceName="seq_mat_id")
int id
@OneToOne(mappedBy="mat", optional=true, fetch=FetchType.LAZY)
@PrimaryKeyJoinColumn(name="id", referencedColumnName="matId")
@Cascade([ALL, DELETE_ORPHAN])
MatImage matImage
int matTemplateId
int number
...
}
// MatImage
@Entity
@Table(name="matimage")
public class MatImage implements Serializable {
@Id
int matId
@OneToOne(optional=true, fetch=FetchType.LAZY)
@JoinColumn(name="matId", referencedColumnName="id")
Mat mat
@Column(name="img_eventid")
int eventId
...
}
答案 0 :(得分:4)
我不确定你真的想要什么。但您可以使用类似下面的内容来设置双向关系。
@Entity
public class Mat implements Serializable {
private MutableInt id = new Mutable(-1);
private MatImage matImage;
@Id
@GeneratedValue(strategy=GenerationType.AUTO, generator="SeqMat")
@SequenceGenerator(name="SeqMat", sequenceName="seq_mat_id")
public Integer getId() {
return this.id.intValue;
}
public void setId(Integer id) {
this.id.setValue(id)
}
public void setIdAsMutableInt(MutableInt id) {
this.id = id;
}
@OneToOne(fetch=FetchType.LAZY)
@PrimaryKeyJoinColumn
@Cascade({CascadeType.ALL, CascadeType.DELETE_ORPHAN})
public MatImage getMatImage() {
return this.matImage;
}
public void setMatImage(MatImage matImage) {
this.matImage = matImage;
this.matImage.setIdAsMutableInt(this.id);
}
}
现在我们的MatImage
@Entity
public class MatImage implements Serializable {
private MutableInt id = new MutableInt(-1);
private Mat mat;
@Id
public Integer getId() {
return this.id.intValue();
}
public void setId(Integer id) {
this.id.setValue(id);
}
public void setIdAsMutableInt(MutableInt id) {
this.id = id;
}
@OneToOne(fetch=FetchType.LAZY)
@PrimaryKeyJoinColumn
public Mat getMat() {
return mat;
}
public void setMat(Mat mat) {
this.mat = mat;
this.mat.setIdAsMutableInt(this.id);
}
}
一些建议
JPA规范不包含用于处理共享主键生成问题的标准化方法
它解释了为什么我使用org.apache.commons.lang.mutable.MutableInt
作为Mat
和MatImage
在内存中共享相同对象(id)的方式。
现在您可以使用以下内容:
Mat mat = new Mat();
MatImage matImage = new MatImage();
/**
* Set up both sides
*
* Or use some kind of add convenience method To take car of it
*/
mat.setImage(matImage);
matImage.setMat(mat);
session.saveOrUpdate(mat);
两者将共享相同的生成ID。
建议:将注释配置放在getter方法而不是成员字段中。 Hibernate使用代理对象。但是在getter方法中使用注释时代理工作正常,而不是在成员字段中使用时。
的问候,
答案 1 :(得分:3)
问题在于,默认情况下,JPA不允许对双向,共享密钥,一对一关系进行适当的注释。 Hibernate有一个完美的构造,但请注意正确的顺序,以及注释的特定参数(您也可以阅读hibernate引用,但除非您先预设依赖对象,否则它们的示例将无效使用下面的方法可以避免 - 它基本上可以一次性自动保存两个对象)。 在此示例中,每个作业都在处理消息。 Job是“父”,消息是依赖对象。消息的主键是作业ID(用于消除消息表中的一个额外列。如果需要在作业范围之外使用消息,则可以在消息表中创建一个id,使用与下面相同的设置) 。创建作业并附加消息时,hibernate会在调用jobDao.save时自动保存这两个对象。
@Entity
@Table
public class Job implements Serializable{
private Long id;
@Id
@GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@OneToOne(cascade = CascadeType.ALL, mappedBy = "job")
public Message getMessage() {
return message;
}
public void setMessage(Message message) {
this.message = message;
message.setJob(this);
}
}
现在依赖的消息:
@Entity
@Table
public abstract class Message implements Serializable {
private Job job;
@Id
@GeneratedValue(generator = "foreign")
@GenericGenerator(
name = "foreign",
strategy = "foreign",
parameters = {@org.hibernate.annotations.Parameter(name = "property", value = "job")})
@OneToOne(optional = false, cascade = CascadeType.ALL)
public Job getJob() {
return job;
}
public void setJob(Job job) {
this.job = job;
}
}