вообще, это, конечно рабочий вариант. мы, действительно, можем считать, что последние 6-9 знаков в long - это наша дробная часть, а все, что левее - целая часть. и, при этом, все хранить в "копейках". тогда мы всегда можем делать одинаковое округление в последнем знаке и оставаться в рамках целых чисел. но тут нас ожидает другая проблема: у нас не такой уж большой максимум. сделки на "сотни миллионов" потребуют отдельной "ветки" в логике обработки, которая будет работать медленнее основной. а ведь "сотни миллионов" - это не так уж и много. в общем, предложенная "оптимизация" находится где-то сильно справа на кривой "шипилева". туда стоит лезть только будучи на 100% уверенным, что "тормозит" именно арифметика, а не условный I/O
это прекрасное предложение! только придется всю "арифметику" для них реализовать. либо воспользоваться готовой библиотекой. но будет ли этой быстрее BigDecimal? кто-то проверял?
ну кое к чему все-таки можно придраться например к:
Будьте решительны, не тяните с релизом — можно исправить неудачную страницу, но нельзя исправить пустую
С одной стороны - да, а с другой - "не бывает второго шанса произвести первое впечатление" Если ваш полусырой прототип, который вы решительно решили поскорее презентовать потенциальным инвесторам/покупателям, упадет пару раз прямо во время презентации, то это может крайне негативно повлиять на ваши будущие доходы. Если ваш сервис ложиться из-за "хабраэфекта" после публикации рекламной статьи, то это может сильно повлиять на доверие пользователей к вашему сервису в будущем. И так далее.
Решительность - это хорошо, но про разумный баланс с осторожностью забывать тоже не стоит.
раскажи пожалуйста, каким образом язык программирования 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-а.
вообще, это, конечно рабочий вариант. мы, действительно, можем считать, что последние 6-9 знаков в long - это наша дробная часть, а все, что левее - целая часть. и, при этом, все хранить в "копейках". тогда мы всегда можем делать одинаковое округление в последнем знаке и оставаться в рамках целых чисел.
но тут нас ожидает другая проблема: у нас не такой уж большой максимум.
сделки на "сотни миллионов" потребуют отдельной "ветки" в логике обработки, которая будет работать медленнее основной.
а ведь "сотни миллионов" - это не так уж и много.
в общем, предложенная "оптимизация" находится где-то сильно справа на кривой "шипилева".
туда стоит лезть только будучи на 100% уверенным, что "тормозит" именно арифметика, а не условный I/O
это прекрасное предложение! только придется всю "арифметику" для них реализовать.
либо воспользоваться готовой библиотекой.
но будет ли этой быстрее BigDecimal? кто-то проверял?
-del-
у меня курс 1 к 3
(1 "доллар" за 3 "рубля")
сколько "долларов" я могу получить за 10 "рублей"?
(ответ 333 "цента" - совершенно не верный)
а теперь подумаем над операцией деления.
мне нужно пересчитывать юани в рубли и обратно по курсу 1 : 11.3
;)
Большое спасибо за статью!
Получился действительно достаточно простой способ объяснить относительно не простые для понимания концепции.
ну кое к чему все-таки можно придраться
например к:
С одной стороны - да, а с другой - "не бывает второго шанса произвести первое впечатление"
Если ваш полусырой прототип, который вы решительно решили поскорее презентовать потенциальным инвесторам/покупателям, упадет пару раз прямо во время презентации, то это может крайне негативно повлиять на ваши будущие доходы.
Если ваш сервис ложиться из-за "хабраэфекта" после публикации рекламной статьи, то это может сильно повлиять на доверие пользователей к вашему сервису в будущем.
И так далее.
Решительность - это хорошо, но про разумный баланс с осторожностью забывать тоже не стоит.
раскажи пожалуйста, каким образом язык программирования 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.
А тут слайды.
вот ровно так в Котлине и сделали. и язык все популярнее и популярнее.