代码之家  ›  专栏  ›  技术社区  ›  Alex R

引用完整性约束冲突-如何在引用同一事务中的实体之前强制写入引用的实体?

  •  0
  • Alex R  · 技术社区  · 7 年前

    我有一个Hibernate实体,它包含对另一个实体的引用:

    class AddressEntity {
    
        private @ManyToOne(cascade=CascadeType.PERSIST) AddressKeyEntity addressKey;
    
        ...
    

    AddressKeyEntity 以及包含的对象 AddressEntity 通过一个 currentSession() @Transactional 服务,刷新时出现错误:

    Caused by: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "FKRA3ESUOPYYQJ271LGGEASAFXU: PUBLIC.ADDRESSENTITY FOREIGN KEY(ADDRESSKEY_ID) REFERENCES PUBLIC.ADDRESSKEYENTITY(ID) (14410)"; SQL statement:
    insert into AddressEntity (addressKey_id, latitude, longitude, id) values (?, ?, ?, ?) [23506-196]
    

    之前

    如何修复?

    这是 save 方法(我称之为 persist

    public void merge(AddressEntity item) {
        if(item != null) {
            item.resetBoundaries(nb -> mergeNamedBoundary(nb.getValue()));
            Session session = sessionFactory.getCurrentSession();
            if(!session.contains(item)) {
                session.save(item);
            }
        }
    }
    
    public AddressKeyEntity merge(AddressKeyEntity addressKeyEntity) {
    
        AddressKeyEntity ret = fetch(addressKeyEntity);
    
        if(ret == null) {
            sessionFactory.getCurrentSession().save(addressKeyEntity);
            ret = addressKeyEntity;
        }       
        return ret;
    
    }
    
    public Map<AddressKeyEntity, AddressEntity> persist(Map<AddressKeyEntity, AddressEntity> newValues) {
        Session session = sessionFactory.getCurrentSession();
    
        newValues.keySet().forEach(this::merge);
        session.flush();
    
        Map<AddressKeyEntity, AddressEntity> out = new HashMap<>();
        newValues.forEach((k,v) -> {
            AddressKeyEntity pKey = merge(v.getAddressKey());
            v.setAddressKey(pKey);
            merge(v);
            out.put(pKey, v);
        });
        session.flush();
        return out;
    }
    

    这里是AddressKeyEntity

    package com.ctc.gis.dto.address;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Table;
    import javax.persistence.UniqueConstraint;
    
    import org.hibernate.annotations.NaturalId;
    
    import com.ctc.gis.api.AddressKey;
    import com.fasterxml.jackson.annotation.JsonIgnore;
    
    @Entity
    @Table(uniqueConstraints = { @UniqueConstraint(columnNames = {"address", "city", "state", "zip", "apn"}) })
    public class AddressKeyEntity implements AddressKey {
    
        private @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) Integer id;
    
        private @NaturalId String address;
    
        private @NaturalId String city;
    
        private @NaturalId String state;
    
        private @NaturalId Integer zip;
    
        private @NaturalId String apn;
    
        public AddressKeyEntity() {
        }
    
        public AddressKeyEntity(String address, String city, String state, Integer zip, String apn) {
            this.setAddress(address);
            this.setCity(city);
            this.setState(state);
            this.setZip(zip);
            this.setApn(apn);
        }
    
        public AddressKeyEntity(AddressKey addressKey) {
            this(addressKey.getAddress(), addressKey.getCity(), addressKey.getState(), addressKey.getZip(), addressKey.getApn());
        }
    
        @Override
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String streetAddress) {
            this.address = normalize(streetAddress);
        }
    
        @Override
        public String getCity() {
            return city;
        }
    
        public void setCity(String city) {
            this.city = normalize(city);
        }
    
        @Override
        public String getState() {
            return state;
        }
    
        public void setState(String state) {
            this.state = normalize(state);
        }
    
        private static String normalize(String str) {
            return str == null ? null : str.toUpperCase();
        }
    
        @Override
        public Integer getZip() {
            return zip;
        }
    
        public void setZip(Integer zip) {
            this.zip = zip;
        }
    
        @Override
        public String getApn() {
            return apn;
        }
    
        public void setApn(String apn) {
            this.apn = apn;
        }
    
        @Override
        public String toString() {
            return asKey();
        }
    
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((apn == null) ? 0 : apn.hashCode());
            result = prime * result + ((address == null) ? 0 : address.hashCode());
            result = prime * result + ((city == null) ? 0 : city.hashCode());
            result = prime * result + ((state == null) ? 0 : state.hashCode());
            result = prime * result + ((zip == null) ? 0 : zip.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;
            AddressKeyEntity other = (AddressKeyEntity) obj;
            if (apn == null) {
                if (other.apn != null)
                    return false;
            } else if (!apn.equals(other.apn))
                return false;
            if (address == null) {
                if (other.address != null)
                    return false;
            } else if (!address.equals(other.address))
                return false;
            if (city == null) {
                if (other.city != null)
                    return false;
            } else if (!city.equals(other.city))
                return false;
            if (state == null) {
                if (other.state != null)
                    return false;
            } else if (!state.equals(other.state))
                return false;
            if (zip == null) {
                if (other.zip != null)
                    return false;
            } else if (!zip.equals(other.zip))
                return false;
            return true;
        }
    
        @JsonIgnore
        public boolean isTransient() {
            return id == null;
        }
    
        public Integer getId() {
            return id;
        }
    }
    
    1 回复  |  直到 7 年前
        1
  •  0
  •   Alex R    7 年前

    这个问题是由于 AddressKeyEntity.equals() ,如问题所示。

    if (getClass() != obj.getClass()) 所有直接的字段访问都会导致在比较一个普通的 AddressKeyEntity 被一个 休眠代理

    不正确的 equals() persist() 方法。

    通过替换Eclipse生成的 等于() 方法,该方法只使用接口上的字段getter比较每个字段,而忽略类差异。