Я вижу что-то странное после обновления своего приложения до SpringBoot 3 с SpringBoot 2.
Приложение использует существующую базу данных (MySQL), которая не использует передовой опыт. Один из них использует YN для логических значений, хранящихся в VARCHAR.
Чтобы упростить работу с Java, я создал конвертер, который выглядит так:
@Converter
public class BooleanToStringConverter implements AttributeConverter<Boolean, String> {
@Override
public String convertToDatabaseColumn(Boolean attribute) {
if (attribute == null) {
return null;
}
return attribute ? "Y" : "N";
}
@Override
public Boolean convertToEntityAttribute(String dbData) {
if (dbData == null) {
return null;
}
return dbData.equals("Y");
}
}
И на своих бобах я использую его как:
@Column(name = "DELIVERED")
@Convert(converter = BooleanToStringConverter.class)
private Boolean delivered;
Все идет нормально.
Если я создам репозиторий для запроса всех доставленных объектов, я напишу что-то вроде этого в SpringBoot 2:
List<MyClass> findAllByDeliveredIsTrue();
Но если я запускаю это в SpringBoot 3, он возвращает пустой список.
Мне нужно написать запрос вручную, чтобы он работал.
@Query("SELECT c FROM MyClass c WHERE c.delivered = true")
List<MyClass> findAllByDeliveredIsTrue();
Есть идеи, почему это происходит?
Редактировать:
Я включил журналы SQL, и определенно запросы создаются по-другому. Для простоты я перевел некоторые вещи в посте, потому что в реальном сценарии база данных на испанском языке и использует S/N вместо Y/N.
Java service
@PostConstruct
public void init() {
log.error("DEBUG INIT");
repo.findFirstByActiuIsTrueOrderByDataDesc();
repo.findFirstByActiuIsTrueOrderByDataDesc1(); ///Working case
log.error("DEBUG END");
}
Repository
//Failing case
Optional<Condicio> findFirstByActiuIsTrueOrderByDataDesc();
@Query("SELECT c FROM Condicio c WHERE c.actiu = true ORDER BY c.data ASC")
Optional<Condicio> findFirstByActiuIsTrueOrderByDataDesc1(); //Working case
2023-05-13T10:23:19.336+02:00 ERROR 42936 --- [ restartedMain] c.n.a.backend.service.CondicioService : DEBUG INIT
Hibernate: select c1_0.COND_CONDICIO,c1_0.COND_ACTIU,c1_0.COND_DATA,c1_0.COND_DESCRIPCIO from CONDICIONS c1_0 where c1_0.COND_ACTIU order by c1_0.COND_DATA desc limit ?
Hibernate: select c1_0.COND_CONDICIO,c1_0.COND_ACTIU,c1_0.COND_DATA,c1_0.COND_DESCRIPCIO from CONDICIONS c1_0 where c1_0.COND_ACTIU='S' order by c1_0.COND_DATA asc
2023-05-13T10:23:19.451+02:00 ERROR 42936 --- [ restartedMain] c.n.a.backend.service.CondicioService : DEBUG END
Если «SELECT c FROM MyClass c WHERE c.delivered = true» работает, не означает ли это, что DELIVERED на самом деле является BOOLEAN, а не VARCHAR?
Тот же код работает в SB 2. JPA настроен правильно, и я вижу упомянутый журнал. Кроме того, сработает переписывание запроса как findAllByDelivered(Boolean delivered);. Как уже упоминалось, на уровне базы данных базовым типом является VARCHAR, и используется преобразователь.
@AlexRoig ты пробовал не использовать глагол is? то есть findAllByDeliveredTrue вместо findAllByDeliveredIsTrue?
@AndreyB.Panfilov Пожалуйста, укажите, как именно изменение имени метода, как вы предлагаете, может решить эту проблему. Это невозможно.
@OP В вашем посте нет ключевого слова isTrue. Просьба уточнить.
@ user207421 Я бы посоветовал сначала прочитать официальную документацию: «True findByActiveTrue () … где x.active = true»
Вы тщательно обрабатываете «ноль» как третье возможное значение. Возможно, проблема в нем?




Код, который вы показали, работает не с логическим значением, а со значением с тремя состояниями. Решите, что вы будете делать с NULL. Тогда убедитесь, что в вашем коде есть 3 случая.
Я бы не стал пытаться исправить да/нет в VARCHAR, если вы действительно не можете исправить схему и данные. Вместо этого проверяйте == 'N' по мере необходимости. (Я выбрал сторону «нет», потому что это работает с большинством европейских языков.
Однако обратите внимание, что == 'Y' — это не то же самое, что != 'N' — из-за опции NULL.
Кроме того, убедитесь, что COLLATION этого VARCHAR складывается по регистру, так что 'n' == 'N'. Имя сопоставления должно заканчиваться на `_ci``.
Обработка нулей — хороший момент, но реализация преобразователя BooleanToString, которую я уже рассмотрел, уже охватывает это. Дело в том, что эти запросы работали на SpringBoot 2 и ни с того ни с сего на Spring Boot 3 они больше не работают. Похоже, что есть какой-то новый оптимизатор запросов, который заменяет JPQL на c1_0.COND_ACTIU для неявной проверки логического истинного состояния вместо его указания, и, поскольку он не проходит через преобразователь, БД фактически не знает, с чем его сравнивать. БД — это то, что я не могу изменить, и есть собственная ERP, и мы не можем ее контролировать.
Оказывается, это проблема CriteriaBuilder в Spring Data. Проблема отслеживается в выпуске репозитория № 2800. На момент ответа он все еще не решен, и затронутые версии по крайней мере Spring Boot 3.0.2
Вы уверены, что Spring Boot автоматически настраивает репозиторий как репозиторий JPA? Действительно ли тот же код работал в Spring Boot 2? Видите ли вы в журналах: «Загрузка репозиториев Spring Data JPA» и «Найдены интерфейсы репозитория NNN JPA»