Hibernate - 使用生成的复合键插入临时子实体

Hibernate - Insert transient child entities with generated composite key

我在使用 Hibernate 插入一堆瞬态子实体时遇到问题,这些实体可能通过保存分离的父实体而具有由其他瞬态子实体组成的复合键。我很确定我的复合键 class 设置正确,但每次我尝试保存具有瞬态实体(尚未生成 ID)的父实体时,我都会收到此错误:

org.hibernate.id.IdentifierGenerationException: null id generated for:class org._.website.data.entity.Mean

所以 Hibernate 从不生成复合键,我认为它应该能够给出被引用的属性。但是,由于复合键引用的属性也是暂时的,因此没有 ID 可用于手动设置复合键。所以我希望 Hibernate 足够聪明,可以自己生成。

有没有办法让 Hibernate 处理 saving/inserting 具有引用其他瞬态子实体的复合键的瞬态子实体?

这是我正在使用的代码。如果 projectDao.save(project);

失败
Variable variable = new Variable();
variable.setProject(project);
variable.setName("x");

Belief belief = new Belief();
belief.setProject(project);
belief.setName("model-1");

Mean mean = new Mean();
mean.setVariable(variable);
mean.setBelief(belief);

// I can't do this because variable and belief are transient and have no ID yet
//MeanPK meanPk = new MeanPK(variableId, beliefId);
//mean.setPk(meanPk);

belief.getMeans().add(mean);

project.getVariables().add(variable);
project.getBeliefs().add(belief);

projectDao.save(project);

如果有帮助,这里是可嵌入的 MeanPK class

@Embeddable
public static class MeanPK implements Serializable {

    private static final long serialVersionUID = 341373316515655834L;

    @GeneratedValue
    @Column(name = "belief_id", nullable = false, updatable = false)
    protected Integer beliefId;

    @GeneratedValue
    @Column(name = "variable_id", nullable = false, updatable = false)
    protected Integer variableId;

    // getters/setters excluded for brevity

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof MeanPK)) {
            return false;
        }
        MeanPK other = (MeanPK) obj;
        return beliefId.equals(other.beliefId) && variableId.equals(other.variableId);
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(beliefId).append(variableId).toHashCode();
    }

}

如果绝对必要,我可以先保存复合键引用的瞬态实体以获取 ID 并手动构建 MeanPK 复合键,但我希望 Hibernate 能够自行处理一次调用 projectDao.save(...);

感谢您的帮助!

考虑在您的实体上使用级联注释 classes...保存操作将尝试在 Mean 具有 ID 之前保存项目。尝试做 projectDao.save(mean);首先或在项目 class...

的 Mean 集合上使用级联注释

像这样...

  Mean mean = new Mean();
    mean.setVariable(variable);
    mean.setBelief(belief);
    **projectDao.save(mean);** //first option

    // I can't do this because variable and belief are transient and have no ID yet
    //MeanPK meanPk = new MeanPK(variableId, beliefId);
    //mean.setPk(meanPk);

    belief.getMeans().add(mean);

    project.getVariables().add(variable);
    project.getBeliefs().add(belief);

    projectDao.save(project);

//second option

//or in your method declaration section in your Project class remove 

getVariables().add(variable) &
getBeliefs().add(belief)

//as well as their associated variable declarations and add
// mappedBy foreign key constraint meanId
 @OneToMany(cascade = CascadeType.ALL, mappedBy = "meanId") 
//add to variable declarations section
    private Collection<Means> meansCollection;
//and add the following method under getter/setter section

    @XmlTransient
    public Collection<Mean> getMeansCollection() {
        return meansCollection;
    }

//In the Project class constructor do the following initialization of the MeanCollection

meansCollection = new ArrayList();

//now your call simply becomes 

Mean mean = new Mean();
mean.setVariable(variable);
mean.setBelief(belief);

Project project = new Project();
project.getMeansCollection().add(means);
projectDao.save(project);

// Also it looks like you should be using @JoinColumns for the variable_id &              
// belief_id fields where each variable is actually a class variable       
// representation and not an Integer. In this case you will have mean_id as   
// the single primary key and class Variable & Belief each as a @JoinColumn  
// foreign key constraint

//4 spaces

我找到了我的问题的答案,我想我会 post 以防有人发现它有用。

我所做的是将引用的 Variable 和 Belief 实体存储在 MeanPK class 本身中,当它们被设置为 Mean 实体时。我向 MeanPk class 中的 ID getter 添加了一些逻辑,以便当它们被 hibernate 调用时,它将首先检查以设置存储在 MeanPK class 中的对象的 id。这是有效的,因为 hibernate 将在到达 Mean 实体之前插入并保留瞬态 Variable 和 Belief 实体,因为它是最底层的子实体。我的所有集合都有 CascadeType.ALL,所以我不需要担心手动保存每个实体,Hibernate 会将保存操作从父级级联到子级。

这是更新后的 MeanPK class 和 Mean 实体 class:

@Entity
@Table(name = "mean")
public class Mean implements Serializable {

    private static final long serialVersionUID = -5732898358425089380L;


    // composite key
    @EmbeddedId
    private MeanPK pk = new MeanPK();

    @ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH })
    @JoinColumn(name = "belief_id", insertable = false, nullable = false, updatable = false)
    private Belief belief;

    @ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH })
    @JoinColumn(name = "variable_id", insertable = false, nullable = false, updatable = false)
    private Variable variable;

    // more attributes excluded

    public MeanPK getPk() {
        return pk;
    }

    protected void setPk(MeanPK pk) {
        this.pk = pk;
    }


    public Belief getBelief() {
        return belief;
    }

    public void setBelief(Belief belief) {
        pk.setBelief(this.belief = belief);
    }


    @XmlTransient
    public Variable getVariable() {
        return variable;
    }

    public void setVariable(Variable variable) {
        pk.setVariable(this.variable = variable);
    }


    @Embeddable
    public static class MeanPK implements Serializable {

        private static final long serialVersionUID = 341373316515655834L;

        @Access(AccessType.PROPERTY)
        @Column(name = "belief_id", nullable = false, updatable = false)
        protected Integer beliefId;

        @Access(AccessType.PROPERTY)
        @Column(name = "variable_id", nullable = false, updatable = false)
        protected Integer variableId;

        @Transient
        private Belief belief;

        @Transient
        private Variable variable;


        public Integer getBeliefId() {
            if (beliefId == null && belief != null) {
                beliefId = belief.getId();
            }
            return beliefId;
        }

        protected void setBeliefId(Integer beliefId) {
            this.beliefId = beliefId;
        }

        public Belief getBelief() {
            return belief;
        }

        void setBelief(Belief belief) {
            this.belief = belief;
            if (belief != null) {
                beliefId = belief.getId();
            }
        }


        public Integer getVariableId() {
            if (variableId == null && variable != null) {
                variableId = variable.getId();
            }
            return variableId;
        }

        protected void setVariableId(Integer variableId) {
            this.variableId = variableId;
        }

        public Variable getVariable() {
            return variable;
        }

        void setVariable(Variable variable) {
            this.variable = variable;
            if (variable != null) {
                variableId = variable.getId();
            }
        }


        @Override
        public boolean equals(Object obj) {
            if (obj == this) {
                return true;
            }
            if (!(obj instanceof MeanPK)) {
                return false;
            }
            MeanPK other = (MeanPK) obj;
            return getBeliefId().equals(other.getBeliefId()) && getVariableId().equals(other.getVariableId());
        }

        @Override
        public int hashCode() {
            return new HashCodeBuilder().append(getBeliefId()).append(getVariableId()).toHashCode();
        }

    }

}

是的它的@Transient

你是这样打电话的吗?

MeanPK meanPk = new MeanPK(variableId, beliefId);

拨打此电话时是否对您有用

Mean mean = new Mean(variableId, beliefId);