Ответы пользователя по тегу Hibernate
  • Почему хибернейт не апдейтит таблицу?

    @bobzer
    Java EE Developer
    Поиск в Гугле по фразе "hibernate.hbm2ddl.auto update not working" даёт следующую информацию о принципах работы "update":
    • Добавляет новые столбцы
    • Не удаляет столбцы
    • Не изменяет столбцы

    Для полноценной работы со структурой БД используйте системы миграции, например, Liquibase или FlywayDb. Функции, встроенные в Hibernate, малопригодны для реальных проектов, они подходят в основном для тестов.
    Ответ написан
  • Spring Security. Разные права для разных пользователей. (Как скрыть для Коли (одного из юзеров) возможность "Создать пост" ) ???

    @bobzer
    Java EE Developer
    Никакой фреймворк общего применения не знает требований Вашего конкретного приложения. Это значит, что доступ к каждому действию придётся так или иначе прописать в самом приложении. Если требуется тонкая настройка доступов, то нередко создаётся понятие "права на действие", множество которых можно собирать в роли. При этом приложение "знает" о правах (тонких настройках), а роли создаются произвольно в административной части приложения и наполняются нужными правами. Такой подход создаёт дополнительные сложности в программировании и в последующей эксплуатации, но это плата за гибкость. Для обеспечения максимальной гибкости, при разработке приложения на каждое действие, доступное пользователю, придётся создавать соответствующее право, и в приложении прописывать его проверку.
    Ответ написан
  • Как добавить новые данные в таблицу?

    @bobzer
    Java EE Developer
    Метод setMap следует вызывать только в случае если вы только что создали экземпляр @Entity, и ещё не сохраняли его в БД. Если же сущность уже сохранена в БД, или считана из БД, то вместо интерфейса Map, объявленного в классе сущности, подставляется его реализация, связанная с функциями работы с БД. "Движок" JPA (обычно Hibernate) "видя", что на месте Map такая реализация, как бы "знает", что это связанные записи, которые уже сохранены в БД. Если же подменить целиком Map, то это "знание" теряется и начинается непредвиденное поведение.

    Для модификации списка, хранимого в Map, следует вызывать методы Map.put и Map.remove:
    entity.getMap().put(...);
    entity.getMap().remove(child);

    Hibernate отследит эти обращения и при сохранении родительской сущности корректно добавит/удалит дочерние объекты.
    Ответ написан
  • Как наследовать @Entity не mapped классом в Hibernate?

    @bobzer
    Java EE Developer
    Если я правильно понимаю, все классы проецируются на одну таблицу. При этом Field содержит проекции тех полей таблицы, которые используются во всех классах, а наследники Field проецируют дополнительно специфические для них поля. В таком случае должна подойти следующая конструкция:

    @MappedSuperclass
    abstract class Field {
    //...
    }
    
    @Entity
    @Table(name = "FIELDS")
    class SliderField extends Field {
    //...
    }
    
    @Entity
    @Table(name = "FIELDS")
    class DateField extends Field {
    //...
    }

    Основная Ваша ошибка в том, что аннотацией @Entity должны помечаться конечные классы, а не родительские. Промежуточные родительские классы при этом следует отметить как @MappedSuperclass.
    Ответ написан
  • Как правильно задеплоить проект EAR в wildfly 9.0.2?

    @bobzer
    Java EE Developer
    Судя по всему, разворачиваемое приложение требует наличие домена безопасности с именем "JbSecurityDomain". Обычно, он используется для аутентификации пользователей приложения, в таком случае ссылка на него должна быть из ВАШ.EAR/НЕКИЙ.WAR/WEB-INF/jboss-web.xml. Описание домена должно быть добавлено в раздел <security-domains> конфигурации сервера.

    Если нет никакой информации о том, как настраивать безопасность для конкретно вашего приложения, то это может стать проблемой. Но, раз уж есть Datasource, то с высокой степенью вероятности можно предположить, что информация о пользователях хранится в БД. Если это так, то можно попытаться добавить примерно следующее:
    <security-domains>
    ...
                    <security-domain name="JbSecurityDomain">
                        <authentication>
                            <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule" flag="required">
                                <module-option name="dsJndiName" value="имя-вашего-datasource"/>
                                <module-option name="principalsQuery" value="select password from users where login = ?"/>
                                <module-option name="rolesQuery" value="select t3.code, 'Roles' from users t1 inner join user_roles t2 on t2.user_id = t1.id and t1.login = ? inner join roles t3 on t2.role_id = t3.id"/>
                            </login-module>
                        </authentication>
                    </security-domain>
                </security-domains>

    В principalsQuery и rolesQuery вставить SQL-запросы выборки данных в соответствии со структурой вашей БД. В rolesQuery первым полем должно быть имя роли из таблицы, вторым полем константа 'Roles'.
    Ответ написан
  • Откуда ошибка "Column 'id' cannot be null" в Hibernate?

    @bobzer
    Java EE Developer
    Проверьте настройки дочерних сущностей. Мысль примерно такая - генерация ID настроена везде, но Hibernate не всегда "знает" о том, что в конкретном случае надо использовать эту генерацию. Например, вы сохраняете нового пользователя - сохранение проходит. Затем вы добавляете роль в список ролей пользователя, сохраняете - и возникает ошибка потому, что в маппинге списка ролей не указано CascadeType.PERSIST или CascadeType.ALL. Если вы не "сказали" Hibernate-у сохранять дочерние сущности, но добавили новый объект в список, то не будет попытки создать добавленный объект в БД. Если дело именно в дочерних объектах, то этим может объясняться и непостоянство возникновения ошибки: не добавляли дочерних объектов - всё ОК, добавили - ошибка.

    Проверьте все связи @OneToMany.
    Ответ написан
  • Почему Hibernate не подхватывает новые записи в базе?

    @bobzer
    Java EE Developer
    Проверьте как у вас завершаются транзакции после добавления записей, возможно, забыли сделать commit. Попробуйте добавить запись и проверить её наличие средствами самой БД (SQL-запросом), а не в вашем приложении.

    Плюс проверьте логику чтения данных, поработайте под отладчиком (или добавьте вывод сообщений в лог) и убедитесь в том, что приложение вызывает метод чтения периодически, а не только при старте.
    Ответ написан
  • Как исправить Null id generated?

    @bobzer
    Java EE Developer
    У Вас нигде не определена стратегия генерации первичных ключей, ни на уровне СУБД, ни на уровне вашей бизнес-логики. Для генерации ID могу порекомендовать использовать имеющиеся в MySQL средства. Для этого определите поле первичного ключа в БД как:
    ID BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT
    а в маппингах сущностей укажите следующее:
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    protected Long id;

    При таком подходе значение первичного ключа создаётся непосредственно в момент вставки записи в таблицу. При выполнении команды INSERT значение поля ID не указывается, а MySQL сам подставит значение, применив для его генерации сиквенс (специфический объект СУБД, последовательно генерирующий числа, обычно начиная с 1).

    Настоятельно не рекомендую использовать примитивы для определения ID в сущности (private int id в Note), т.к. при создании нового экземпляра класса будет присвоено значение по умолчанию 0, что приводит к заблуждению - первичный ключ еще не генерировался, но у него уже есть какое-то значение.

    Обычно, класс сущности, описывающий идентификатор, выносится в родительский класс, а все сущности проекта наследуются от него, за счёт чего возникает возможность не описывать поле ID повторно во всех наследниках, избавляясь от дублирования кода. Но, сначала добейтесь работоспособности текущей структуры, а затем занимайтесь рефакторингом иерархии классов. Вот пример определения родительского класса:
    import javax.persistence.*;
    import java.io.Serializable;
    
    @MappedSuperclass
    public abstract class Identifier implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "ID")
        protected Long id;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            Identifier that = (Identifier) o;
    
            if (getId() != null) {
                return getId().equals(that.getId());
            } else {
                return super.equals(o);
            }
        }
    
        public int hashCode() {
            return getId() != null ? getId().hashCode() : super.hashCode();
        }
    }


    UPD
    Маппинги сущностей БД обычно определяются с помощью аннотаций JPA, следующим образом:
    import javax.persistence.*;
    
    @Entity()
    @Table(name = "note")
    public class Note extends Identifier {
    //fields, getters, setters
    }
    Ответ написан
  • Как сохранить сложную сущность в JPA?

    @bobzer
    Java EE Developer
    Есть "топорный" способ: перед сохранением создайте новые экземпляры "Room, Classifier, Profile, Event", подставьте в них "эти самые айдишники", затем подставьте экземпляры в Agenda.

    Хотите изящности - пересмотрите архитектуру. Как вариант, мапить в Agenda не классы, а простые поля - идентификаторы. Обмениваться между клиентом и сервером такими легковесными сущностями, а уже на клиенте подставлять экземпляры, доставая их из отдельных хранилищ. Таким образом работает, например, ExtJs, предоставляя удобные функции для "разворачивания" ID-шников в экземпляры при отображении пользователю и "сворачивая" обратно при передаче на сервер.
    Ответ написан
  • Hibernate cache lvl2 vs Redis?

    @bobzer
    Java EE Developer
    Во многих случаях, ответом на вопрос "как лучше кешировать" является "никак". У вас есть на руках конкретные данные о нагрузочном тестировании вашего приложения, в которых четко видно какая из частей Системы какие ресурсы потребляет? Если нет - то погуглите фразу "преждевременная оптимизация".

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

    Т.к. конкретный вопрос задан, то всё же отвечу. Любая более-менее развитая СУБД прекрасно умеет кешировать всё, что нужно и в общем случае справляется с этим лучше любых кешей. Если четко видите, что уже не справляется - оптимизируйте СУБД. Кеш Hibernate вряд ли даст ощутимый прирост, опять же в общем случае (количество информации в вопросе позволяет обсуждать только общий случай).

    Любые программные кеши требуют собственно программирования. Если вы точно знаете какие сущности надо кешировать и в каком режиме, и при этом типов немного, то возможно, проще всего будет просто создать некий Map прямо в вашем коде и всю работу с ним реализовать самому. Сторонние продукты есть смысл подключать, если логика кеширования и чистки кеша сложная, да ещё, например, требуются распределённый кеш. Тогда да - смотрите отдельные продукты.
    Ответ написан
  • Как правильно сделать полную копию объекта, со всеми потомками в hibernate?

    @bobzer
    Java EE Developer
    Точного ответа не дам, но есть что сказать по этому поводу:
    1. Проблема с Proxy обычно возникает на дочерних коллекциях, особенно когда они помечены как Lazy. Hibernate подставляет в коллекцию свой прокси-объект и при обращении к коллекции осуществляет чтение из БД.
    2. Судя по всему, Hibernate.initialize(entity) в вашем случае не делает ровным счётом ничего. Этот метод следует применять не к самой сущности, а ко всем Lazy-коллекциям сущности, по отдельности к каждой. Если сущность не является detached (не "оторвана" от сессии, в которой её считывали из БД), то Hibernate.initialize и вовсе не требуется, прокси-объекты коллекций (из п.п. 1) сами считают всё из БД при обращении к списку в коде.
    3. Ваш хак - первое что пришло мне в голову еще в начале чтения вопроса (если EntryKey - это первичный ключ сущности). Как только сущность "теряет" первичный ключ, она становится кандидатом на insert, если же первичный ключ есть - тогда Hibernate сделает update. Если аккуратно "обыграть" такие манипуляции с учетом этого правила и на дочерних объектах, то почему бы и нет?
    4. Вопросы клонирования объектов - это отдельная большая и непростая тема, даже без привязки её к сущностям БД (что ещё больше эту проблему усугубляет). Два основных варианта осуществления клонирования: 1 - использовать специфические библиотеки; 2 - писать всё руками (100500 set(get())-ов). Самый надежный и производительный вариант - 2, но он порождает много лишнего кода, который еще и поддерживать надо. Тем не менее, такой подход "живее всех живых", особенно если кол-во сущностей для клонирования можно пересчитать по пальцам.
    Ответ написан
  • Как сделать связь между сотрудниками и заказами?

    @bobzer
    Java EE Developer
    1. Как сделать подобную связь писал тут: Hibernate many to many с дополнительными атрибутами?

    2.
    как гарантировать наличие связи
    Hibernate - это ORM, не меньше, но и не больше. Он дает удобный объектный доступ к данным в БД. Но Hibernate не обязан разбираться в вашей бизнес-логике. Ну уровне реляционной БД нельзя гарантировать наличие связи many-to-many, логично, что и на уровне Hibernate этого тоже сделать нельзя. Соответственно, ответ на ваш вопрос - гарантировать наличие связи должна ваша бизнес-логика.
    Ответ написан
  • Как работать с транзакциями в hibernate?

    @bobzer
    Java EE Developer
    Вы смешали все в кучу и сами себя запутали. Во-первых, есть Container-Managed Transactions (CMT) и Bean-Managed Transactions (см. гугл). Зачастую, всё, что можно указывать аннотациями (такими, как @Transactional) относится к CMT. При работе с CMT в вашем приложении нет кода работы с сессиями и транзакциями, всё это неявно делает контейнер (например, сервер приложений). Раз у вас есть этот код, значит у вас Bean-Managed Transactions и аннотации просто игнорируются.

    Касательно lazy - вопрос вообще отдельный. Обычно проблемы с lazy инициализацией возникают при чтении данных сущности, а не при ее сохранении. Так что приведенный код и "попытке вытянуть lazy data из объекта - получаю ошибки" друг к другу отношения не имеют.

    В общем и целом, если хотите нормально разобраться, прервите кодинг и займитесь изучением документации.
    Ответ написан
  • Лучшая практика DAO в Hibernate: почему всегда нужно писать как можно меньше запросов(query)?

    @bobzer
    Java EE Developer
    Столько всего можно сказать, что аж пальцы разбегаются.

    Во-первых, классика - не занимайтесь преждевременной оптимизацией. Вы ведь сделали замеры расхода ресурсов, перед тем как оптимизировать? Этот запрос хотя бы пару раз в секунду выполняется, не реже, нет? Вы ведь точно уверены, что именно это место "жрет" не менее 20% времени от общего времени обработки процесса? И вы ведь в курсе о том, что любые программные кеши - ничто в сравнении с тем, как умеет кешировать любая "приличная" СУБД?

    Во-вторых - построение HQL-запросов. Насколько мне известно, для One-To-Many нет разницы между сущностью и её ID. Вы замапили сущность, а не её ID, получив возможность доступа к объекту целиком при вычитывании из БД. Но это вам не мешает использовать просто ID при указании критериев выборки. Вам вовсе не нужно читать сущность из БД прежде чем передать ее в HQL-запрос, т.к. Hibernate-у не нужна сущность целиком, он всё равно "возьмет" лишь ID. Т.е., у вас есть ID, но в маппинге указана сущность - в HQL можно передать эмуляцию - создать new Proveder(), указать ему ID и передать в HQL объект Proveder без чтения его из БД.
    Ответ написан
  • Hibernate many to many с дополнительными атрибутами?

    @bobzer
    Java EE Developer
    Думаю, что вам нужен one-to-many на самом деле, т.е. в сущности User хранится список сущностей Achievement. При этом сущность Achievement имеет поля, описывающие конкретное достижение конкретного пользователя (дата получения и другие параметры), а также ссылку на тип достижения (следующий уровень one-to-many). По поводу Set/List - как объявите в классе так и будет, но для сортировки требуется указать order-by в .hbm.xml.

    Примерно так:
    public class User {
        private List<Achievement> achievements;
        get...
        set...
    }
    
    public class Achievement {
        private User user;
        private Date createDate;
        private DicAchievementType type;//справочник типов достижений
        get...
        set...
    }


    User.hbm.xml
    <bag name="achievements" inverse="true" cascade="all,delete-orphan" order-by="CREATE_DATE">
        <key column="user_id"/>
        <one-to-many class="Achievement"/>
    </bag>


    Achievement.hbm.xml
    <many-to-one name="user" column="user_id" class="User"/>
    <many-to-one name="type" column="achievement_type_id" class="DicAchievementType"/>
    Ответ написан
  • Как составить Full-Text HQL-запрос, содержащий SQL contains?

    @bobzer
    Java EE Developer
    Hibernate прекрасно работает с SQL, сделайте так:
    Query query = session.createSQLQuery("select * from test_docs where Contains(xxtext,'Boaz')")
    .addEntity(TestDoc.class);

    Если работаете через EntityManager, то там есть createNativeQuery()
    Ответ написан
  • Hibernate игнорирует настройки и работает с другой базой. В чём может быть проблема?

    @bobzer
    Java EE Developer
    Независимо от того, какая выбрана система сборки, все они компилируют классы в какую-либо отдельную папку, и обычно в нее же сохраняют дополнительные ресурсы, такие, как hibernate.cfg.xml. Самый простой путь - сделать поиск по файловой системе файла hibernate.cfg.xml и посмотреть его содержимое везде, где он найден. Думаю, что найдется версия с URL-ом "/test"...
    Ответ написан
  • Можно ли описать связь один ко многим c возможностью изменять поле с внешним ключом?

    @bobzer
    Java EE Developer
    Несмотря на то, что на объектном уровне Hibernate требует объект Group, на самом деле ему для корректного сохранения в БД нужен лишь group_id. Можно создать пустой/фейковый (не считанный из БД) экземпляр Group и указать ему нужный вам group_id. После этого его можно смело передавать в user.setGroup(group); Если количество групп в Системе ограничено и все они известны заранее (подтекст вопроса это подразумевает), то можно хранить как константы экземпляры Group с заполненным полем ID.
    Ответ написан
  • JPA и Hibernate или Hibernate?

    @bobzer
    Java EE Developer
    JPA - это интерфейс языка JAVA, не имеющий реализации в самом языке. Если просто добавить аннотации JPA в свои классы, то не произойдет ровным счетом ничего. Для того, чтобы аннотации начали "работать", следует развернуть и настроить в проекте фреймворк, который "найдет" все классы с JPA-аннотациями и "состыкует" их с сущностями СУБД. Этим фреймворком у вас является Hibernate.

    По поводу "работать с hibernate можно и через JPA или отдельно от него" - тут в основном дело вкуса. У вас есть два варианта сообщить Hibernate какие классы "замапить" на таблицы БД:
    1. Добавить в классы аннотации JPA
    2. Создать XML-файлы, в которых будет описано сопоставление классов объектам БД.

    По доступной функциональности оба метода равны между собой, при этом аннотации JPA более современны. Я бы рекомендовал JPA, т.к. при открытии класса сразу будет видно, что он "замаплен", а в случае использования XML, "замапленный" класс визуально неотличим от других классов.
    Ответ написан
  • Hibernate, OneToMany Каскадной добавление/удаление детей

    @bobzer
    Java EE Developer
    Похоже на то, что ошибка возникает из-за лишних манипуляций с сущностями. Посмотрите под отладкой содержимое сессии, в ней собирается список всех объектов, подлежащих сохранению при вызове flush/commit. Скорее всего, кроме явно видимой в коде сущности user, в сессии в какой-то момент (может, userDAO.findUserItem ?) появляется ещё один экземпляр User, например в UserItemId. Вам надо добиться того, чтобы все взаимосвязи ссылались на один и тот же экземпляр объекта. Для этого попытайтесь выполнять все операции в одной сессии, т.е., если в потоке выполнения кода уже открыта одна сессия, не открывать других сессий (в userDAO.findUserItem ?) пока не будет закрыта текущая сессия.
    Ответ написан