Корпоративный оракл продали/втюхали. А как вы буде обосновывать необходимость лицензии jOOQ?
ok, ok, you won this round.
А теперь представляем что билд-агенты не имеют доступ к докерхабу и вообще вам не подконтрольны.
А чего тут представлять, они у меня и не имеют доступа к докерхабу, и мне не подконтрольны. Но имеют доступ ко внутреннему докер репозиторию и там лежат базовые имиджи. Нету возможности запускать контейнеры для CI в 2019? Ну, надеюсь вы с них рейтами хорошо за это дерете. Можно локально гонять тогда за такие деньге :)
Потому что нужно написать запросы, добавить все поля в маперы, не забыть как из резалтсета получить нулл значение, для интов/лонгов например. А еще нужно трекать состояние «сессии» чтобы знать какой элемент вложенной коллекции надо инсертить, а какой обновлять/игнорировать. Если у вас делит — это именно делит, а не апдейт, тут тоже надо думать.
Ха, дяденька, так вы это хибернейт заново переписывали? Так лучше уж его натуральный наверное пользовать. Нужно удалить элемент из коллекции? entityRepository.delete(child, parent); — в таком ключе я имею ввиду использование Spring JDBC и подобного. Явно, просто, понятно, и не так сложно как адепты ORM пугают.
Мы по маппером имеем в виду класс, который из сиквеля делает бизнес-объект, да? Даошку грубо говоря.
думаю да. На подобие такого (kotlin):
private val claimableRowMapper: (rs: ResultSet, _: Int) -> Claimable = { rs, _ -> Claimable( id = rs.getLong("id"), version = rs.getInt("version"), warehouseId = rs.getInt("warehouse_id"), supplierId = rs.getLong("supplier_id"), segments = fromPgJson(rs.getString("segments"), segmentListType), mutationTimestamp = rs.getTimestamp("mutation_timestamp").toInstant(), orderType = OrderType.valueOf(rs.getString("order_type")), closureDate = rs.getDate("closure_date")?.toLocalDate() ) } fun findOne(claimableId: Long): Claimable? = jdbcTemplate.query( // OK, we're cheating with *, not good for every context but we're 100% resilient "SELECT * FROM claimables WHERE id = ?", arrayOf(claimableId), claimableRowMapper ).singleOrNull()
сайдэффекты — это хорошо? Ну тогда говорить особо не о чем по этому пункту :)
Если не нравится — не используйте.
не все проекты начинаются с нуля. Те что с нуля — не используем. И опытные девелоперы в моей среде не советуют. Вот и интересуюсь, зачем другие пользуют, что находят.
Повторюсь, мы о сеттерах или бизнес методах-мутаторах?
о вот таком:
order.addItem(item1);
Что при более внимательном рассмотрении действительно не так страшно — отследить изменения легко.
public Map<Item, Integer> getItems() { return Collections.unmodifiableMap(items); }
^^ если бы все пользователи Хибернейт так делали, моя жизнь была бы лучше. К сожалению, в живой природе попадается все чаще такое (кстати не от индусов каких, а от вполне таких наших синьоров с энтерпрайзных галер):
public List<Item> getItems() { return items; } ... doSomeFancyStuffWithLotsOfStreamingAndMaybeBitOfReactiveProgrammingAndSneakilyModifyCollectionInASubtleWay(myOrder.getItems())
что становится сложнее отследить для частоиспользуемых сущностей, особенно если нет явных repository.save(), что и вызвало мою бурную реакцию.
А если ещё и от провайдера JPA абстрагироваться, то даже возможности писать диалект не будет.
Зачем?
Видимо таки придётся во всех мутаторах child-а ручками дописать обновление версии parent-а.
Я бы с удовольствием, только из-за того, что нету явного момента сохранения — это становится немного проблематично. Очень. Кроме того заносить такие детали в каждый доменный объект:
public void setStatus(Status newStatus) { this.status = newStatus; this.increaseMyAndParentVersion(); }
не всегда желательно. И наверное будет не очень приятно если под Лобоком сидите.
А то в EclipseLink лисенеры другие.
тут у меня 100% YAGNI.
Думаю, что понимаю. У меня все ноги в крови от
public List<Item> getItems() { return items; } ... doSomeFancyStuffWithLotsOfStreamingAndMaybeBitOfReactiveProgrammingAndSneakilyModifyCollectionInASubtleWayMaybeCompletelyDifferentThread(myOrder.getItems())
Мое имхо что каскады лучше не использовать вообще.
в смысле каскады на обновление / удаление не использовать? И на хибернейт только для вычитки длинных графов полагаться?
файловую систему можно использовать вместо бд на проде.
ну типа наверное можно если не важно, что данные могут навернутся. Или распределенная файловая система с гарантиями.
Spring JDBC templates? Рылли? Даже в инфраструктуре спринга репозитории на ДжДБС теплейте занимают больше времени на разработку чем с хибером (это по опыту последних проектов).
Да? А че так? У меня пятый год активного использования в разных проектах — полет вполне нормальный. Особенно с переходом на Котлин, так можно даже сказать приятно. Но все с минимальной магией, без стандартных Спринговых репозиториев — простые Jdbc Templates, Insert Templates или как их там зовут.
Сюда же количество багов, особенно когда люди не включат БД в воркфлоу тестов.
Это в любом случае. docker + полноценная база без всяких компромиссов типа H2. Но это к хибернейтному коду тоже относится. Даже еще больше, в нем одними репозиториями точно не обойдешься.
Сюда же надо не забывать о том что существуют джуниоры, которые на ДжДБС теплейте такого трешака могут натворить (чтобы код был типобезопасным и переиспользуемым :) )
Так эти же джуниоры такого же или еще худшего с Хибернейтом вытворят если оставить без присмотру. И хорошо если только с моделью, а то как-то свой Entity Traverser написали — то вообще так и не получилось выковырять.
jOOQ как бы платный. Это шоустопер для энтерпрайзов.
Платный для платных БД вроде? Но понимаю, Оракл корпоративно закупили, а на эту лицинзию уже денег не получить.
№ 3: оказывается все еще есть много проектов где не используют спринг :) А по «альтернативным JPA» СО ответы сложнее найти.
Apache commons db? Использовал до Спринга — вроде тоже терпимо было.
Но в общем Хибернейт — гуано.
Где ж выход?
Что смущает в мутирующих объектах?
мутации сложно отслеживать в нетривиальных проектах.
О каких костылях Хибернейта идёт речь?
На каждое отступление от идей ORM начала 2000х костыль нужен. Захотел в постгресе jsonb заиспользовать, чтобы ненужные джойны не плодить — добро пожаловать свой диалект писать. Нужно обновить версию parent по обновлению child? Добро пожаловать в прекрасный мир Event Listeners. Которых в свою очередь есть разные наборы с разными ограничениями.
Да, именно так.
в моем мирке side-effects недолюбливают, особенно такие здоровые как флуш в базу графа сущности. Так что это как раз ИМХО не плюс, а здоровый минус Хибернейта.
JPA же о случае, когда доменная область уже сформирована и возникла необходимость хранить её в БД (а до этого, гипотетически, файлы устраивали). В этом случае достаточно описать маппинг, а Хибернейт и схему может создать, и данные туда положить, и изменения данных бизнес-логикой — проапдейтить.
Это гипотетически или реальный кейс? Сложно представить развитый проект с файловым персистенсом. Даже если и есть такое, то надеюсь файлы не в сеттерах пишутся?
Классический ответ таков — JPA отражает доменную область на БД без написания sql вручную.
Доменная область с любыми репозиториями будет нормально выглядеть:
Order persistedOrder = orderRepository.persist(order) //can also update the original order instance, but that's bit of a smelly practice orderRepository.addOrderitem( persistedOrder, // often simply persistedOrder.id new Item("bbb", Money.of(50, "EUR")) )
в репозиториях да, будет SQL или еще что, но если содержать минималистично и без логики — то не будет принципиально сложнее Хибернейтовских маппингов и костылей.
Потом делать
Item item2 = new Item("bbb«, Money.of(50, «EUR»));
order.addItem(item2);
order.addItem(item1);
Я вижу то, что думаю? Обновление order без явного вызова репозитория или entityManager, расчитывая на autoflush ?
Вижу такое очень часто, дико сложно поддерживать в комбинации с привычкой Hibernate разработчиков использовать глубоко мутирующие объекты.
Это не антипаттерн? Как бороться? Для меня лично такая фича одна из основных мотиваций рефакторинга от Хайбернейта на альтернативы даже если пока вроде работает без проблем.
Это значит, что надо написать20-30 мапперов, да? Агонь решение ) про то, сколько багов будет создано в процессе и какая будет стоимость разработки имеет смысл говорить? Это при том, что существенного плюса финальный результат не даст.
откуда баги в мапперах? Оттуда же, откуда в маппинге хибернейта. Принципиальных различий нет, особенно если следовать правильному совету топик кастера:
Правильный подход — всегда создавать отдельные DTO классы.
blog.jooq.org/tag/entitygraph
jooq вообще проприетарная штука, или частично проприетарная, точно не помню. Странно продвигать его как серьезную замену свободным решениям.
в данном конкретном случае я ссылался на полезный разбор из их блога, где нечего про собственно jooq нет.
Отдельный вопрос открытости и свободности jOOQ для открытых баз, но для простоты можно ограничиться Spring JDBC Templates или apache commons-dbutils.
Другой вопрос, почему граф оказался таким разветвленным, и нет ли частичной вины Хибернейта в этом
Постановка вопроса вообще некорректна. Хиб это лишь инструмент по маппингу таблиц на объекты, как он может диктовать архитектуру графа данных? Она диктуется исключительно бизнес-логикой. Разветвленная логика, большая бизнес-структура данных, вот и большой граф.
Бизнес логика редко диктует выбор корней графов, полной нормализации, забивания статических словарных значений в базу или выборки по типу «верни мне все, а я там разберусь что мне нужно». Так же смотри на максиму топиккастера:
JPA же предлагает другой принцип: вначале Java-классы, потом их сохранение в БД.
В десятки раз? Вытащить?
Десятки это образно, я не мерял. Но предполагаю, что близок к правде.
первый спринт/два будет Хибернейтом быстрее, потом чем дальше в лес тем чаще он будет становиться обузой. Из моего лично опыта по крайней мере.
Каждые мелкий апдейт, селект и прочие будет превращаться в отдельную, забагованную вручную написанную километровую sql-jdbc процедуру. Да, стоимость разработки всего этого по сравнению с em.find(BusinessGraphRoot.class, id); — десятки раз, если не сотни.
если ваши разработчики не могут написать простой Insert или Update без драмы, то сильно подозреваю что Хайбернейт ваших проблем не решит. Максимум — перенесет из репозиториев в сервисы.
Проблемы данных с большими графами связей.
ОК, в целом пункт принимаю. Спасибо!
Если вам нужно вытащить сущность, которая является корнем графа на20-30 узлов, и вы решите делать это плейн олд ждбц, вы это сделаете (возможно), но это замедлит разработку вашего проекта в десятки раз.
В десятки раз? Вытащить? Та вроде не, операция довольно тривиальная: выборка по ключу парента, результат пропустить через маппер. Повторить
Другой вопрос, почему граф оказался таким разветвленным, и нет ли частичной вины Хибернейта в этом, но оставим этот вопрос на потом.
Вопрос не троллинга ради, а чтобы понять для себя: зачем вообще использовать Хибернейт?
Пока вижу две ситуации:
1) демо/пилотный проект. Работа с БД не важна, в простейшем случае не нужно об этом думать — все запустит Spring/JPA/Hibernate.
2) коробочный продукт реально поддерживающий работу с разными базами данных.
В остальных случаях — какие проблемы решает Хибернейт не создавая больших, когда есть вменяемый jOOQ, Spring JDBC templates, etc?
Интересно. Плюс малых населенных пунктов в том, ИМХО, что интеграция происходит как правило проще, что автор демонстрирует. Весьма полезно, если не менять страну через год/два.
Вы не можете поехать в отпуск с ребенком в сентябре, так как учебное заведение не разрешает этого делать и если вы все равно поедете, то получите штраф.
Насколько помню, теоретически возможно если доказать что по другому — никак. Например, официальная бумага от работодателя что в другое время отпуск дать возможности нет. Но практически — да, с посещаемостью все строже, и в отпуск всей округой срываются одновременно.
если проблема выучить хайбер так и надо говорить
вижу знатока тонкостей хайбера. Прибирал за такими много.
Если у вас за
user.getId()
скрываются
вас ждут классы требующие выделения системных ресурсов, типа открытия сокета, доступа к контексту графической оболочки и еще вагон вещей
, то у вас крупные проблемы.
ОК, то есть если я понимаю правильно, можно в принципе отделить Change Management от технической доставки изменений и работать с высокоуровневыми требованиями по утяжеленному процессу.
а что, когда удаляется родитель — удалить детей?