powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Java [игнор отключен] [закрыт для гостей] / Hibernate - cохранение объекта
25 сообщений из 37, страница 1 из 2
Hibernate - cохранение объекта
    #39301216
myaucha
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
БД - Oracle, создан сиквенс и привязан аннотацией к объекту. При создании нового java-объекта поле id изначально равно нулю. Когда объект сохраняется через save(obj) метод отрабатывает успешно и генерируется id отличный от нуля, хотя одно из полей в таблице (назовем его условно field) имеет уникальный индекс. Затем происходит выход из транзакции (из метода, помеченного аннотацией @Transactional). В этот момент выбрасывается исключение нарушения ограничения уникальности field, транзакция откатывается. Сгенерированный id остается в java-объекте, что неприятно, приходится "вручную" его обнулять.

Вопрос. Почему исключение не возникло на самом save(obj)? То есть хибер закэшировал вставку?! Ведь если зайти в pl/sql developer и вручную провести подобную операцию, то исключение появится сразу, а не при попытке зафиксировать транзакцию.
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39301220
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
myauchaПри создании нового java-объекта поле id изначально равно нулю.
Почему не null?

myauchaСгенерированный id остается в java-объекте, что неприятно, приходится "вручную" его обнулять.

А вот это интересный факт.

myauchaВопрос. Почему исключение не возникло на самом save(obj)?
Потому что Session aka EntityManager aka Unit of Work ещё называется кешем первого уровня. Это такая оптимизация, чтобы на каждый пук не генерировать SQL запрос. Для особо торопливых есть метод flush()

myauchaТо есть хибер закэшировал вставку?!

Конечно.

myauchaВедь если зайти в pl/sql developer и вручную провести подобную операцию, то исключение появится сразу, а не при попытке зафиксировать транзакцию.
Ведь ORM и PL/SQL это не одно и то же?
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39301224
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
myaucha,

По поводу отката id вот тут хорошо описано
http://stackoverflow.com/questions/20636144/entity-state-and-entity-id-value-in-jpa-hibernate-after-rollback
JPA требует такого поведения
Но хибер позволяет включить опцию hibernate.use_identifier_rollback
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39301323
myaucha
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пока читаю про состояния объекта попутно задам уточняющие вопросы

BlazkowiczmyauchaПри создании нового java-объекта поле id изначально равно нулю.
Почему не null?Объявлял id как int, поэтому и ноль. Есть какие-то предубеждения против использования простых типов в качестве идентификаторов?!

BlazkowiczПо поводу отката id вот тут хорошо описано
http://stackoverflow.com/questions/20636144/entity-state-and-entity-id-value-in-jpa-hibernate-after-rollback
JPA требует такого поведения
Но хибер позволяет включить опцию hibernate.use_identifier_rollbackОпция use_identifier_rollback не помогла. Добавлял ее, как в таком варианте - hibernate.use_identifier_rollback, так и без префикса - use_identifier_rollback.
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39301334
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
myauchaЕсть какие-то предубеждения против использования простых типов в качестве идентификаторов?!
Конечно. БД разрешает вам хранить 0 в колонке первичного ключа. А вот null не разрешит.

myauchaв таком варианте - hibernate.use_identifier_rollback, так и без префикса - use_identifier_rollback.
Тут я ничего не могу сказать. Надо посмотреть как он используется в исходнике. И куда вы его вообще пихали.
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39301347
myaucha
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
BlazkowiczmyauchaЕсть какие-то предубеждения против использования простых типов в качестве идентификаторов?!
Конечно. БД разрешает вам хранить 0 в колонке первичного ключа. А вот null не разрешит.Пока не убедили. Если приложение выполняет проверку на id == 0 и отправляет объект в зависимости от результата либо на save(obj), либо на update(obj), то риска вроде бы нет?!

myauchaв таком варианте - hibernate.use_identifier_rollback, так и без префикса - use_identifier_rollback.
Тут я ничего не могу сказать. Надо посмотреть как он используется в исходнике. И куда вы его вообще пихали.[/quot]В статье (да и в документации к настройке это прописано) высказывались предположения о том, что эта настройка работает только для delete.

Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.use_identifier_rollback">true</prop>
                <prop key="hibernate.format_sql">true</prop>
            </props>
        </property>



Вы-то сами как поступаете в проектах с идентификаторами объектов, когда выполняется Вставка и нарушаются ограничения, приводящие к откату?
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39301361
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
myauchaто риска вроде бы нет?!
Assumption is the mother of all fuck ups.
1) Целостность данных БД нужно обеспечивать средствами БД, а не ORM.
2) Сторонние библиотеки (например Spring Data) не в курсе деталей вашего Hibernate маппинга и опираются на общие практики, такие как проверку id на null.
3) В случае присвоения кем-то значения 0 по ошибке (генератор сломался или ещё какой лисопед самоуправством занялся) вы не сможете отличить его от значения 0 по-умолчанию.
4) Некоторые бд имеют по-умолчанию инкремент от 0. Что может выйти боком, когда вдруг понадобится портировать с Oracle.

Остаётся вопрос. Почему бы сразу не убрать грабли?


myauchaВы-то сами как поступаете в проектах с идентификаторами объектов, когда выполняется Вставка и нарушаются ограничения, приводящие к откату?
Обычно, я про этот объект забываю. Он невалидный. Зачем мне его состояние? Нужно сформировать новый.
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39301393
myaucha
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
myauchaВы-то сами как поступаете в проектах с идентификаторами объектов, когда выполняется Вставка и нарушаются ограничения, приводящие к откату?Обычно, я про этот объект забываю. Он невалидный. Зачем мне его состояние? Нужно сформировать новый.[/quot]Вколачивал пользователь данные в форму, они пришли в контроллер в виде объекта, объект семантически верный, пытаемся его сохранить, но ограничения в БД не позволили это сделать. Логично передать объект пользователю обратно на доработку.
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39301402
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
myauchaВколачивал пользователь данные в форму, они пришли в контроллер в виде объекта, объект семантически верный, пытаемся его сохранить, но ограничения в БД не позволили это сделать. Логично передать объект пользователю обратно на доработку.
Во-первых, объекты на UI и в ORM совпадают только в простейших случаях. При большой модели предметной области оказывается очевидным что объекты на UI нужны совсем не такие как в ORM.
Во-вторых почему мы в UI потеряли данные, если мы их ещё не сохранили?
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39301432
no56892
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
myaucha,
1.Руками обнулять.
2.Первой же строчкой в сервисе делать копию того, что пришло.
3.Dto и им подобное.
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39301435
no56892
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
авторПри большой модели предметной области оказывается очевидным что объекты на UI нужны совсем не такие как в ORM.
Вопрос нахрена тогда вообще нужен ORM?
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39301443
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
myaucha,
Значит на sql.ru дураки. Т.к. в форуме если объект не прошел, то он потерян.
Хотя написать за свой счет можно что угодно. Заказчик только рад будет.
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39301447
myaucha
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
BlazkowiczВо-первых, объекты на UI и в ORM совпадают только в простейших случаях. При большой модели предметной области оказывается очевидным что объекты на UI нужны совсем не такие как в ORM.
Во-вторых почему мы в UI потеряли данные, если мы их ещё не сохранили?В общем понятно. В простом случае (когда объект ввода является объектом сохранения) тот объект, который приходит в контроллер надо копировать и сохранять в базу уже копию. Если транзакция завершается откатом, то копию забываем и передаем пользователю для повторного редактирования оригинал.
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39301449
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
myaucha,
Разве при райзе у вас объект куда то пропал?
1. Правило в веб - не допускать райзе. Откуда оно?
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39301477
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
no56892Вопрос нахрена тогда вообще нужен ORM?
Ну, если у вас кроме UI и БД других слоёв нет, то в вашей архитектуре, вероятно не нужен.
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39301478
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
myauchaВ общем понятно. В простом случае (когда объект ввода является объектом сохранения) тот объект, который приходит в контроллер надо копировать и сохранять в базу уже копию. Если транзакция завершается откатом, то копию забываем и передаем пользователю для повторного редактирования оригинал.
Обратите ещё внимание на такую фичу hibernate как dirty check. Описанный вами вариант не единственный. Например можно контроллер научить заполнять уже загруженный объект. А не использовать тот, который он создал.
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39302223
myaucha
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Еще один вопрос. Поскольку он связан с существующим, то не буду создавать отдельную тему. Если происходит сохранение объекта в методе контроллера через вызов service.save(obj). Метод используется для создания нового объекта или изменения существующего. Известно, что при сохранении могут возникнуть две ошибки, которые должны быть обработаны. Первая ошибка ObjAlreadyExists возникает когда объект с указанным именем уже существует в базе, вторая ObjNotFound - объект не найден в базе (например, в результате оптимистичного метода редактирования и последующего сохранения). Естественно возникает желание сделать нечто такое:
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
            try {
                obj = service.save(obj);
            }
            catch (ObjAlreadyExists e) {
                bindingResult.rejectValue("obj", "NotUnique.obj.name");
                return InitObjEditModel(obj, "edit");
            }
            catch (ObjNotFound e) {
                bindingResult.rejectValue("obj", "NotFound.obj.id");
                return InitObjEditModel(obj, "edit");
            }



В самом service.save вызывается dao.save, в нем генерируются исключения, но не ObjAlreadyExists или ObjNotFound, а другие унаследованные от DataAccessException или от RuntimeException. Причем, реализация dao может быть выполнена для jdbc, для hibernate или для чего-то еще. В каждой реализации исключения будут разные (некоторые могут и совпадать). Есть желание трансформировать их в ObjAlreadyExists и ObjNotFound. В документации Spring указано, что потомки DataAccessException - это преобразованные (например, из jdbc или hibernate исключений) runtime-исключения, и что Spring рекомендует использовать именно их вместо нудных описаний throws в методах. Предположим, их доводы убедительны плюс не хочется засорять код dao-реализаций конструкциями try/catch и генерированием соответствующих ObjAlreadyExists и ObjNotFound-исключений. Поэтому объявляю ObjAlreadyExists и ObjNotFound наследниками runtime-исключений и пытаюсь их преобразовать...

Вопрос где лучше сделать такое преобразование?! Если сделать отдельный метод в контроллере, обозначив его аннотацией @ExceptionHandler, то после преобразования уже, насколько я понимаю, не получится вернуться в try/catch obj = service.save(obj) и продолжить оперировать объектами obj или bindingResult. То есть, существует ли способ перехватить исключение, выполнить преобразование и продолжить подниматься по цепочке вызовов. Насколько я понял, разные варианты реализации обработки - @ControllerAdvice или интерфейс HandlerExceptionResolver - это все обработка упущенных (необработанных) исключений. А нужен некий interceptor.

Может вообще надо делать по-старинке (по крайней мере для данного примера) - в dao перехватывать исключения и генерировать ObjAlreadyExists и ObjNotFound, унаследованные от обычного Exception. И протянуть (посредством throws-описания в заголовке метода) их через dao.save и service.save, обязав к тому же пользователя предусмотреть обработку try/catch?!
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39302243
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
myauchaЕще один вопрос. Поскольку он связан с существующим, то не буду создавать отдельную тему.
В подфоруме Java все вопросы связаны одной технологией. Ваш новый вопрос к первоначальному ведь не относится совершенно.

myauchaПервая ошибка ObjAlreadyExists возникает когда объект с указанным именем уже существует в базе
У объектов нет имён. И кстати, в случае ORM говорят о сущностях (entity), потому как два объекта могут быть одной сущностью, например.

myauchaвторая ObjNotFound - объект не найден в базе (например, в результате оптимистичного метода редактирования и последующего сохранения).
Вот этого я вообще не понял. Как NotFound связан с оптимистичной блокировкой?

myauchaВ самом service.save вызывается dao.save, в нем генерируются исключения, но не ObjAlreadyExists или ObjNotFound

DAO для ORM почти бесполезны. Обычно они вырождаются в Repository.

myaucha, а другие унаследованные от DataAccessException или от RuntimeException.
Если вам это критично, то нужно для каждой реализации написать обработчик и конвертор исключений.
http://docs.spring.io/autorepo/docs/spring/4.2.x/spring-framework-reference/html/dao.html

myauchaПричем, реализация dao может быть выполнена для jdbc, для hibernate или для чего-то еще.
Я надеюсь вы не используете одну и ту же базу для JDBC и ORM?

myauchaВ документации Spring указано, что потомки DataAccessException - это преобразованные (например, из jdbc или hibernate исключений) runtime-исключения, и что Spring рекомендует использовать именно их вместо нудных описаний throws в методах.

Зачем вы всё в кучу мешаете? DataAccessException это одно. Runtime vs Checked Exception это отдельная тема.

myauchaВопрос где лучше сделать такое преобразование?!

Очевидно, что в DAO.

myauchaЕсли сделать отдельный метод в контроллере, обозначив его аннотацией @ExceptionHandler, то после преобразования уже, насколько я понимаю, не получится вернуться в try/catch obj = service.save(obj) и продолжить оперировать объектами obj или bindingResult. То есть, существует ли способ перехватить исключение, выполнить преобразование и продолжить подниматься по цепочке вызовов. Насколько я понял, разные варианты реализации обработки - @ControllerAdvice или интерфейс HandlerExceptionResolver - это все обработка упущенных (необработанных) исключений. А нужен некий interceptor.

Ну, логично что это можно сделать через AOP. Но только не на уровне MVC, когда уже поздно обрабатывать ошибку обращения к данным.
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39302263
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
myaucha,
Где ты взял эти исключения?
У тебя весь роман текста построен на этих двух именах.
Может
SaveOrUpdate (объект
?
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39302545
myaucha
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В итоге получилось следующее

Исключения
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
public final class Exceptions {

    private Exceptions() {}

    public static class UserAlreadyExists extends DuplicateKeyException {
        public UserAlreadyExists(User user, Throwable cause) {
            super("User id [" + user.getUsername() + "] already exists", cause);
        }
    }

    public static class UserNotFound extends DataRetrievalFailureException {
        public UserNotFound(User user, Throwable cause) {
            super("User id [" + user.getId() + "] not found", cause);
        }
    }
}


Контроллер
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
            ...
            try {
                user = userService.save(user);
            }
            // уже существует
            catch (Exceptions.UserAlreadyExists e) {
                bindingResult.rejectValue("username", "NotUnique.user.username");
                return InitUserEditModel(user, error_cmd);
            }
            // не найден (возможно был удален)
            catch (Exceptions.UserNotFound e) {
                bindingResult.rejectValue("username", "NotFound.user.username");
                return InitUserEditModel(user, error_cmd);
            }
            ...


Сервис
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public User save(User user) {

        User copyUser = new User(user);
        userDAO.save(copyUser);
        return copyUser;
    }


DAO (JDBC)
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
    @Override
    public void save(User user) {

        try {
            if (user.getId() == null)
                insert(user);
            else
                update(user);
        }
        catch (DuplicateKeyException e) {
            throw new Exceptions.UserAlreadyExists(user, e);
        }
    }

    private void update(User user) {
        ...
        if (jdbcTemplate.update(SQL, params) == 0)
            throw new Exceptions.UserNotFound(user, null);
    }


DAO (Hibernate)
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
    @Override
    public void save(User user) {

        try {
            getCurrentSession().saveOrUpdate(user);
            getCurrentSession().flush();
        }
        catch(ConstraintViolationException e) {
            throw new Exceptions.UserAlreadyExists(user, e);
        }
        catch(StaleStateException e) {
            throw new Exceptions.UserNotFound(user, e);
        }
    }



Особенности: Поскольку исключения UserAlreadyExists и UserNotFound рантаймовские, то при написании в контроллере userService.save не будет требования их обработки - это минус. Зато не надо тянуть их через слои и прописывать атрибут rollbackFor для @Transactional, чтобы откатывалась транзакция.

Странности: В DAO для jdbc перехватывается ошибка spring-а DuplicateKeyException, то есть уже после преобразования. В DAO же для hibernate перехватываются хибернетовские исключения ConstraintViolationException и StaleStateException. Если выйти из хибернетовского save, то ошибка останется по-прежнему ConstraintViolationException. Если выйти из сервисного save, то исключение преобразуется в spring-овое. Если теперь прописать в настройках
Код: xml
1.
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

, то В DAO для hibernate по-прежнему перехватываются хибернетовские исключения, на выходе они преобразуются в spring-овые, при выходе из транзакции как и раньше - spring-овые. Сейчас, пока писал, понял почему для JDBC сразу получаю spring-овое - потому что через jdbcTemplate работаю. Так что все понятно.

Ну и flush пришлось все-таки написать, потому что иначе ошибку можно будет отловить только на выходе из транзакции (то есть в контролере), а тогда уже поздно - выходить из сервиса ошибка должна уже преобразованной.
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39302561
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
myauchaUserAlreadyExists и UserNotFound
где взял эти исключения?
Их не будет при сохранении объекта хибером.
19618727
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39302566
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
myaucha,
вот это DuplicateKeyException - с чего вдруг у тебя появится?
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39302584
no56892
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
myaucha,
У тебя какая-то фигня с настройкой Spring + hibernate походу.
DuplicateKeyException это если только делать persist с уже проставленным id - то возможно.
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39302588
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
no56892myaucha,
У тебя какая-то фигня с настройкой Spring + hibernate походу.
DuplicateKeyException это если только делать persist с уже проставленным id - то возможно.
+1
Что-то он неделю борется как Дон кихот с этой проблемой.
...
Рейтинг: 0 / 0
Hibernate - cохранение объекта
    #39302591
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В хибере (ОРМ) всё просто:
- мы знаем какой (новый или существующий) мы правим и указываем хиберу
- хибер сам знает что делать чтобы исключений не было
- если СПЕЦИАЛЬНО указать ему что не надо затирать изменения соседом, то будет исключение всего одно по оптимистической блокировке.
Всё.
...
Рейтинг: 0 / 0
25 сообщений из 37, страница 1 из 2
Форумы / Java [игнор отключен] [закрыт для гостей] / Hibernate - cохранение объекта
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]