Hibernate Natural ID重复问题

时间:2011-05-19 12:50:58

标签: java sql hibernate relational-database unique-constraint

我是Hibernate和DB的新手,所以请原谅这个基本问题。

我正在使用DIS protocol,特别是DIS的Open-DIS实现。在DIS中,每个EntityStatePdu(包含模拟中实体的状态)都有一个EntityId对象,一个3个整数的元组。我想将此对象用作自然ID,并保持标准的代理ID。我的问题是我无法弄清楚如何确保DB确定给定的EntityId已经存在并使用该EntityId的主键作为EntityStatePdu中的外键。

换句话说,我有两个EntityStatePdus,EntityID(1,2,3);即我们有两个来自同一实体的更新。我想要以下内容:

表:

entity_id 
pk   site   app    entity
0    1      2      3


entity_state_pdu
pk  entity_id_fk  timestamp
0   0             1
1   0             2

以下是我正在测试的简化类:

@Entity
public class TestEntity {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    @NaturalId
    @ManyToOne(cascade = CascadeType.ALL)
    private TestId naturalId;

    public Long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public TestId getNaturalId() {
        return naturalId;
    }

    public void setNaturalId(TestId naturalId) {
        this.naturalId = naturalId;
    }
}

@Entity
public class TestId {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    @NaturalId
    private int site;
    @NaturalId
    private int app;
    @NaturalId
    private int entity;

    public TestId(int site, int app, int entity) {
        this.site = site;
        this.app = app;
        this.entity = entity;
    }

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public int getSite() {
        return site;
    }
    public void setSite(int site) {
        this.site = site;
    }
    public int getApp() {
        return app;
    }
    public void setApp(int app) {
        this.app = app;
    }
    public int getEntity() {
        return entity;
    }
    public void setEntity(int entity) {
        this.entity = entity;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + app;
        result = prime * result + entity;
        result = prime * result + site;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        TestId other = (TestId) obj;
        if (app != other.app)
            return false;
        if (entity != other.entity)
            return false;
        if (site != other.site)
            return false;
        return true;
    }
}

我尝试使用两个独立的TestId对象(具有相同的站点,应用程序和实体)来将两个TestEntity对象存储到DB中,方法如下:

public static void main(String[] args) {

    SessionFactory factory = createFactory();

    Session session = factory.openSession();
    Transaction tx = session.beginTransaction();
    TestId id1 = new TestId(1,2,3);
    TestEntity entity1 = new TestEntity();
    entity1.setNaturalId(id1);
    session.save(entity1);
    tx.commit();
    session.close();

    Session session2 = factory.openSession();
    Transaction tx2 = session2.beginTransaction();
    TestId id2 = new TestId(1,2,3);
    TestEntity entity2 = new TestEntity();
    entity2.setNaturalId(id2);
    session2.save(entity2);
    tx2.commit();
    session2.close();
}

我在session2.save(entity2)行上得到一个很长的堆栈跟踪,其中显着的行是

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '2-3-1' for key 'app'

我希望这很清楚。谢谢。

2 个答案:

答案 0 :(得分:2)

正如甲壳虫乐队所说,当你告诉Hibernate你有一个自然键时,它告诉数据库强制执行唯一性。这种情况下的唯一性是通过引发您看到的类型的异常(MySQLIntegrityConstraintViolationException)来强制执行的。

我知道的唯一解决方法是首先尝试获取与您的业务标识(TestId相等)匹配的对象,然后在找到该实例时使用该实例,或者如果是,则使用新实例不。 Hibernate不会自动为您执行此操作。

答案 1 :(得分:1)

当您将某些字段标记为自然ID时,这意味着在该上下文中,这些字段的组合将是唯一的,因此,例如,如果您有一个名为person的类,其具有FirstName和LastName作为自然ID,则可能只有一个实体具有John作为其FirstName,Smith作为其LastName。(它类似于唯一索引)

在您的代码中:

TestId id1 = new TestId(1,2,3);

TestId id2 = new TestId(1,2,3);

指的是具有相同自然ID的两个不同实体,因此它们不能同时保存到数据库中。