Hibernate Natural ID дубликат выпуска - PullRequest
6 голосов
/ 19 мая 2011

Я новичок в Hibernate и БД в целом, поэтому простите за простейший вопрос.

Я работаю с протоколом DIS и, в частности, с реализацией DIS-Open-DIS.В DIS каждый EntityStatePdu (содержащий состояние объекта в моделировании) имеет объект EntityId, кортеж из 3 целых чисел.Я хочу использовать этот объект в качестве естественного идентификатора, а также поддерживать стандартный суррогатный идентификатор.Моя проблема в том, что я не могу понять, как убедиться, что БД определяет, что данный 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;
    }
}

Я пытаюсь сохранить два объекта TestEntity в БД с двумя отдельными объектами TestId (которые равны с точки зрения наличия одного и того же сайта, приложения и объекта) следующим образом:

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 ]

2 голосов
/ 19 мая 2011

Как сказал Битлз, когда вы сообщаете Hibernate, что у вас есть Natural Key, он сообщает DB о необходимости обеспечения уникальности.Уникальность в этом случае обеспечивается применением исключения типа, который вы видели (MySQLIntegrityConstraintViolationException).

Единственный способ обойти это, о котором я знаю, - это сначала попытаться получить объект, который соответствует вашему бизнесу.идентичность (TestId равен) сначала, а затем работать либо с этим экземпляром, если он найден, либо с новым экземпляром, если это не так.Hibernate не делает это автоматически для вас.

1 голос
/ 19 мая 2011

Когда вы помечаете некоторые поля как Natural ID, это означает, что в этом контексте комбинация этих полей будет уникальной, поэтому, например, если у вас есть класс с именем person, который имеет FirstName и LastName в качестве Natural ID, может быть только один объект, имеющийДжон как его FirstName и Смит как его LastName. (Это похоже на уникальный индекс)

В вашем коде:

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

и

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

ссылаютсядвум разным сущностям с одинаковым Natural ID, поэтому их нельзя сохранить в базе данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...