сохранение объекта с зависимыми объектами, на которые есть ссылки, с помощью hibernate - PullRequest
12 голосов
/ 12 января 2012

Мы используем Hibernate в качестве слоя постоянства и имеем сложную объектную модель.Не раскрывая реальную модель данных, я хочу объяснить проблему на следующем простом примере.

class Person {
    private Integer id; //PK
    private String name;
    private Account account;
    // other data, setters, getters
}


class Account {
    private Integer id; //PK
    // other data, setters, getters
}

Отображение БД определяется с использованием HBM следующим образом:

 <class name="Person" table="PERSON">
    <id name="id" column="ID">
        <generator class="native"/>
    </id>
    <version name="version" type="java.lang.Long"/>
    <property name="name" type="java.lang.String" length="50" column="NAME"/>
    <many-to-one name="account" column="ACCOUNT_ID"
                class="com.mycompany.model.Account"/>

</class>

Я должен сохранитьНовый заполненный экземпляр Person связан с существующим Account.Вызов инициируется веб-клиентом, поэтому на моем уровне я получаю экземпляр Person, на который ссылается экземпляр Account, который содержит только его идентификатор.

Если я пытаюсь вызвать saveOrUpdate(person), выдается следующее исключение:

org.hibernate.TransientObjectException: 
object references an unsaved transient instance - save the transient instance before flushing: 
com.mycompany.model.Account

Чтобы избежать этого, я должен найти постоянный объект Account по идентификатору и затем вызвать person.setAccount(persistedAccount).В этом случае все работает отлично.

Но в реальной жизни я имею дело с десятками сущностей, связанных друг с другом.Я не хочу писать специальный код для каждой ссылки.

Интересно, есть ли какое-то общее решение для этой проблемы.

Ответы [ 6 ]

7 голосов
/ 12 января 2012

Чтобы сохранить одну сущность, вам просто нужно иметь ссылки на ее прямые зависимости.Тот факт, что эти другие объекты ссылаются на другие объекты, не имеет значения.

Лучший способ сделать это - получить прокси для сущности, на которую ссылаются, даже не обращаясь к базе данных, используя session.load(Account.class, accountId).

То, что вы делаете, является правильным решением: получить ссылку на постоянную учетную запись и установить эту ссылку во вновь созданной учетной записи.

2 голосов
/ 24 августа 2013

Hibernate позволяет вам использовать только ссылочные классы с ID, вам не нужно делать session.load ().

Единственное, что важно, это то, что если у вашего ссылочного объекта есть VERSION, тогда должна быть установлена ​​версия. В вашем случае вы должны указать версию объекта Account.

1 голос
/ 12 января 2012

Используйте cascade="all" для сопоставления * -to-

0 голосов
/ 14 января 2014

Существует еще одно решение проблемы - использование конструктора по умолчанию для сущности, на которую вы хотите ссылаться, и установка идентификатора и версии (если она версионная).У нас есть следующий метод dao:

public <S extends T> S materialize(EId<S> entityId, Class<S> entityClass) {
        Constructor<S> c = entityClass.getDeclaredConstructor();
        c.setAccessible(true);

        S instance = c.newInstance();
        Fields.set(instance, "id", entityId.getId());
        Fields.set(instance, "version", entityId.getVersion());
        return instance; // Try catch omitted for brevity.
}

Мы можем использовать такой подход, потому что мы не используем отложенную загрузку, а вместо этого имеем «представления» сущностей, которые используются в GUI.Это позволяет нам избегать всех объединений, которые использует Hibernate, чтобы заполнить все активные отношения.Представления всегда имеют идентификатор и версию объекта.Следовательно, мы можем заполнить ссылку, создав объект, который в Hibernate будет казаться не переходным.

Я пробовал и этот подход, и тот, что был в методе session.load ().Они оба работали нормально.Я вижу некоторое преимущество в моем подходе, так как Hibernate не пропустит свои прокси в других местах кода.Если не используется должным образом, я получу NPE вместо исключения «нет привязки к потоку».

0 голосов
/ 12 января 2012

спасибо за помощь. Я уже реализовал свое собственное общее решение, но хотел знать, существуют ли другие решения.

Я хочу поделиться с вами этой идеей. Я называю ссылочные объекты, которые не содержат ничего, кроме идентификатора (или другого поля, которое можно использовать для уникальной идентификации объекта) placeholder.

Итак, я создал аннотацию @Placeholder и разместил ее во всех ссылочных полях. В нашем примере это поле учетной записи класса Person. У нас уже есть класс с именем GenericDao, который упаковывает Hibernate API и имеет метод save(). Я добавил еще один метод saveWithPlacehodlers(), который делает следующее. Он обнаруживает класс данного объекта путем отражения, находит все поля, помеченные аннотацией @Placeholder, находит объекты в БД и вызывает соответствующий установщик в основной сущности, чтобы заменить указанную метку-заполнитель постоянной сущностью.

Аннотация @Placeholder позволяет определить поле, которое будет использоваться для идентификации объекта. По умолчанию id.

Что вы думаете, ребята, об этом решении?

0 голосов
/ 12 января 2012

Вы пробовали cascade="save-update" в many-to-one элементе?Hibernate по умолчанию cascade="none" ...

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