I’m seeing something strange after upgrading my app to SpringBoot 3 from SpringBoot 2.
The application relies on an existing database (MySQL) which doesn’t use good practices. One of them is using Y
N
for booleans stored in a VARCHAR.
To make things easier on the Java side, I created a converter that looks like this:
@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");
}
}
And on my beans I use it as:
@Column(name = "DELIVERED")
@Convert(converter = BooleanToStringConverter.class)
private Boolean delivered;
So far so good.
If I create a repository to query all delivered entities I’d write something like this in SpringBoot 2:
List<MyClass> findAllByDeliveredIsTrue();
But if I run this in SpringBoot 3 it returns an empty list.
I need to write the query manually to make it work.
@Query("SELECT c FROM MyClass c WHERE c.delivered = true")
List<MyClass> findAllByDeliveredIsTrue();
Any idea why it’s happening?
Edit:
I’ve enabled SQL Logs and definitely queries are created differently. For sake of simplicity I translated some things on the post because on real scenario the database is in Spanish and it uses S
/N
instead of 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
2
Answers
It turns out it's an issue on CriteriaBuilder on Spring Data. The issue is being tracked on the repository issue #2800. At the moment of answering it's still not resolved and affected versions are at least
Spring Boot 3.0.2
The code you showed does not work with a boolean, but rather a tri-state value. Decide what you will do with NULLs. Then be sure to have 3 cases throughout your code.
I would not bother trying to fix the yes/no being in a VARCHAR unless you can actually fix the schema and the data. Instead, test for
== 'N'
as needed. (I picked the "no" side because that works with most European languages.Note, however, that
== 'Y'
is not the same as!= 'N'
— because of the NULL option.Also, check that the
COLLATION
of thatVARCHAR
is case-folding so that ‘n’ == ‘N’. The collation name should end in `_ci“.