раскажи пожалуйста, каким образом язык программирования Go решает описанные проблемы объектно-реляционного маппинга? мне всегда казалось, что концепция ORM не зависит от языка программирования. видимо, я ошибаюсь, но очень хочу расширить свои знания по этому поводу! у Go-шных ORM нет ленивой загрузки? но это не решение проблемы же! или ленивая загрузка есть? но как тогда решается проблема состояний объекта относительно соединения с БД (attached/detached)? у Go-шных ORM нет проблемы с N+1? а как же они тогда работают с полями объекта типа "коллекция"? или просто в экосистеме Go даже близко нет аналога JPA/Hibernate и все, что экосистема Go может предложить в качестве ORM-а - это аналог JOOQ в том виде, в каком он был лет 10 назад?
Ну вот, выясняются интересные подробности. Сначала речь шла про raw-sql (который вполне себе пишется в лог), а теперь выясняется, что ожидался лог с sql, где параметры уже подставлены в параметризированный запрос. Все верно, в логах будет запрос вида
select col_1, col_2, col_3 from my_table where col_1 = ? and col_2 = ?
а следом будет напечатан массив со значениями параметров. так работает потому, что хибер, как и полагается хорошему ORM-у, создает PreparedStatement, а потом отдельно передает для него нужный набор параметров. Вот он и в лог выводит параметризованный запрос отдельно и набор параметров отдельно. для 98% случаев этого более чем достаточно. но даже с 20-этажным запросом я не вижу особой проблемы прочитать лог в таком виде, или быстро превратить 2 части лога в одну строку
Конечно же hibernate умеет показывать в логах сгенерированный параметризированный SQL запрос! Кажется, с самой первой версии умел. Показать параметры запроса в логах тоже не проблема.
а теперь в том же DTO надо добавить новое поле, которое в Entity есть, но оно тоже ленивое. Что придется делать? Придется пойти и поправить запрос на уровне работающем с Entity! Таким образом, информация о структуре DTO протекает на уровень работы с Entity. И вот уже нет никакой "слоистой" архитектуры. Есть очередная "дырявая", в которой слои протекают друг в друга.
в том, что надо знать, когда они нужны, а когда нет. нужно знать, в каком DTO используются поля из Tags, а в каком не используются. а это уже знание о структуре DTO на уровне слоя для работы с Entity
так где же должно быть управление транзакциями в вашей многослойной архитектуре?! если слой работы с Entity не знает, что потом из Entity запросят для формирования DTO, то что же он должен вытащить из БД для предотвращения появления LazyInitializationException в будущем при обращении к "ленивым" полям Entity?!
казалось бы, ну вытаскивай joing fetch ленивые поля всегда и нет проблем! но они есть если у меня по 100 тегов у каждого продукта и я вытаскиваю 100 продуктов, значит из БД мне приедет 10000 строк. и зачем мне гонять эти данные, если потом при формировании DTO к коллекции тегов никто не обратится? а если у меня десяток ленивых полей в Entity и для разных DTO используются разные поля?
и вот у нас уже знание о структуре DTO неявно потекло на уровень работы с Entity
при чем тут commit/rollback?! мы пока только про выборки. этого достаточно. так где же идет управление транзакцией и каким это образом "функционал слоя работающего с Entity должен быть реализован так, чтобы исключить возникновение LazyInitializationException"? чтобы исключить LazyInitializationException слой работы с Entity должен как-то знать, что именно потом будет запрошено из этой Entity. то есть, слой работы с Entity должен неявно знать структуру будущего DTO. и вот у нас уже потекли слои друг в друга. причем, неявно.
Если у меня есть сущность Product и внутри нее есть множество Tag (замапленных как OneToMany), а у Tag есть color то вот я выбрал Product по ID и передал его в верхний слой. а там у моего Product спросили product.getTags() и побежали по этому списку спрашивая getColor() у каждого элемента. что же мы получим в слое работы с DTO, если заранее не вытащить join fetch Tag ?
и в каком слое происходит управление транзакцией? в слое, где находятся DTO? тогда это нарушение принципов слоеной архитектуры в слое, где идет работа с Entity? тогда добро пожаловать в LazyInitializationExeption, либо слой работы с Entity должен как-то знать, что именно надо вытащить из БД, чтобы на слоях выше LazyInitializationExeption не возникало. а значит у нас знание о структуре DTO неявно перетекает на уровень работы с Entity.
простите, но все эти "слоеные архитектуры" - очередной marketing bullshit для продажи книг и курсов. ну и еще отличный баттлфилд для холиваров
а аудит в очередь/DW мы должны писать в рамках транзакции, если вызываемый метод транзакционный? если нет, то у нас есть не иллюзорный шанс записать в очередь/DW данные, но не записать в БД (и наоборот) если да, то у нас, внезапно, появляется распределенный коммит и не понятно, где транзакция начинается и заканчивается. или тут еще между строк предполагается транзакционный outbox для аудит-данных (с ретраями, очистками по таймеру, контролем задержки и прочим блэкджеком)?
ну и в качестве примера выбрана крайне неудачная реализация аспекта. комментировать не буду, так как статья, вроде бы, не о конкретной реализации, а о "подходе"
итого, в сухом остатке имеем: не забывайте использовать одинаковые имена ключей для трейсов и, если очень хочется, логгируйте последовательность вызова методов в рамках обработки запроса
У Кея Хрстманна есть прекрасный доклад по этой теме. Называется "Java for Small Coding Tasks" Настоятельно рекомендую. Тема раскрыта немного с другой стороны, но зато лучше показана практическая сторона этого JEP-а.
но если вы завели еще одну группу, то консьюмер из новой группы будет вынужден прочитать все те же сообщения, что и консьюмер из первой группы. как это поможет "разгрузить" первый консьюмер?
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
// do work
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break; // Exit the loop
}
}
System.out.println("Worker thread was interrupted.");
}
но вы же просто заменили catch на addWrapper(ExceptionHandler.DEFAULT) это не снизило когнитивную нагрузку при чтении это ее повысило вместо привычного, описанного во всех учебниках синтаксиса, надо осознавать ваш доморощенный
кстати, покажите пожалуйста пример с вашим аналогом finally (post-processor, который отрабатывает всегда, вне зависимости от того, было ли выброшено исключение в процессе прохода по вашему пайплайну)
я из мира радиоэлектроники в программирование пришел. математик из меня - никакой, но я тоже использую операцию "+", когда мне нужно сложить 2 числа, а не сажусь паять сумматор
раскажи пожалуйста, каким образом язык программирования Go решает описанные проблемы объектно-реляционного маппинга?
мне всегда казалось, что концепция ORM не зависит от языка программирования.
видимо, я ошибаюсь, но очень хочу расширить свои знания по этому поводу!
у Go-шных ORM нет ленивой загрузки? но это не решение проблемы же!
или ленивая загрузка есть? но как тогда решается проблема состояний объекта относительно соединения с БД (attached/detached)?
у Go-шных ORM нет проблемы с N+1? а как же они тогда работают с полями объекта типа "коллекция"?
или просто в экосистеме Go даже близко нет аналога JPA/Hibernate и все, что экосистема Go может предложить в качестве ORM-а - это аналог JOOQ в том виде, в каком он был лет 10 назад?
Ну вот, выясняются интересные подробности. Сначала речь шла про raw-sql (который вполне себе пишется в лог), а теперь выясняется, что ожидался лог с sql, где параметры уже подставлены в параметризированный запрос.
Все верно, в логах будет запрос вида
а следом будет напечатан массив со значениями параметров.
так работает потому, что хибер, как и полагается хорошему ORM-у, создает PreparedStatement, а потом отдельно передает для него нужный набор параметров.
Вот он и в лог выводит параметризованный запрос отдельно и набор параметров отдельно.
для 98% случаев этого более чем достаточно.
но даже с 20-этажным запросом я не вижу особой проблемы прочитать лог в таком виде, или быстро превратить 2 части лога в одну строку
Конечно же hibernate умеет показывать в логах сгенерированный параметризированный SQL запрос! Кажется, с самой первой версии умел.
Показать параметры запроса в логах тоже не проблема.
а теперь в том же DTO надо добавить новое поле, которое в Entity есть, но оно тоже ленивое. Что придется делать? Придется пойти и поправить запрос на уровне работающем с Entity!
Таким образом, информация о структуре DTO протекает на уровень работы с Entity.
И вот уже нет никакой "слоистой" архитектуры. Есть очередная "дырявая", в которой слои протекают друг в друга.
в том, что надо знать, когда они нужны, а когда нет.
нужно знать, в каком DTO используются поля из Tags, а в каком не используются.
а это уже знание о структуре DTO на уровне слоя для работы с Entity
так где же должно быть управление транзакциями в вашей многослойной архитектуре?!
если слой работы с Entity не знает, что потом из Entity запросят для формирования DTO, то что же он должен вытащить из БД для предотвращения появления LazyInitializationException в будущем при обращении к "ленивым" полям Entity?!
казалось бы, ну вытаскивай joing fetch ленивые поля всегда и нет проблем!
но они есть
если у меня по 100 тегов у каждого продукта и я вытаскиваю 100 продуктов, значит из БД мне приедет 10000 строк.
и зачем мне гонять эти данные, если потом при формировании DTO к коллекции тегов никто не обратится?
а если у меня десяток ленивых полей в Entity и для разных DTO используются разные поля?
и вот у нас уже знание о структуре DTO неявно потекло на уровень работы с Entity
при чем тут commit/rollback?!
мы пока только про выборки. этого достаточно.
так где же идет управление транзакцией и каким это образом "функционал слоя работающего с Entity должен быть реализован так, чтобы исключить возникновение LazyInitializationException"?
чтобы исключить LazyInitializationException слой работы с Entity должен как-то знать, что именно потом будет запрошено из этой Entity.
то есть, слой работы с Entity должен неявно знать структуру будущего DTO.
и вот у нас уже потекли слои друг в друга.
причем, неявно.
Если у меня есть сущность Product и внутри нее есть множество Tag (замапленных как OneToMany), а у Tag есть color
то вот я выбрал Product по ID и передал его в верхний слой.
а там у моего Product спросили product.getTags() и побежали по этому списку спрашивая getColor() у каждого элемента.
что же мы получим в слое работы с DTO, если заранее не вытащить join fetch Tag
?
все еще bullshit
с чего это вдруг Pageable не работает с Query?!
я каждый день пишу запросы вроде
и все прекрасно работает!
limit/offset на месте
вы точно мануал по spring data jpa читали?
и в каком слое происходит управление транзакцией?
в слое, где находятся DTO? тогда это нарушение принципов слоеной архитектуры
в слое, где идет работа с Entity? тогда добро пожаловать в LazyInitializationExeption, либо слой работы с Entity должен как-то знать, что именно надо вытащить из БД, чтобы на слоях выше LazyInitializationExeption не возникало. а значит у нас знание о структуре DTO неявно перетекает на уровень работы с Entity.
простите, но все эти "слоеные архитектуры" - очередной marketing bullshit для продажи книг и курсов. ну и еще отличный баттлфилд для холиваров
а аудит в очередь/DW мы должны писать в рамках транзакции, если вызываемый метод транзакционный?
если нет, то у нас есть не иллюзорный шанс записать в очередь/DW данные, но не записать в БД (и наоборот)
если да, то у нас, внезапно, появляется распределенный коммит и не понятно, где транзакция начинается и заканчивается.
или тут еще между строк предполагается транзакционный outbox для аудит-данных (с ретраями, очистками по таймеру, контролем задержки и прочим блэкджеком)?
ну и в качестве примера выбрана крайне неудачная реализация аспекта.
комментировать не буду, так как статья, вроде бы, не о конкретной реализации, а о "подходе"
итого, в сухом остатке имеем: не забывайте использовать одинаковые имена ключей для трейсов и, если очень хочется, логгируйте последовательность вызова методов в рамках обработки запроса
честно говоря, на "систему" и "подход" не тянет.
Это же перевод старой статьи отсюда
https://www.baeldung.com/spring-data-jpa-projections
но без пометки "Перевод"
фу так делать
У Кея Хрстманна есть прекрасный доклад по этой теме.
Называется "Java for Small Coding Tasks"
Настоятельно рекомендую.
Тема раскрыта немного с другой стороны, но зато лучше показана практическая сторона этого JEP-а.
Тут видео доклада на javaone.
А тут слайды.
вот ровно так в Котлине и сделали. и язык все популярнее и популярнее.
но если вы завели еще одну группу, то консьюмер из новой группы будет вынужден прочитать все те же сообщения, что и консьюмер из первой группы. как это поможет "разгрузить" первый консьюмер?
обычно в этом случае делают так
только он Евгений.
Это очень недооцененная статья.
Спасибо, что опубликовали!
но вы же просто заменили catch на addWrapper(ExceptionHandler.DEFAULT)
это не снизило когнитивную нагрузку при чтении
это ее повысило
вместо привычного, описанного во всех учебниках синтаксиса, надо осознавать ваш доморощенный
кстати, покажите пожалуйста пример с вашим аналогом finally (post-processor, который отрабатывает всегда, вне зависимости от того, было ли выброшено исключение в процессе прохода по вашему пайплайну)
Смысл этих аннотаций - отлавливать null-refs во время компиляции (сборки).
Валидаторы тут совершенно ни при чем.
я из мира радиоэлектроники в программирование пришел. математик из меня - никакой, но я тоже использую операцию "+", когда мне нужно сложить 2 числа, а не сажусь паять сумматор