Hibernate - 使用生成的复合键插入瞬态子实体

时间:2015-02-18 02:24:06

标签: java hibernate jpa

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

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

所以Hibernate永远不会生成Composite键,我认为它应该能够给出被引用的属性。但是,由于组合键引用的属性也是暂时的,因此没有ID可以手动设置组合键。所以我希望Hibernate足够聪明来完成这一代。

有没有办法让Hibernate使用引用其他瞬态子实体的复合键来处理瞬态子实体的保存/插入?

这是我正在使用的代码。如果在projectDao.save(项目)上失败;

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);

如果有帮助,这里是Embeddable MeanPK类

@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(...);

感谢您的帮助!

3 个答案:

答案 0 :(得分:1)

我想出了我的问题的答案,我想如果有人发现它有用,我会发布它。

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

这是更新的MeanPK类和Mean实体类:

@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();
        }

    }

}

答案 1 :(得分:0)

研究在实体类上使用级联注释...保存操作将尝试在Mean具有ID之前保存项目。尝试做projectDao.save(mean);首先或在Project类的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

答案 2 :(得分:0)

是的@Transient

所以你打电话是这样的吗?

MeanPK meanPk = new MeanPK(variableId,beliefId);

在拨打电话时这对你有用吗<​​/ p>

平均值=新均值(variableId,beliefId);