• Почему хибернейт не апдейтит таблицу?

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

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

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

    @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.
    Ответ написан
  • Как задеплоить приложение?

    @bobzer
    Java EE Developer
    Скачайте, разверните и запустите, например, Wildfly - это Opensource версия JBoss. Убедитесь, что после старта в логах нет ошибок, т.е. "пустой" сервер запустился корректно. Затем в папке /standalone/deployments создайте папку my.war. В папке my.war создайте текстовый файл index.html, в нём напишите строку "Hello, world". Последним шагом в папке /standalone/deployments создайте пустой текстовый файл с именем my.war.dodeploy. Убедитесь, что спустя несколько секунд в логах появилась запись Deployed "my.war". Зайдите по адресу localhost/my
    Ответ написан
  • Как правильно задеплоить проект 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'.
    Ответ написан
    Комментировать
  • Как избавится от дубля path JSTL на страницe jsp?

    @bobzer
    Java EE Developer
    <td>
        <sf:input path="login" id="login_id" class="form-control input-sm" readonly="${affiliateProgramConstantInfo.login != null}"/>
        <div class="has-error">
            <sf:errors path="login" class="help-inline"/>
        </div>
    </td>
    Ответ написан
    1 комментарий
  • Откуда ошибка "Column 'id' cannot be null" в Hibernate?

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

    Проверьте все связи @OneToMany.
    Ответ написан
  • Как деплоются приложения Java EE?

    @bobzer
    Java EE Developer
    нужно обновить частично, только один модуль
    Соответственно, этот модуль должен быть самостоятельным артефактом Maven. Обычно, артефакт для EAR описывает формирование приложения из других модулей и конфигурационных файлов. Следует разбить приложение на артефакты (обычно jar/war), которые входят в состав EAR.

    Вообще, при сборке для промышленной среды надежнее собирать всё приложение (EAR в вашем случае) целиком. А при сборке для окружения разработчика вообще удобнее использовать не сборщик Maven, а среду разработки. Idea, например, прекрасно понимает формат конфигураций Maven и на их основе может создавать свои артефакты (и настоятельно порекомендует это сделать при обнаружении pom.xml). Если собирать артефакты с помощью Idea, то даже при сборке EAR целиком фактически будут обновляться только изменённые файлы, что ускоряет процесс сборки на порядки, в сравнении с Maven. Это достигается за счёт того, что у Idea есть свой контроль версий и она знает что именно следует пересобрать, а что можно оставить как есть. Наибольший выигрыш получается для exploded артефактов (которые не архивируются, а выкладываются в виде структуры папок с файлами), т.к. в таком случае не приходится перепаковывать модули целиком при изменении небольшого количества файлов в них.

    Для каждого артефакта Maven Idea создаст две версии своих артефактов - упакованную и не упакованную (exploded). В случае, если ваше приложение состоит из нескольких модулей, может потребоваться ручное вмешательство в сгенерированные артефакты, т.к. exploded-артефакт верхнего уровня в конфигурации по-умолчанию содержит подчинённые артефакты в упакованном виде. Потребуется удалить вариант в упакованном виде и на его место вставить exploded-вариант. Минус состоит в том, что при изменении артефакта Maven, Idea предложит перегенерировать и свои артефакты, все ручные настройки при этом пропадут. Но настройки топологии проекта меняются нечасто, поэтому лишней минутой работы по ручной реконфигурации обычно можно пренебречь...

    Если Вы только начали экспериментировать с Java EE, то создание EAR может быть неоправданным усложнением, т.к. во многих случаях веб-артефакт более низкого уровня - WAR - прекрасно справляется со всеми задачами Enterprise-приложения. При этом получается более простая структура, соответственно и с настройкой сборки проблем меньше.
    Ответ написан
    4 комментария
  • Как работает блокировка у Singleton на кластере?

    @bobzer
    Java EE Developer
    Как решается проблема, когда нужно иметь Singleton на нескольких серверах?

    1. Блокировка записи в БД. Если бизнес логика вносит какие-то изменения в БД, то перед этим осуществляйте чтение записи с блокировкой, SELECT ... FOR UPDATE (для JPA LockMode/LockModeType и прочее, в зависимости от реализации и версии). При этом, получаем актуальное состояние записи одновременно запрещая её изменять другим потокам (и другим узлам кластера) до тех пор, пока не завершится транзакция обработки записи. Сразу после чтения с блокировкой проверяем, подлежит ли она обработке или нет, на случай, если пока получали блокировку другой поток уже обработал запись. Обычно, сама запись содержит поле, сообщающее о том, следует ли её обрабатывать, например - некий статус. Если статус говорит о том, что запись уже обработана, то обработку не выполняем, завершаем транзакцию, переходим к следующей записи. Если не обработана - соответственно обрабатываем. Пока идёт обработка текущим потоком, другие потоки (узлы) останавливаются на моменте чтения с блокировкой, это поведение обеспечивает СУБД. После завершения обработки, смены статуса записи и подтверждения транзакции, запись высвобождается, и если другой поток пытался её обработать, то СУБД разрешает ему считать запись с блокировкой. Поток проверяет статус, "видит" что она уже обработана и не обрабатывает её. Плюсы: универсальность, независимость от контейнера, практически "непробиваемый". Минус: вероятно возникновение излишней нагрузки, в самом неприятном варианте - всю работу делает один узел, остальные потребляя те же мощности не делают ничего полезного. Если записей менее 100 тысяч в сутки, а узлов менее 5-10, то скорее всего, это не будет большой проблемой.
    2. Для Wildfly начиная с версии 10 есть альтернативный вариант - Singleton deployments. Этот сервис существовал в JBoss 5/6, но был "выпилен" в JBoss 7 (6 EAP) в угоду соответствия стандартам Java EE. Раньше работало так: вы разворачиваете свои кластерные синглтоны в отдельное приложение, и "ложите" в отдельную папку развертывания. Отличие Wildfy в том, что вместо отдельной папки, требуется добавить в приложение специфический дескриптор развёртывания. JBoss/Wildfly гарантирует, что все такие приложения будут запускаться только в одном экземпляре на весь кластер. Плюс: нет лишней нагрузки. Минус: надёжность под сомнением, например, "сломался" протокол UDP, все узлы потеряли друг друга и решили стать главными и развернули ваш синглтон. Вообще, во всём что касается атомарности, лучше СУБД (ИМХО) ещё ничего не придумано.
    3. Реализуете вариант 1 и разворачиваете его как вариант 2. При таком раскладе избавляетесь от минусов обоих подходов, получая все их плюсы.


    PS Касательно варианта 1. Если работа синглтонов не связана с обработкой записей БД, их всё равно можно синхронизировать таким же образом. Создаёте в любой таблице (например, в таблице каких-нибудь системных настроек) запись и синхронизируете синглтоны на ней.
    Ответ написан
    Комментировать
  • Почему 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
    }
    Ответ написан
  • RMI или JSF чем будет правильнее?

    @bobzer
    Java EE Developer
    технология RMI выдержит?
    практически любая технология выдержит, если выдержат Ваши аппаратные ресурсы. В основном это сеть и дисковая подсистема, но может быть и нагрузка на ОЗУ сервера приложений, т.к. многие технологии прежде чем сохранить файл на диск, загружают его байтовое представление в ОЗУ сервера. Насчёт "~1000 человек" - эта цифра ни о чём не говорит, в данном случае следует считать количество и размер загружаемых/скачиваемых файлов а не абстрактных пользователей.

    Как правильно реализовать?
    Если Вы используете спецификацию JSF для построения клиентской части, то логично использовать средства загрузки файлов, предоставляемые библиотеками/фреймворками, реализующими эту спецификацию - практически все они имеют компоненты загрузки файлов на сервер. Я рекомендую использовать PrimeFaces - это активно развивающийся, богатый компонентами фреймворк. У них есть несколько вариантов компонента загрузки файла(-ов) на сервер, начать можно с этого

    Касательно RMI - он здесь вообще не к месту. JSF работает в браузере, используя протокол HTTP, поэтому и для загрузки файлов логично использовать тот же протокол.
    Ответ написан
    Комментировать
  • Как сохранить сложную сущность в JPA?

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

    Хотите изящности - пересмотрите архитектуру. Как вариант, мапить в Agenda не классы, а простые поля - идентификаторы. Обмениваться между клиентом и сервером такими легковесными сущностями, а уже на клиенте подставлять экземпляры, доставая их из отдельных хранилищ. Таким образом работает, например, ExtJs, предоставляя удобные функции для "разворачивания" ID-шников в экземпляры при отображении пользователю и "сворачивая" обратно при передаче на сервер.
    Ответ написан
    Комментировать
  • Java. Отправка письма через gmail host c аутентификацией proxy по логину и паролю?

    @bobzer
    Java EE Developer
    Я бы посоветовал для начала добиться работоспособности кода в окружении, в котором нет прокси сервера, т.к. не ясно с кем "бороться" - с почтовым сервером или с прокси. Недавно "бился" с Gmail, вот мои работающие настройки:
    mail.smtp.host              smtp.gmail.com
    mail.smtp.socketFactory.port	 465
    mail.smtp.socketFactory.class	 javax.net.ssl.SSLSocketFactory
    mail.smtp.auth                         true
    mail.smtp.port                         465
    mail.sender.email                    mymail@gmail.com
    mail.sender.email.password    mypassword
    mail.smtp.ssl.enable                true
    mail.smtp.ssl.trust                   *
    Ответ написан
    Комментировать
  • Как свинтить вместе JAX-WS веб-сервис и @XmlAnyElement?

    @bobzer
    Java EE Developer
    Обычно, сервера приложений уже содержат библиотеки для поддержки веб-сервисов. Вероятно, ваше приложение также содержит какие-то из таких библиотек, или зависимых.

    Попробуйте развернуть "чистый" сервер приложений, с нуля. Затем разверните ваше приложение, но удалите из него все библиотеки, а лучше вообще оставить только класс веб-сервиса и web.xml. Если в такой конфигурации веб-сервис развернётся, то поштучно добавляйте нужные библиотеки (зависимости maven), пересобирайте и деплойте заново, до тех пор, пока ошибка не повторится. Так поймёте какая именно библиотека конфликтует. Вероятно, её надо будет удалить из зависимостей, либо указать как Provided.
    Ответ написан
  • Hibernate cache lvl2 vs Redis?

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

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

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

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

    @bobzer
    Java EE Developer
    Что такое Java Web Start:
    • Позволяет активировать приложения одним щелчком мыши
    • Обеспечивает использование новейших версий приложений
    • Устраняет необходимость проведения сложных процедур установки и обновления
    Ответ написан
    Комментировать
  • Можно ли с помощью Apache Lucene определить, входит ли какая-то строка из набора в текст?

    @bobzer
    Java EE Developer
    Вот работающий пример поиска по неточному вхождению:
    public static void main(String[] args) throws Exception {
            String fieldName = "myField";
    
            //создание тестового индекса
            Directory directory = new RAMDirectory();//в "настоящей" Системе здесь должно быть FSDirectory.open(dir)
            RussianAnalyzer analyzer = new RussianAnalyzer(Version.LUCENE_46);
            IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_46, analyzer);
            IndexWriter writer = new IndexWriter(directory, config);
            writer.addDocument(createDocument(fieldName, "Я живу у мамы"));
            writer.addDocument(createDocument(fieldName, "В доме было холодно"));
            writer.commit();
            writer.close();
    
            //поиск
            int startFrom = 0;
            int pageSize = 20;
            DirectoryReader ireader = DirectoryReader.open(directory);
            IndexSearcher indexSearcher = new IndexSearcher(ireader);
            //FuzzyQuery осуществляет поиск неточных вхождений
            FuzzyQuery wildcardQuery = new FuzzyQuery(new Term(fieldName, "мама"));
            TopDocs topDocs = indexSearcher.search(wildcardQuery, startFrom + pageSize);
            ScoreDoc[] hits = topDocs.scoreDocs;
            for (int i = startFrom; i < topDocs.totalHits; i++) {
                if (i > (startFrom + pageSize) - 1) {
                    break;
                }
                Document hitDoc = indexSearcher.doc(hits[i].doc);
                if (hitDoc != null) {
                    System.out.println(hitDoc.get(fieldName));
                }
            }
        }

    Часть исходников взята с серьёзной промышленной Системы, так что если что-то кажется странным, не думайте, просто используйте. Когда всё заработает "как часы", тогда вернётесь к "странностям" и поразмыслите, стоит ли переделывать...
    Ответ написан
    Комментировать
  • Как правильно сделать полную копию объекта, со всеми потомками в 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, но он порождает много лишнего кода, который еще и поддерживать надо. Тем не менее, такой подход "живее всех живых", особенно если кол-во сущностей для клонирования можно пересчитать по пальцам.
    Ответ написан
    7 комментариев