【问题标题】:error updating parent-child relationship using Hibernate-JPA使用 Hibernate-JPA 更新父子关系时出错
【发布时间】:2011-07-28 10:30:41
【问题描述】:

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

AttributeType ------< AttributeValue

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

@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

  • 从“size”属性类型中删除“small”属性值
  • 为“size”属性类型添加“small”属性值

我得到一个异常,表明违反了唯一约束。这表明 Hibernate-JPA 在删除之前执行了属性值的插入,这似乎无缘无故地引发了此类问题。

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

@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”选项,该选项仅在事务完成时才检查约束。但是,我使用的是 SQL Server,所以这对我没有帮助。

【问题讨论】:

  • 能否请您显示您的表格的 DDL?可能你没有违反你定义的约束,而是其他一些?

标签: java hibernate jpa persistence


【解决方案1】:

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

HHH-2801

当新的关联实体具有生成的 ID 时,就会出现问题 被添加到集合中。第一步,合并实体时 包含这个集合,是为了级联保存新的关联 实体。级联必须在对集合进行其他更改之前发生。 因为这个新关联实体的唯一键与 一个已经被持久化的实体,一个 ConstraintViolationException 是 抛出。 这是预期行为

使用新集合(即一次性删除),如 先前的评论)也会导致违反约束,因为 新的关联实体将保存在新关联实体的级联中 收藏。

其中一种方法的示例(使用复合 ID 而不是生成的 ID)在 manytomanywithassocclass.tar.gz 中进行了说明,并已签入Svn

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

请参阅5.1.2.1. Composite identifier,了解如何使用 JPA 注释。
Chapter 8. Component Mapping
8.4. Components as composite identifiers

【讨论】:

    【解决方案2】:

    我不确定我是否理解这个问题,因为它已经很晚了,但我会尝试的第一件事是覆盖 AttributeValue 的 equals 方法以包含这两个唯一的字段。

    【讨论】:

    • 没错。确保 AttributeValue 类正确实现了 equals 和 hashCode。 Hibernate 可能认为您正在合并的 AttributeValue 不在集合中。检查此链接以获取更多信息community.jboss.org/wiki/EqualsAndHashCode
    • 并尝试使用无序的Set&lt;AttributeValue&gt; 而不是List&lt;AttributeValue&gt;
    【解决方案3】:

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

    查看合并。尝试改用更新。

    【讨论】:

    • 我已经知道删除不会出现在插入之前。如果是这样,我就没有问题了
    • 也许合并不是合适的方法。
    猜你喜欢
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-12
    相关资源
    最近更新 更多