Мне кажется, что выбор между Spring Data JDBC и Spring Data JPA сродни выбору между ложкой и вилкой. И тем, и тем мы едим, но щи удобнее есть ложкой, а пельмени — вилкой.
Иными словами, выбирать нужно исходя из создавшегося положение и моя заметка как раз о случаях, когда лучше выбрать Spring Data JPA и не наступить на грабли.
Здесь для упрощения всё показано в одном методе, однако возможна (и мне подобные встречались) ситуация, когда у нас есть что-то вроде
BankAccount acc = repository.findById(id).orElseThow(NPE::new);
check(id);
public void (check) {
boolean available = repository.findIfAvailable(id);
if (!available) throw new IllegalStateException();
}
при чём всё это выполняется в одной транзакции (т.е. используется одна и та же сессия). Для неискушенного разработчика неочевидно, что второй repository.findById(id) возьмёт сущность из наличия.
Кстати, если метод очень большой и туда в разное время влезло несколько разработчиков, то и описанный мной вариант возможен почти дословно.
Можно и так, правда, в этом случае часть методов, объявленных в интерфейсе и тело будет иметь только в интерфейсе, другая же часть будет реализована в классе. ИМХО, лучше уже всё делать единообразно.
Точно не скажу, разговор был формата курилки. Спросил про миграцию на СБ2, вылезло несколько проблем, а поскольку задача была не очень важной, то занимались ей в перерывах между основной работой.
Вот что запомнилось точно — проблемы со Спринг Датой. Начиная с версии 2 изменились названия ключевых методов в JpaRepository:
В проекте несколько десятков репозиториев и тысячи обращений к указанным методам. Миграция "в лоб" (использование новых методов) зацепила бы каждый второй файл. Поэтому воспользовались возможностью подгонки репозиториев под свои нужды:
//основа для всех репозиториев
@NoRepositoryBean
public interface BaseJpaRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
// определяем метод findOne, аналогичный тёзке, существовавшему в версиях 1.*
@Deprecated
T findOne(ID id);
// тоже для findAll
@Deprecated
List<T> findAll(Iterable<ID> ids);
}
public class BaseJpaRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseJpaRepository<T, ID> {
private JpaEntityInformation<T, ?> entityInfo;
private EntityManager entityManager;
public BaseJpaRepositoryImpl(JpaEntityInformation<T, ?> entityInfo, EntityManager entityManager) {
super(entityInfo, entityManager);
this.entityInfo = entityInfo;
this.entityManager = entityManager;
}
//обеспечиваем совместимость поведения
@Override
public T findOne(ID id) {
return findById(id).orElse(null);
}
@Override
public List<T> findAll(Iterable<ID> ids) {
return findAllById(ids);
}
}
//какой-нибудь репозиторий
interface SomeRepository extends BaseJpaRepository<SomeEntiy, Lond> {
}
И о чудо! Все ошибки компиляции исчезают, код работает как и прежде, а изменено всего 2 класса, а не 200. Теперь можно неспешно заменять устаревшее АПИ на новое, благо "Идея" заботливо подсветит все вызовы помеченные @Deprecated.
Знаю один из проектов, который ещё не переехал на СБ2 именно из-за Актуатора.
Кстати, раз уж речь зашла от миграции: отдельная и интересная тема — переезд с голого Спринга на Спринг Бут. Так на одном из проектов столкнулись с тем, что перестал работать откат транзакций при выбрасывании проверяемых исключений. Когда-то давно возникла необходимость откатывать спринговую транзакцию при выбрасывании этого исключения. Это было сделано с помощью AnnotationTransactionAttributeSource примерно вот так:
public class CustomAttributeSource extends AnnotationTransactionAttributeSource {
@Override
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
return new DefaultTransactionAttribute() {
@Override
public boolean rollbackOn(Throwable ex) {
return super.rollbackOn(ex) || ex instanceof CustomCheckedException;
}
};
}
}
На чистом Спринге работало, после переезда на СБ1 транзакции откатываться перестали.
1. Количество развёрнутых носителей не исчисляется тысячами, в лучшем случае сотнями. Количество же развёрнутых боеголовок не превышает двух тысяч. Данные из Википедии.
2. Перехватив ракету до развода боевых блоков можно одной противоракетой уничтожить сразу несколько головных частей. А как мы помним, речь не идёт о перехвате тысяч, а всего лишь сотен носителей. Таким образом, масштабное развёртывание ПРО может сильно уменьшить возможность нанесения ответного удара. Не даром же этот договор был заключен.
3. Как я указал в первом ответе (и как справедливо заметил ув. grondek ) в установки может быть загружено всё что угодно. И именно эта возможность вызывает наибольшее беспокойство.
США свою систему ПРО (из договора о которой они вышли в одностороннем порядке ещё в 2002 году) почему-то не сворачивают и не ликвидируют. Более того, ПРО размещается в непосредственной близости от границ России, в частности в Польше и Румынии. Официально заявляется, что это для защиты от Ирана (верим!). И вот Минобороны России вполне резонно подозревает, что пусковые установки ракет ПРО в этих странах могут использоваться в т. ч. для размещения наступательного вооружения и запрашивает проверку этих установок. На что получает отказ, хотя если наступательных вооружений там нет и установка их невозможна, то этот отказ выглядит подозрительным.
Получается, что Россия не может запросить даже проверку объектов, вызывающих у неё опасение (небезосновательно), при этом от самой России в ультимативной форме требуют уничтожить вооружение, которое, во-первых, не размещается непосредственно у границ США, и во-вторых не является доказанным нарушением Договора РСМД.
Интересно, что в этом случае (малое количество вызовов StringBuilder.append) точное выделение памяти даёт очень хороший прирост, чего не скажешь о случае, когда вызовов StringBuilder.append значительно больше.
Мне кажется, что выбор между Spring Data JDBC и Spring Data JPA сродни выбору между ложкой и вилкой. И тем, и тем мы едим, но щи удобнее есть ложкой, а пельмени — вилкой.
Иными словами, выбирать нужно исходя из создавшегося положение и моя заметка как раз о случаях, когда лучше выбрать Spring Data JPA и не наступить на грабли.
И вам спасибо!
Не за что, наберу ещё материала — и будет вторая часть про кастомизацию репозиториев. Там тоже есть интересные моменты.
Здесь для упрощения всё показано в одном методе, однако возможна (и мне подобные встречались) ситуация, когда у нас есть что-то вроде
при чём всё это выполняется в одной транзакции (т.е. используется одна и та же сессия). Для неискушенного разработчика неочевидно, что второй
repository.findById(id)
возьмёт сущность из наличия.Кстати, если метод очень большой и туда в разное время влезло несколько разработчиков, то и описанный мной вариант возможен почти дословно.
Да уж, впору начинать их коллекционировать и на собеседованиях просить соискателей рассказать о своих случаях.
Дело не в прослойке, а в ошибках компиляции.
На СБ1 (для которого Спринг Дата 1.*) это работает, на СБ2 (для которого Спринг Дата 2.*) нужно:
Optional<SomeEntity>
Optional::isPresent
или цепочкуOptional::orElse/Optional::orElseGet
И так в каждом сервисе.
Точно не скажу, разговор был формата курилки. Спросил про миграцию на СБ2, вылезло несколько проблем, а поскольку задача была не очень важной, то занимались ей в перерывах между основной работой.
Вот что запомнилось точно — проблемы со Спринг Датой. Начиная с версии 2 изменились названия ключевых методов в
JpaRepository
:В проекте несколько десятков репозиториев и тысячи обращений к указанным методам. Миграция "в лоб" (использование новых методов) зацепила бы каждый второй файл. Поэтому воспользовались возможностью подгонки репозиториев под свои нужды:
И о чудо! Все ошибки компиляции исчезают, код работает как и прежде, а изменено всего 2 класса, а не 200. Теперь можно неспешно заменять устаревшее АПИ на новое, благо "Идея" заботливо подсветит все вызовы помеченные
@Deprecated
.Ответ переехал выше.
Знаю один из проектов, который ещё не переехал на СБ2 именно из-за Актуатора.
Кстати, раз уж речь зашла от миграции: отдельная и интересная тема — переезд с голого Спринга на Спринг Бут. Так на одном из проектов столкнулись с тем, что перестал работать откат транзакций при выбрасывании проверяемых исключений. Когда-то давно возникла необходимость откатывать спринговую транзакцию при выбрасывании этого исключения. Это было сделано с помощью
AnnotationTransactionAttributeSource
примерно вот так:На чистом Спринге работало, после переезда на СБ1 транзакции откатываться перестали.
2. Перехватив ракету до развода боевых блоков можно одной противоракетой уничтожить сразу несколько головных частей. А как мы помним, речь не идёт о перехвате тысяч, а всего лишь сотен носителей. Таким образом, масштабное развёртывание ПРО может сильно уменьшить возможность нанесения ответного удара. Не даром же этот договор был заключен.
3. Как я указал в первом ответе (и как справедливо заметил ув. grondek ) в установки может быть загружено всё что угодно. И именно эта возможность вызывает наибольшее беспокойство.
Получается, что Россия не может запросить даже проверку объектов, вызывающих у неё опасение (небезосновательно), при этом от самой России в ультимативной форме требуют уничтожить вооружение, которое, во-первых, не размещается непосредственно у границ США, и во-вторых не является доказанным нарушением Договора РСМД.
Вы правы, передача размера в StringBuilder даёт неплохой прирост в данном случае
Вывод
Интересно, что в этом случае (малое количество вызовов StringBuilder.append) точное выделение памяти даёт очень хороший прирост, чего не скажешь о случае, когда вызовов StringBuilder.append значительно больше.
Спасибо! Уже не первый год работаю, но не устаю удивляться, как много тонкостей стоит вроде бы за простым кодом.
Вы имеете ввиду неэквивалентность байт-кода? Поведение обоих методов, насколько я понимаю, одинаковое: