-
Notifications
You must be signed in to change notification settings - Fork 1.5k
AttributeConverter is not called anymore for null parameters in query in 2.7.0 #2549
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I am seeing a similar error when using the Hibernate annotation |
@CyrilChevalier So far, using Spring Data 3.0 with HSQL, I have it working just fine. I borrowed your entity type, repository definition, and converter: @ExtendWith(SpringExtension.class)
@ContextConfiguration
public class AttributeConverterIntegrationTests {
@Autowired MyEntityRepository repository;
@Test
void nullBasedFinderShouldBlowUp() {
LocalDate now = LocalDate.now();
repository.save(new MyEntity("TEST", now));
repository.save(new MyEntity("Null", null));
List<MyEntity> dates = repository.findByDate(now);
dates = repository.findByDate(null);
}
@Entity
@Table(name = "my_entity")
@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
static class MyEntity {
@Id
@GeneratedValue private Long id;
private String value;
@Convert(converter = CustomAttributeConverter.class) //
private LocalDate date;
public MyEntity(String value, LocalDate date) {
this.value = value;
this.date = date;
}
}
interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
@Query("select e from AttributeConverterIntegrationTests$MyEntity e WHERE e.date = :date")
List<MyEntity> findByDate(@Nullable @Param("date") LocalDate date);
}
static class CustomAttributeConverter implements AttributeConverter<LocalDate, Long> {
@Override
public Long convertToDatabaseColumn(LocalDate attribute) {
long convertedValue = attribute != null ? attribute.atStartOfDay(ZoneOffset.UTC).toInstant().toEpochMilli() : 0L;
System.out.println("Converted " + attribute + " to " + convertedValue + " for the database.");
return convertedValue;
}
@Override
public LocalDate convertToEntityAttribute(Long dbData) {
LocalDate convertedValue = dbData != null && dbData != 0
? Instant.ofEpochMilli(dbData).atZone(ZoneOffset.UTC).toLocalDate()
: null;
System.out.println("Converted database value " + dbData + " into " + convertedValue);
return convertedValue;
}
}
@Configuration
@ImportResource("classpath:infrastructure.xml")
@EnableJpaRepositories(basePackageClasses = MyEntityRepository.class, considerNestedRepositories = true,
includeFilters = @ComponentScan.Filter(value = { MyEntityRepository.class }, type = FilterType.ASSIGNABLE_TYPE))
static class Config {
@Bean
CustomAttributeConverter customAttributeConverter() {
return new CustomAttributeConverter();
}
}
} It produced:
There are some extra infrastructure bits not shown since we don't have access to Spring Boot. It should be pointed out that I had to add |
Okay, I ran the same thing against Spring Data 2.7.x, and it still passed: @ExtendWith(SpringExtension.class)
@ContextConfiguration
public class AttributeConverterIntegrationTests {
@Autowired MyEntityRepository repository;
@Test
void nullBasedFinderShouldBlowUp() {
LocalDate now = LocalDate.now();
repository.save(new MyEntity("TEST", now));
repository.save(new MyEntity("Null", null));
List<MyEntity> dates = repository.findByDate(now);
dates = repository.findByDate(null);
}
@Entity
@Table(name = "my_entity")
@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
static class MyEntity {
@Id
@GeneratedValue private Long id;
private String value;
@Convert(converter = CustomAttributeConverter.class) //
private LocalDate date;
public MyEntity(String value, LocalDate date) {
this.value = value;
this.date = date;
}
}
interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
@Query("select e from AttributeConverterIntegrationTests$MyEntity e WHERE e.date = :date")
List<MyEntity> findByDate(@Nullable @Param("date") LocalDate date);
}
static class CustomAttributeConverter implements AttributeConverter<LocalDate, Long> {
@Override
public Long convertToDatabaseColumn(LocalDate attribute) {
long convertedValue = attribute != null ? attribute.atStartOfDay(ZoneOffset.UTC).toInstant().toEpochMilli() : 0L;
System.out.println("Converted " + attribute + " to " + convertedValue + " for the database.");
return convertedValue;
}
@Override
public LocalDate convertToEntityAttribute(Long dbData) {
LocalDate convertedValue = dbData != null && dbData != 0
? Instant.ofEpochMilli(dbData).atZone(ZoneOffset.UTC).toLocalDate()
: null;
System.out.println("Converted database value " + dbData + " into " + convertedValue);
return convertedValue;
}
}
@Configuration
@ImportResource("classpath:infrastructure.xml")
@EnableJpaRepositories(basePackageClasses = MyEntityRepository.class, considerNestedRepositories = true,
includeFilters = @ComponentScan.Filter(value = { MyEntityRepository.class }, type = FilterType.ASSIGNABLE_TYPE))
static class Config {
@Bean
CustomAttributeConverter customAttributeConverter() {
return new CustomAttributeConverter();
}
}
} Produces:
Not sure if there is something else happening that is causing havoc. The biggest differences are see are A) you're using postgresql while I'm using HSQL and B) I added |
If I don't put in that
|
Part of my concern is what you're expecting to happen when you invoke
I guess this isn't coming up because your database engine is blowing up sooner? |
Okay, I swapped out HSQL and plugged in Testcontainers so I could use PostgreSQL, and what do you know...
This Looks very similar to yours:
Investigating further. |
More digging has revealed that Hibernate does NOT apply attribute converters when Related: https://hibernate.atlassian.net/browse/HHH-8697, https://hibernate.atlassian.net/browse/HHH-9901, https://hibernate.atlassian.net/browse/HHH-9320 |
Hi @gregturn, Sorry for the late answer and thank you for all the research. I had seen that it worked with HSQL and not with Postgresql. Hibernate not using AttributeConverter for null values is a concern. I've made a workaround in our code for these cases. For @quaff : indeed, as I previously said, I think the problem comes from the changes in the way Spring Data processes the null values. The new class HibernateJpaParametersParameterAccessor transforms null values into TypedParameterValue objects. As a consequence, the AttributeConverter for these values is not called anymore by Hibernate. Thanks again for your work, guys! |
@CyrilChevalier I just patched the way |
If you could provide us with a reproducer showing that attribute converters are indeed invoke for |
@gregturn I've not dug into all the information given here, but I opened #2601 which was closed due to relation to this issue. The piece I am querying about is the fact that this says Simply moving the null check to the last conditional allows binding to work properly. |
@therealaleko #2601 wasn't closed due to relation to this issue, but because it is a duplicate of #2548. |
@schauder and it was closed without information from what I can tell. So I'm left confused as to what/where the root cause of my issue is. Can you help me pin point that so I can track it, please? |
This fix was backported to |
Context
We have some entites that used AttributeConverter to convert a numeric type to a LocalDate type like this one :
With this kind of AttributeConverter :
This is an example of query we can use in the repo :
Problem
In 2.7.0 version, when I call the method findByDate with a null parameter with a postgresql database, the query failed with this message :
With the previous versions, the query works perfectly.
I think the problem comes from the new class HibernateJpaParametersParameterAccessor which transforms null values into TypedParameterValue objects. The AttributeConverter for these values is not called anymore.
I have prepared a project to reproduce the problem : spring-data-jpa-convert-null.
The text was updated successfully, but these errors were encountered: