Hibernate Natural ID重复问题

发布于 2024-11-08 13:56:24 字数 3702 浏览 0 评论 0原文

一般来说,我对 Hibernate 和 DB 都很陌生,所以请原谅这个基本问题。

我正在使用 DIS 协议,特别是 DIS 的 Open-DIS 实现。在DIS中,每个EntityStatePdu(包含模拟中实体的状态)都有一个EntityId对象,一个3个整数的元组。我想使用这个对象作为自然 ID,并维护一个标准的代理 ID。我的问题是我无法弄清楚如何确保数据库确定给定的 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 对象存储到数据库中(它们在具有相同的站点、应用程序、和实体)通过以下方式:

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'

我希望这足够清楚。谢谢。

I'm new to Hibernate and DBs in general, so pardon the elementary question.

I am working with the DIS protocol and specifically the Open-DIS implementation of DIS. In DIS, each EntityStatePdu (containing the state of an entity in the simulation) has an EntityId object, a tuple of 3 integers. I want to use this object as a Natural Id, and maintain a standard surrogate ID as well. My problem is I cannot figure out how to ensure that the DB determines that a given EntityId already exists and use that EntityId's primary key as the foreign key in the EntityStatePdu.

In other words, say I have two EntityStatePdus, with EntityID (1, 2, 3); i.e. we have two updates from the same entity. I would want something like the following:

tables:

entity_id 
pk   site   app    entity
0    1      2      3


entity_state_pdu
pk  entity_id_fk  timestamp
0   0             1
1   0             2

Here are the simplified classes I am testing with:

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

and

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

I attempt to store two TestEntity objects into the DB with two separate TestId objects (which are equal in terms of having the same site, app, and entity) in the following way:

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();
}

I get a long stack trace on the session2.save(entity2) line, with the salient line being

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

I hope this is clear enough. Thank you.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

隐诗 2024-11-15 13:56:24

正如 Beatles 所说,当您告诉 Hibernate 您有一个自然键时,它会告诉 DB 强制执行唯一性。本例中的唯一性是通过引发您所看到的类型的异常 (MySQLIntegrityConstraintViolationException) 来强制执行的。

据我所知,解决此问题的唯一方法是首先尝试获取与您的业务身份匹配的对象(TestId 相等),然后使用该实例(如果找到)或使用新实例(如果未找到)。 Hibernate 不会自动为您执行此操作。

As Beatles said, when you tell Hibernate you have a Natural Key, it tells the DB to enforce uniqueness. The uniqueness in this case is being enforced by raising an exception of the type you saw (MySQLIntegrityConstraintViolationException).

The only way around this that I'm aware of is to first try and fetch an object that matches your business identity (TestId's equal) first, and then work either with that instance if it's found or with a new instance if it's not. Hibernate doesn't automatically do that for you.

混浊又暗下来 2024-11-15 13:56:24

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

在您的代码中:

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

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

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

When you mark some fields as Natural ID it means that in that context the combination of these fields will be unique therefore for example if you have a class called person that has FirstName and LastName as Natural ID there could be only one entity having John as its FirstName and Smith as its LastName.(it is similar to a unique index)

In your code :

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

and

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

are referring to two different entities with the same Natural ID thus they can't both be persisted to the database.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文