首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Hibernate-JPA更新父-子关系时出错

使用Hibernate-JPA更新父-子关系时出错
EN

Stack Overflow用户
提问于 2011-07-28 10:30:41
回答 3查看 2.5K关注 0票数 3

我的Hibernate-JPA域模型具有以下实体:

代码语言:javascript
复制
AttributeType ------< AttributeValue

相关的Java类如下所示(getter和setter省略了):

代码语言:javascript
复制
@Entity
public class AttributeType {

  @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @Column(unique = true, nullable = false)
  private String name;

  @OneToMany(mappedBy = "attributeType", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
  private List<AttributeValue> values = new ArrayList<AttributeValue>();    
}

@Entity @Table(uniqueConstraints = @UniqueConstraint(columnNames = {"value", "attribute_type_id"}))
public class AttributeValue {
  @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @ManyToOne(optional = false)
  private AttributeType attributeType;

  @Column(nullable = false)
  private String value;
}

注意,AttributeValue.valueAttributeValue.attributeType有一个唯一的约束,因为对于属性类型(例如大小),我们不希望不止一次地出现属性值(例如小)。

如果我通过在单个事务中执行以下操作来更新AttributeType

  • 从“大小”属性类型中删除“小”属性值
  • 在“大小”属性类型中添加“小”属性值

我得到一个异常,表示违反了唯一约束。这表明Hibernate-JPA在删除之前执行属性值的插入,这似乎没有明显的原因导致了这种问题。

执行AttributeType更新的类如下所示:

代码语言:javascript
复制
@Transactional(propagation = Propagation.SUPPORTS)
public class SomeService {

  private EntityManager entityManager; // set by dependency injection

  @Transactional(propagation = Propagation.REQUIRED)
  public AttributeType updateAttributeType(AttributeType attributeType) throws Exception {

    attributeType = entityManager.merge(attributeType);
    entityManager.flush();
    entityManager.refresh(attributeType);
    return attributeType;
  }
}

我可以通过迭代属性值来解决这个问题,找出哪些属性值已被更新/删除/插入,然后按以下顺序执行:

  1. 删除
  2. 更新
  3. 镶片

但似乎ORM应该能为我做到这一点。我已经读过,Oracle提供了一个"deferConstraints“选项,该选项只在事务完成后才会检查约束。但是,我使用的是Server,因此这不会对我有帮助。

EN

回答 3

Stack Overflow用户

发布于 2011-08-09 18:29:28

您需要使用复合ID而不是生成的ID。

HHH-2801 当向集合中添加具有生成ID的新关联实体时,就会出现问题。当合并包含此集合的实体时,第一步是级联保存新的关联实体。必须在对集合进行其他更改之前进行级联。因为此新关联实体的唯一键与已持久化的实体相同,因此抛出一个ConstraintViolationException。,这是预期的行为。 使用新集合(如上一条注释中建议的一次删除)也会导致违反约束,因为新的关联实体将保存在新集合的级联上。 其中一种方法(使用复合ID而不是生成的ID)的示例演示了>在manytomanywithassocclass.tar.gz中,并被签入Svn

代码语言:javascript
复制
@Entity  
public class AttributeType {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Integer id;
    @Column(unique = true, nullable = false)
    private String name;
    @OneToMany(mappedBy = "attributeType", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
    private List<AttributeValue> values = new ArrayList<AttributeValue>();

    //Getter, Setter...

}

@Entity
@Table (uniqueConstraints = @UniqueConstraint(columnNames = { "value", "attributeType_id" }))
public class AttributeValue{

    @EmbeddedId AttributeValueId id;    

    @MapsId(value= "id")    
    @ManyToOne(optional = false)    
    private AttributeType attributeType;

    private String value2;

    public AttributeValue() {
         this.id = new AttributeValueId(); 
    }

    public AttributeType getAttributeType() {
        return attributeType;
    }
    public void setAttributeType(AttributeType pAttributeType) {
        this.id.setAttributeTypeID(pAttributeType.getId());
        this.attributeType = pAttributeType;
    }
    public String getValue() {
        return id.getAttributeValue();
    }
    public void setValue(String value) {
        this.id.setAttributeValue(value);
    }

    @Embeddable
    public static class AttributeValueId implements Serializable {

        private Integer id;
        private String value;

        public AttributeValueId() {
        }

        public AttributeValueId(Integer pAttributeTypeID, String pAttributeValue) {
            this.id = pAttributeTypeID;
            this.value = pAttributeValue;
        }

        public Integer getAttributeTypeID() {
            return id;
        }

        public void setAttributeTypeID(Integer attributeTypeID) {
            this.id = attributeTypeID;
        }

        public String getAttributeValue() {
            return value;
        }

        public void setAttributeValue(String attributeValue) {
            this.value = attributeValue;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime
                    * result
                    + ((id == null) ? 0 : id
                            .hashCode());
            result = prime
                    * result
                    + ((value == null) ? 0 : value.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            AttributeValueId other = (AttributeValueId) obj;
            if (id == null) {
                if (other.id != null)
                    return false;
            } else if (!id.equals(other.id))
                return false;
            if (value == null) {
                if (other.value != null)
                    return false;
            } else if (!value.equals(other.value))
                return false;
            return true;
        }
    }
}

有关如何使用JPA注释执行此操作,请参见5.1.2.1.复合标识符

请参阅第8章.组件映射

请参阅8.4。组件作为复合标识符

票数 4
EN

Stack Overflow用户

发布于 2011-08-10 21:28:00

我不确定随着时间的推移,我是否理解了这个问题,但我首先要尝试的是覆盖AttributeValue的等于方法,以包含这两个唯一的字段。

票数 2
EN

Stack Overflow用户

发布于 2011-08-09 15:06:54

在hibernate会话中,有一个队列用于删除,一个用于插入。调试以查看是否在插入之前删除。

看看合并。尝试使用update。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6857517

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档