Сучасна диджитал-освіта для дітей — безоплатне заняття в GoITeens ×
Mazda CX 5
×
Java Developer в Rakuten Kobo
  • ORM Сначала классы, потом таблицы

    Если написать

    @Entity
    @Table(name = "orders")
    public class OrderEntity {
       @Id
       private Long id;
       // ...
       private Long clientId;
    }
    

    ...нужно вызывать потом метод по получению Client-а по его ID.

  • Как использовать Hibernate: основные проблемы и их решения

    Итоги падведьом ©

    Да, мы вроде уже возле финиша.

    Люблю Optional, не люблю isPresent(), написал бы через orElse.
    С другой стороны, вижу if — пишу тест, а orElse в глаза бросается только JaCoCo.

    Map снимает этот вопрос?
    Никоим образом.

    Извините, поспешил

    class Client {
      ... // /previous code
       @LazyCollection(LazyCollectionOption.EXTRA)
       private Map<Client, Friendship> friends;
    }
    
    В попытке держать метаданные дружбы клиентов и сохранить свою модель вы создали структуру, в которой клиент А содержит мапу из клиентов и объектов, у которых тоже по 2 клиента, причем в каждой паре 1 из клиентов — тот же А? У вас 4-уровневая (ой-вей!) структура из клиентов.
    Вам не кажется эта структура офигеть каким искусственным, громоздким, излишним, error-prone и вообще неудачным решением

    Неа, Map кажется мне простым и понятным решением, не зависящим от JPA и сервисов.

    Но спасибо, что подняли эту тему, допишу абзац про *-to-many.

    С одной стороны, я понимаю, что любое ТЗ вы можете решить в рамках своего подхода и с помощью сервисов, дробления графа и jpql не проиграть в N+1.

    С другой стороны, может правы и те, кто к вашим strict rules дополнительно запрещает *-to-many вообще, потому что сегодня там три записи, а завтра три миллиона, а код уже задеплоен.

    Правильно понимаю, что отличие наших подходов в том, что я свободно отношусь к модели и играю DTORRO-шками, а вы достигаете того же вводя правила и платя строками кода?

  • ORM Сначала классы, потом таблицы

    Насколько втупую? Как в разделе «Обычный подход»? Потом с ними очень неудобно работать, в сервисах начинается код по доставанию данных из разных таблиц вперемешку с бизнес-логикой.

  • ORM Сначала классы, потом таблицы

    Вы знаете? Поделитесь рецептом, пожалуйста.

    Иначе как мне догадаться, что это простецкая схема создалась из таких классов dou.ua/...​les/how-to-use-hibernate — раздел «Правильные entities». И ладно аннотации, а там у автора ещё бизнес-методы в них.

  • ORM Сначала классы, потом таблицы

    Как угадать java-классы для, например, s.dou.ua/...​-files/image1_Fh1bISl.png ?

  • ORM Сначала классы, потом таблицы

    Нет, но извините. И вопрос тот же: как в случае Hibernate угадывают джава классы по таблицам? И насколько хорошо?

  • ORM Сначала классы, потом таблицы

    Мне кажется, что если делать перевод классы Java -> структура БД -> классы Java, то результат будет не очень хорош, как с любым дизассемблированием. Поэтому интересуюсь, как R -> O делают.

  • ORM Сначала классы, потом таблицы

    У меня есть небольшой опыт работы с Hibernate (см. статью). Есть мнения, что он тормозит. Поэтому спрашиваю, как его используют, особенно в случае, когда БД уже существует.

  • Как использовать Hibernate: основные проблемы и их решения

    А у меня не ООП? Смелое заявление, весьма смелое :)

    Убедите в обратном, пожалуйста. У меня возникло ощущение, что вся логика у вас в сервисах, entity — POJO. Как у вас будет выглядеть метод FriendshipService.establishBetween? Тест на него? Почему вы считаете, что изменение класса Client для задачи «добавить друзей» — это

    залезли пальцами прямо в модель и увеличили ее сложность

    ?

    Дайте мне информацию о конкретно этой связи.

    Извольте

    class Client {
      ... // /previous code
       private Map<Client, Friendship> friends;
    }
    
    когда и при каких обстоятельствах мы с вами познакомились.

    На ДОУ, в комментариях :-)

    В пунктах 1 вы можете выбрать 5000 записей (а вдруг вы блоггер)

    Конечно, с отношениями *-к-многим нужно быть аккуратным. Замечание Влада, что @OneToMany следовало бы назвать @OneToFew справедливо.

    Map снимает этот вопрос?

    Суть моей операции
    1) СЕЛЕКТ ФРОМ дружбы ВЕРЕ друг1 = Андрей И друг2 = Дмитрий
    2) Если резалт сет не пустой, создать 1 объект

    Но делает это сервис. С помощью таблицы. Могут ли Андрей и Дмитрий быть друзьями без них?

  • Как использовать Hibernate: основные проблемы и их решения

    Поскольку внешние запросы, скажем веб, каждый в своей сессии, то нет, не шарится.

  • Как использовать Hibernate: основные проблемы и их решения

    в вашей архитектуре есть entity-businessDTO-transportDTO. С таким вариантом я могу согласиться

    Ок, с этим разобрались. Я всю ветку выше именно это говорил. Спасибо, что помогли четче сформулировать.

    мы избегаем бидирекшинал связей, поэтому у нас нет циклов в сущностях.... Причина — мы приняли строгую иерархичность сущностей...имеем структуру зависимостей в сервисах

    А то я такого не видел никогда :-)

    Обратите внимание, это, имхо, охренительно важно — моя реализация ТЗ не затронула а) ни класс Client б) ни класс ClientService. А что у вас? Вы залезли пальцами прямо в модель и увеличили ее сложность.

    Конечно, это охренительно важно. Я топлю за ООП, весь код, который я привёл — это объекты. Не POJO, у них нет сеттеров (Order.setStatus и Order.setExpress — это вполне валидные бизнес-мутаторы). В Order.removeItem есть защита от ухода в отрицительные значения и написан тест на этот метод. Я не ставлю никаких ограничений на иерархию и связи: надо bi-directional (в объектном, не БД смысле) — сейчас в Client добавим Set<Order>.

    Всё это добро свободно работает без JPA. А JPA я воспринимаю именно как Persistance реально существующих объектов. Да, чтобы Money сохранить — надо конвертер написатьвсе уже в jadira сделали. И вообще @Type — это не JPA, а Hibernate-specific. Но я реальный класс сохраняю, а не модель.

    Думаю, мы вполне можем с Дмитрием Думанским сработаться: домен у него, утверждалось, есть. Накидаем анноташекНапишем orm.xml и ACID будет вместо файловой системы.

    Не возвражаете, если ваши сервисы мы назовём модулями с процедурами, а модель — структурами?

    Мои аргументы в пользу ООП.

    Сервис у меня выглядит так

    class ClientService {
        ClientDao clientDao;
    
        @Transactional
        void addFriend(Long clientId, Long friendId) {
            Client client = clientDao.getBy(clientId);
            Client friend = clientDao.getBy(friendId);
            client.addFriend(friend);
        }
    }
    

    Unit-тест на такой метод я писать не буду: просто перепроверять вызовы — это не black-box тестирование.

    А вот на Client.addFriend, наоборот, обязательно AAA-тест напишу: убедиться, что StackOverflowException не поймал, и дружба взаимная получается.

    public class ClientTest {
        @Test
        public void addFriend() {
            // arrange
            Client andriy = new Client("Andriy", "Slobodyanyk");
            Client dmitry = new Client("Dmitry", "Bugay");
    
            // act
            andriy.addFriend(dmitry);
    
            // assert
            assertThat(andriy.getFriends(), hasItem(dmitry));
            assertThat(dmitry.getFriends(), hasItem(andriy));
        }
    }
    
  • Как использовать Hibernate: основные проблемы и их решения

    Хибернейт тут роли не играет.

    Я переформулирую, расклад такой. Меняется и удаляется вообще всё, как расписал Dmtry Bugay. Поэтому аудит — это незатейливая табличка из трёх столбцов: кто-когда-что сделал (в текстовом виде). Никаких референсов на другие таблицы у неё нет.

    Бизнес-логика в таком стиле

    <blockquote>public List<Item> getItems() {
       return items;
    }
    ... 
    
    doSomeFancyStuffWithLotsOfStreamingAndMaybeBitOfReactiveProgrammingAndSneakilyModifyCollectionInASubtleWay(myOrder.getItems());
    

    Лично я предпочитаю бизнес-мутаторы, но Хибернейту, по большому счёту, всё равно, кто и как объект поменял.

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

    А теперь делаем аудит. И с EventListener-ами это как раз не проблема. Есть предыдущий state и текущий. Смотрим разницу и пишем в audit table.

    явными репозиториями задача решается легко добавлением сравнения

    Так а что вообще в CRUD задачах сложного? C хибернейтом просто кода меньше писать, а так всё то же самое.

  • Как использовать Hibernate: основные проблемы и их решения

    Оффтопик: Прими восхищение телеграмм-каналом вообще и последним постом в частности!

    Підтримав: Włodzimierz Rożkow
  • Как использовать Hibernate: основные проблемы и их решения

    @Type c несколькими полями (Money в статье) — это Hibernate-specific feature. В JPA только AttributeConverter в одно поле. Отзовитесь, кто ещё работал с EclipseLink?

  • Как использовать Hibernate: основные проблемы и их решения

    Сначала хочу поблагодарить вас за проявленное терпение, что не прерываете дискуссию и иллюстрируете её кодом. Благодаря вашим замечания я чётче переформулирую абзац «Entity и RRO». А теперь к делу.

    Полностью согласен с pastebin.com/uCAuvESZ. Также согласен с тем, что либо энтити связаны, либо нет и приемлемо их доставать в разных транзакциях.

    @Request
    JsonModel getBy(id) {
    Model model = modelService.getBy(id, FULL);
    SomeQty qty = qtyService.get(..., FULL); // 2.1
    ModelJson json = mapper.toJson(model, qty); // 2.2
    return json;
    }

    Поговорим о вашем решении с флажком DataIntegrity. Это вы придумали? Я нигде не встречал такого подхода. Его плюс — академическая правильность о разделении слоёв. Недостатки, на которые вы закрываете глаза — это необходимость в интенсивном написании jpql, Hibernate.initialize(), лишние N+1, потенциальные LLE, если в контроллере будет вызов modelService.getBy(id, PARTIAL) и накладывание ограничений на доменную модель, о которых ниже.

    Вы это почувствовали, когда я предложил извлечь только Orders+Clients(без Items) на примере из статьи, и стали менять правила, дескать, в жизни всё сложнее, и два графа из трёх не бывает.

    По уму, надо было сразу спросить, а как же циклические зависимости, когда вы первый раз упомянули Hibernate.initialize(), и мы бы сэкономили много времени.

    Попробую угадать ответ. У вас их нет, потому что домен правильно спроектирован. Ок.

    Теперь моё решение. Можете пропустить, и переходить сразу к ТЗ, если

    . ваш фасад меня не интересует, он мне не нужен, поскольку мне не нужны ваши маппинги и ваши дто.

    1. У меня совершенно классические сервисы, возвращающие энтити, без флажка integrity.
    2. Дальше я говорю контроллерам — ребята, раньше вы обращались к сервису за энтити, а теперь новый договор — обращайтесь к фасаду.
    3. Есть @Transactional-фасад, содержащий сервис(ы), перемапливающий их и возвращающий Raw Result Object (RRO) — чистые данные для контроллера.
    4. Они raw — никакой привязки к DTO, Json, JMS мессадж и JMS транзакции — это просто классы с полями без единой аннотации и зависимости.
    5. Контроллер маппит RRO в DTO.
    6. Сервисы от контроллера полностью скрыты.

    Очевидный недостаток этот решения

    не вы решаете как организовать сервисы, чтобы они предоставляли данные для сборки нужного формата (вдумайтесь в это предложение, в нем суть),

    ... да, на фасад с RRO оказывается определённое влияние.

    Недостатки:

    1. Контроллер требует от фасада, какие данные ему предоставить. Некоторое логическое нарушение однонаправленности слоёв.
    Это осознанный вынужденный компромисс для случаев, когда контроллер не является неизвестной третьей стороной, а разрабатывается тут же в приложении. Впрочем, для неизвестных третих сторон всегда можно создать RRO идентичные по структуре Entity.

    Преимущества:
    1. Я не накладываю вообще никаких ограничений на доменную модель. Любые ссылки, любые связи, хибернейт всё стерпит, только поменяйте на lazy в many-to-one.
    2. Создавая компактные RRO с только необходимыми полями — я выигрываю в N+1.

    Теперь специальный синтетический пример для сравнения подходов.

    Дополняем ТЗ статьи: у клиента появляются друзья — такие же клиенты.

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

    Теперь короткий пересказ статьи.

    Создаём удобный класс не думая о JPA вообще. Добавляем в Client

    class Client {
        // ... prev code
        private List<Client> friends = new ArrayList<>();
    
        public void addFriend(Client friend) {
            friends.add(friend);
            friend.addFriend(this);
        }
    

    Ой, ловим StackOverflowException, добавляем проверку и всё же естественнее и логичнее перейти на set и задуматься о equals/hashCode.

    Навешиваем аннотации, итого:

    class Client {
        // ... prev code
        @ManyToMany(cascade = CascadeType.PERSIST)
        @JoinTable(
                name = "client_friends",
                joinColumns = @JoinColumn(name = "client_id"),
                inverseJoinColumns = @JoinColumn(name = "friend_id")
        )
        private Set<Client> friends = new HashSet<>();
    
        public Set<Client> getFriends() {
            return Collections.unmodifiableSet(friends);
        }
    
        public void addFriend(Client friend) {
            if (friends.add(friend)) {
                friend.addFriend(this);
            }
        }
    }
    

    Как ни крути, мы не может отдавать эту энтити в контроллер.
    Либо LLE, либо OOO. Представление влияет даже на сигнатуру метода.

    class Controller {
        ClientFacade clientFacade;
        int friendsLevel = 2;
    
        Json getClient (Long clientId) {
          ClientRro rro = clientFacade.getBy(clientId, friendsLevel);
          return mapper.toJson(rro);
        }
    }
    
    class ClientFacade {
        ClientService clientService;
    
        @Transactional
        ClientRro getBy(Long clientId, int friendsLevel) {
          Client client = clientService.getBy(clientId);
          return clientRro; // custom building of ClientRro by client of friends level
        }
    }
    
  • Как использовать Hibernate: основные проблемы и их решения

    не очень понимаю в чем для визуализации Хибернейт выигрывает когда аудит уже сохранен. Он же тоже айдишки будет в _aud хранить, которые ризолвить нужно.

    Не факт, что айдишки, может натурально строки, как в log-файле, foreign key все равно не будет. И не все сразу приходят к идее ставить флаг удаления, некоторые физически записи удаляют.

    Когда нужно отслеживать изменения, то таки да — лучше это делать явно на уровне домена.

    В теории — да. Когда выше речь шла о изменении версии parent-а, я тоже так думал. Но в случае аудита — сразу сдаюсь, не буду я каждый сеттер править.

    Хибернейтовский @Audited — штука полезная, но больше для прикрытия формальных требований аудита, чем бизнес требований

    Так и есть. Из Envers только идею инструмента — eventlistner — можно взять, тюнинг там примитивный и потому недостаточный. А в тех двух аудитах, которые я написал, бизнес был весьма изобретателен, казалось бы в такой самоочевидной задаче.

    немногочисленные изменяемые поля проверяются на изменение еще бизнес-логикой, до персистентности, и трекание их не составляет проблем.

    А теперь давайте возьмём мой пример из статьи: Order-Client-Item-Quanity. И представим, что трекать нужно каждый чих.

    Но это специфика, засчитываю себе аудит как потенциальный плюс Хибернейта, особенно для более CRUD приложений.

    Ага

  • Как использовать Hibernate: основные проблемы и их решения

    От меня было предложение назвать фасад внешним сервисом, вы сказали, что суть не измениться, а теперь это всё таки сервис, ок, это не важно.

    Я там выше правил код немного, но суть та же.
    Итак, у нас есть
    1. SmallBO — носитель аннотаций джексона.
    2. Маппер из entity в smallBo
    3. И строчка

    smallBoMapper.toBo(entity);

    Объясните про зло другими словами, пожалуйста. Я не понял про

    гипотетически позволяет кому-нибудь прибивать гвоздями поля жсона к прямым вызовам базы без разделяющей абстракции.
  • Как использовать Hibernate: основные проблемы и их решения

    Давайте дальше двигаться )
    Вы не согласны с тем, что полностью загруженное энтити

    MyEntity e = service.getBy(id, FULL_INTEGRITY);
    Этот вызов — вне транзакции.

    является своим собственным представлением.
    Поделитесь, пожалуйста, в чём принципиальное отличие? Как вы его используете, кроме сорса для маппера?

    Возможно, плохую роль играет моя узкопрофильность? Я никогда не писал em.detach(), чтобы продолжить работать с бизнес-объектом, ловя LLE. Тогда я бы сам пришёл к сервису с парт и фул-интегрити.

  • Как использовать Hibernate: основные проблемы и их решения

    Жпа не про стейтфул

    Я в том смысле, что stateless mapper-ов много разных есть, а stateful — кроме провайдеров JPA не знаю

  • Как использовать Hibernate: основные проблемы и их решения

    Именно так. Кеш 1-ого уровня называется.

← Сtrl 1... 678910...18 Ctrl →