我有一个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]
从该错误看来,引用对象正在被引用对象之前 写入数据库,从而导致FK约束违反。
如何解决?
更新
这是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;
}
}
答案 0 :(得分:0)
问题是由AddressKeyEntity.equals()
的不适当实现引起的,如问题所示。
在将普通if (getClass() != obj.getClass())
与已被休眠代理替换的AddressKeyEntity
进行比较时,条件equals()
和所有直接字段访问都会导致不正确的不匹配。>
错误的persist()
导致equals()
方法中的HashMap逻辑崩溃。
通过用定制的方法替换Eclipse生成的class Test : IJobTask
{
public void Start(string val = "")
{
throw new NotImplementedException();
}
}
public interface ITest
{
void MyMethod<T>(T model) where T : IJobTask;
}
public class ConcreteTest : ITest
{
public void MyMethod<T>(T model) where T : IJobTask
{
}
}
public class Main
{
public Main()
{
var ct = new ConcreteTest();
ct.MyMethod(new Test());
}
}
方法来解决,该方法仅使用接口上的字段获取器比较每个字段,而忽略类差异。