具有双向@OneToOne关系的@PrimaryKeyJoinColumn

时间:2010-01-04 17:47:21

标签: hibernate orm

我有两个对象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

        ...
}

2 个答案:

答案 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 作为MatMatImage在内存中共享相同对象(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;
    }

}