#orm #clojure #persistence #idioms
#orm #clojure #сохраняемость #идиомы
Вопрос:
Я опытный разработчик Java и SQL, рассматривающий Clojure для своего следующего проекта. Я потратил некоторое время на изучение основ, но все еще пытаюсь понять идиоматический способ безболезненной обработки взаимодействий с реляционной базой данных в отношении обновлений.
Большинство тем, которые я прочитал, сосредоточены на стороне запросов, что для меня на самом деле не проблема, поскольку я очень хорошо разбираюсь в SQL и считаю jdbc.next более чем адекватным для моих запросов (возможно, дополненный поддержкой Toucan ‘hydration’, если требуется).
Моя главная проблема заключается в том, как обрабатывать сохраняемость, например, как автоматически сохранять измененную (вложенную) карту (?) Обратно в базу данных без необходимости самостоятельно отслеживать состояние.
В качестве примечания, у меня достаточно опыта работы как с самим Hibernate, так и с JPA, и я все еще чувствую, что они слишком жесткие и подробные.
Инструмент, который я считаю идеальным в этом отношении, является https://www.sqlalchemy.org / который я ранее использовал через jython с большим чувством удовлетворения.
Большое спасибо за ваш вклад.
Обновление на основе некоторых комментариев / вопросов ниже:
Чтобы, надеюсь, более четко определить контекст, предположим, что сценарий корзины покупок веб-приложения: новый клиент проверяет. Необходимо выполнить вставку для клиента, возможно, другую для адреса доставки, другую для заказа, и их необходимо синхронизировать таким образом, чтобы сохранялась ссылочная целостность. Конечно, можно выполнить все шаги вручную, но чем сложнее сценарий, тем сложнее становится обработка. Моя база данных является реляционной.
Второе обновление
Чтобы прояснить мой вопрос дальше, моя проблема заключается не в том, как выдавать инструкции ‘insert / update / delete’, а в том, чтобы отслеживать, какие инструкции должны быть выданы в соответствии с текущим положением вещей.
Я бы ожидал, что уровень «ORM» будет отслеживать, какие из моих «данных» являются новыми, которые были изменены, удалены и т.д., и автоматически выполнять приведенный выше фрагмент кода согласованным образом, аналогично менеджеру сущностей Hibernate (пожалуйста, посмотрите на ссылку ниже https://www.sitepoint.com/hibernate-introduction-persisting-java-objects / в разделе «Выполнение операций CRUDдля объектов» для примера предполагаемой функциональности).
В качестве альтернативы я хотел бы иметь возможность определить, что произошло с каждой «частью» моих данных (граф объектов на языке гибернации), а затем выполнить фрагмент, которым вы поделились, обходя каждый элемент, «запрашивая» его состояние / статус и выводя правильное утверждение для отправки обратно в базу данных.
Из моего понимания языка до сих пор эта функциональность является родной (?) Для структур данных clojure, Но я пока не смог на нее повлиять.
Комментарии:
1. Возможно, я плохо понимаю вашу проблему, но я думаю, что способом Clojure было бы избегать состояния, если это вообще возможно. При необходимости запрашивайте вашу базу данных, преобразуйте данные, отобразите результат. Аналогично, транзакция обратно в БД не изменит состояние вашего приложения.
2. тогда в чем ваш вопрос? если вы хотите выполнить обновление, просто выполните его с помощью jdbc. если вы хотите отслеживать «состояние» (или «изменения»), вы можете взглянуть на datomic.com
3. Чтобы, надеюсь, более четко определить контекст, предположим, что сценарий корзины покупок веб-приложения: новый клиент проверяет. Необходимо выполнить вставку для клиента, другую для заказа, и их необходимо синхронизировать таким образом, чтобы сохранялась ссылочная целостность. Конечно, можно выполнить все шаги вручную, но чем сложнее сценарий, тем сложнее становится обработка. Моя база данных является реляционной.
4. Полные по Тьюрингу языки программирования отличаются тем, что они упрощают, а что нет. Сохранение сложного состояния (как указано выше) — это то, для чего предназначен datomic (и он может отлично работать поверх реляционных баз данных), но, основываясь на вашем описании того, что вы хотите, мне интересно, действительно ли вы не понимаете, что такое Clojure. «Обновление» на самом деле не то, что Clojure стремится упростить. Если вы не согласны с базовыми концепциями и считаете это более приятным способом написания Java, я думаю, вы столкнетесь с большими трениями…
Ответ №1:
Вы говорите, что используете next.jdbc
— в документации есть примеры выполнения обновлений и вставок с использованием транзакций для обеспечения согласованности, поэтому я не уверен, что вы действительно спрашиваете здесь.
next.jdbc.sql
Пространство имен содержит update!
и insert!
функционирует. next.jdbc
Пространство with-transaction
имен предназначено для создания транзакций вокруг нескольких операций.
update!
возвращает количество обновленных строк. insert!
возвращает вновь вставленные ключи (насколько это возможно, в зависимости от того, как работает конкретный используемый вами драйвер базы данных).
В программах Clojure, которые работают с базой данных, часто пытаются отделить код сохраняемости от чистой бизнес-логики, поэтому обычно у вас будет что-то вроде:
(defn some-process [...]
(let [data (some-database-query ...)
updates (perform-business-logic ...)]
(with-transaction [tx ...]
...based on what is in updates...
(update-something tx ...)
(insert-something tx ...)
(insert-more-stuff tx ...))))
Если последующие вставки / обновления основаны на сгенерированных ключах из предыдущих вставок, вы бы использовали a let
для захвата результата предыдущих вставок, чтобы вы могли получить доступ к этим ключам в других вставках / обновлениях.
Было бы разумно реорганизовать приведенное выше, чтобы вся with-transaction
форма была в отдельной some-process
вызываемой функции, передавая updates
и соединение / источник данных.
Если это не отвечает на ваш вопрос, можете ли вы попытаться уточнить, что вы на самом деле спрашиваете?
Кроме того, не стесняйтесь присоединиться к Slack Clojurians и публиковать вопросы в #sql
канале, если вы хотите интерактивного разговора с другими Clojurians о сохраняемости реляционной базы данных.
Слабина Clojure: https://clojurians.slack.com
Самостоятельная регистрация через: http://clojurians.net (или я могу разместить здесь пользовательскую ссылку для приглашения, если у вас возникли проблемы с самостоятельной регистрацией)
Комментарии:
1. Здравствуйте, спасибо за подробный ответ и ссылки. Я уже зарегистрировался на closurians.net и обновил мой вопрос, основываясь на ваших данных. Любые дальнейшие комментарии будут очень признательны!
Ответ №2:
ORM не имеет большого смысла, ни больше, ни в Clojure. Новым, улучшенным методом является CQRS, который можно применять без специальных инструментов и который очень хорошо работает в Clojure. См. Эссе Мартина Фаулера о разделении ответственности за команды и запросы на https://martinfowler.com/bliki/CQRS.html .
Ответ №3:
Я думаю, что нашел что-то, что подходит для моего варианта использования. Я могу использовать datascript в качестве своего локального хранилища данных, а затем, когда я буду готов, я смогу (?) Извлечь все изменения и отправить их обратно в мою «основную» базу данных.
Кажется, люди пытались это сделать, как описано в этой теме:
datomic <-> передачи datascript Я создаю веб-приложение, поддерживаемое datomic на стороне сервера, и планирую использовать datascript в клиенте.
план состоит в том, чтобы:
- экспортируйте кучу связанных данных клиенту (API-интерфейсы datomic query / pull / entity для создания данных edn, подходящих для transact в datascript) — это может быть одноразовая вещь или исходные данные, которые будут извлечены позже.
- позвольте пользователю изменить его (транзакции в datascript)
- сохраните все изменения пользователя обратно в datomic (можно отправить полный список транзакций, выполненных на клиенте, обратно на сервер, но в идеале один tx, представляющий сумму всех изменений)
Одна из проблем, с которой нужно иметь дело, — это идентификация. Например, повышения и сокращения (как на клиенте, так и на сервере) должны происходить с правильными объектами.
У меня есть некоторые планы относительно того, как все это будет работать, но прежде чем вдаваться в подробности, мне просто> любопытно, рассматривал ли кто-нибудь еще этот общий подход?
Существует также ссылка на файл кода (который мне нужно будет переварить).
Большое спасибо за все комментарии и ответ!