*** 映射(休眠)

Posted

技术标签:

【中文标题】*** 映射(休眠)【英文标题】:A *** mapping (Hibernate) 【发布时间】:2010-11-27 08:39:21 【问题描述】:

假设一个像这样的映射

@Entity
public class User 

    private Integer id

    private List<Info> infoList;    

    @Id
    public getId() 
        return this.id;
    

    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumn(name="USER_ID", insertable=false, updateable=false, nullable=false)
    public getInfoList() 
        return this.infoList;
    

    public void addQuestion(Info question) 
        info.setInfoCategory(InfoCategory.QUESTION);
        info.setInfoId(new InfoId(getId(), getInfoList().size()));

        getInfoList().add(question);
    

    public void addAnswer(InfoRepository repository, Integer questionIndex, Info answer) 
        Info question = repository.getInfoById(new InfoId(getId(), questionIndex));

        if(question.getInfoCategory().equals(InfoCategory.ANSWER))
            throw new RuntimeException("Is not a question");

        if(question.getAnswer() != null)
            throw new RuntimeException("You can not post a new answer");

        answer.setInfoCategory(InfoCategory.ANSWER);
        answer.setInfoId(new InfoId(getId(), getInfoList().size()));

        getInfoList().add(answer);

        question.setAnswer(answer);
    


以及由 Info 类映射的问答

@Entity
public class Info implements Serializable 

    private InfoId infoId;

    private Info answer;

    private InfoCategory infoCategory;

    public Info() 

    @Embeddable
    public static class InfoId 

        private Integer userId;
        private Integer index;

        public InfoId(Integer userId, Integer index) 
            this.userId = userId;
            this.index = index;
        

        @Column("USER_ID", updateable=false, nullable=false)
        public getUserId() 
            return this.userId;
         

        @Column("INFO_INDEX", updateable=false, nullable=false)
        public getIndex() 
            return this.index;
        

        // equals and hashcode

    

    // mapped as a ManyToOne instead of @OneToOne
    @ManyToOne
    JoinColumns(
        JoinColumn(name="USER_ID", referencedColumnName="USER_ID", insertable=false, updateable=false),
        JoinColumn(name="ANSWER_INDEX", referencedColumnName="INFO_INDEX", insertable=false)
    )
    public Info getAnswer() 
        return this.answer;  
    

    @EmbeddedId
    public InfoId getInfoId() 
        return this.infoId;
    


在 getAnswer 中,我使用 ManyToOne 而不是 OneToOne,因为一些问题与 OneToOne 映射有关。 OneToOne 可以映射为 ManyToOne(@JoinColumn 中的 unique=true)。 INFO_INDEX 与任何特定目的无关。只需一个键即可支持 LEGACY 系统中的复合主键。

在回答之前,请注意以下事项:

如果一个对象有一个分配的标识符,或者一个复合键,在调用 save() 之前,标识符应该分配给对象实例

所以我必须在 getAnswer 中映射 JoinColumn(name="USER_ID", referencedColumnName="USER_ID", insertable=false, updateable=false) 因为 Hibernate 不允许两个可变属性共享相同collumn (userId 也使用 USER_ID) 否则我将在 answer 属性中获取 USER_ID 必须使用 insertable=false, updateable=false 进行映射

现在看看 getAnswer 映射

@ManyToOne
JoinColumns(
    JoinColumn(name="USER_ID", referencedColumnName="USER_ID", insertable=false, updateable=false),
    JoinColumn(name="ANSWER_INDEX", referencedColumnName="INFO_INDEX", insertable=false)
)

因为它,Hibernate 抱怨你不能混合不同的可插入和可更新

我应该怎么做才能通过它?

请注意它是一个遗留系统。

问候,

【问题讨论】:

【参考方案1】:

您将大大通过放弃嵌入的 id 并改用代理 PK 来简化映射。

如果您需要将InfoId.index 用于某些目的(订购问题/答案?您可以在list 映射中指定),请将其保留为常规属性。

UserId 将在User 上替换为ManyToOne;另一个关联端(在User 类中)将映射为@OneToMany(mappedBy="User")

为什么答案映射为ManyToOne?不应该是OneToMany(例如,1 个问题对多个答案)?无论哪种方式,将类映射到自身都不太理想-您基本上是在实现效率低下的“邻接列表”模型层次结构;另外,在您的情况下,您需要确保它不超过 2 个级别。我会将QuestionAnswer 映射为单独的类;您可以让它们实现一个通用接口或扩展相同的基类(可能是抽象类);无论哪种方式,您都可以享受 Hibernate 提供的隐式多态性。

【讨论】:

好Chss,其实就是ManyToOne。我使用它而不是 OneToOne 因为一些与 OneToOne 映射相关的问题。 OneToOne 可以映射为 ManyToOne(@JoinColumn 中的 unique=true)。 INFO_INDEX 与任何特定目的无关。只需一个键即可支持 LEGACY 系统中的复合主键。我明天试试。问候,【参考方案2】:

根据getAnswer映射中的问题

@ManyToOne
JoinColumns(
    JoinColumn(name="USER_ID", referencedColumnName="USER_ID", insertable=false, updateable=false),
    JoinColumn(name="ANSWER_INDEX", referencedColumnName="INFO_INDEX", insertable=false)
)

Hibernate 会抱怨,因为它不允许混合不同的可插入和可更新。注意在 USER_ID JoinColumn 中可插入和可更新,并且只能在 ANSWER_INDEX JoinColumn 中插入。

所以为了通过它,我根据它设置了ANSWER_INDEX JoinCollumn

JoinColumn(name="ANSWER_INDEX", referencedColumnName="INFO_INDEX", insertable=false, updateable=false)

这样,Hibernate 就不会抱怨了。

我设置了一个名为 answerIndex 的新属性

private Integer answerIndex;   

@Column(name="ANSWER_INDEX", insertable=false)
public void getAnswerIndex() 
    return this.answerIndex;

然后在用户添加答案

public void addAnswer(InfoRepository repository, Integer questionIndex, Info answer) 
    Info question = repository.getInfoById(new InfoId(getId(), questionIndex));

    if(question.getInfoCategory().equals(InfoCategory.ANSWER))
        throw new RuntimeException("Is not a question");

    if(question.getAnswer() != null)
        throw new RuntimeException("You can not post a new answer");

    answer.setInfoCategory(InfoCategory.ANSWER);
    answer.setInfoId(new InfoId(getId(), getInfoList().size()));

    getInfoList().add(answer);

    // Added in order to set up AnswerIndex property
    question.setAnswerIndex(answer.getInfoId().getIndex());

【讨论】:

以上是关于*** 映射(休眠)的主要内容,如果未能解决你的问题,请参考以下文章

如何使用类属性映射休眠中的列?

*** 映射(休眠)

休眠枚举映射

使用休眠映射布尔值

JPA(休眠)映射OneToMany不正确?

从应用程序代码获取休眠映射