Skip to content
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

NullPointerException when loading deeply nested embedded entities #1676

Closed
IvanPriests opened this issue Nov 27, 2023 · 1 comment
Closed
Assignees
Labels
type: bug A general bug

Comments

@IvanPriests
Copy link

IvanPriests commented Nov 27, 2023

During updating spring from 3.1.5 to 3.2.0 our crud jdbc repository method that returning custom response started failing with NPE.
We got method like this:

@Query("""
           SELECT *, 5 as extend_value FROM entity_table
        """)
    fun getExtendedEntities(): List<ExtendEntity>
    
    data class ExtendEntity(
    @Embedded.Nullable
    val entity: Entity,
    val extendValue: Int,
)

Entity example:

@Table("entity_table")
data class Entity(
    @Embedded.Nullable
    val nestedEntity: NestedEntity,
    @Id
    val id: EntityId = EntityId.new(),
    val time: Instant,
) {
    @PersistenceCreator
    constructor(
        id: Ulid,
        time: Instant,
        nestedEntity: NestedEntity,
    ) : this(
        nestedEntity,
        EntityId(id),
        time
    )
}

Exception stacktrace:

Cannot invoke "org.springframework.data.relational.core.mapping.AggregatePath$ColumnInfo.alias()" because the return value of "org.springframework.data.relational.core.mapping.AggregatePath$TableInfo.reverseColumnInfo()" is null
java.lang.NullPointerException: Cannot invoke "org.springframework.data.relational.core.mapping.AggregatePath$ColumnInfo.alias()" because the return value of "org.springframework.data.relational.core.mapping.AggregatePath$TableInfo.reverseColumnInfo()" is null
  at org.springframework.data.jdbc.core.convert.MappingJdbcConverter$ResolvingRelationalPropertyValueProvider.hasValue(MappingJdbcConverter.java:424)
  at org.springframework.data.relational.core.conversion.MappingRelationalConverter.shouldReadEmbeddable(MappingRelationalConverter.java:586)
  at org.springframework.data.relational.core.conversion.MappingRelationalConverter.readEmbedded(MappingRelationalConverter.java:565)
  at org.springframework.data.relational.core.conversion.MappingRelationalConverter$2.getPropertyValue(MappingRelationalConverter.java:490)
  at org.springframework.data.relational.core.conversion.MappingRelationalConverter$2.getPropertyValue(MappingRelationalConverter.java:475)
  at org.springframework.data.mapping.model.PersistentEntityParameterValueProvider.getParameterValue(PersistentEntityParameterValueProvider.java:71)
  at org.springframework.data.relational.core.conversion.MappingRelationalConverter$ConvertingParameterValueProvider.getParameterValue(MappingRelationalConverter.java:1161)
  at org.springframework.data.mapping.model.SpELExpressionParameterValueProvider.getParameterValue(SpELExpressionParameterValueProvider.java:49)
  at org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator.extractInvocationArguments(ClassGeneratingEntityInstantiator.java:301)
  at org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator$EntityInstantiatorAdapter.createInstance(ClassGeneratingEntityInstantiator.java:273)
  at org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:98)
  at org.springframework.data.relational.core.conversion.MappingRelationalConverter.read(MappingRelationalConverter.java:454)
  at org.springframework.data.relational.core.conversion.MappingRelationalConverter.readAggregate(MappingRelationalConverter.java:348)
  at org.springframework.data.relational.core.conversion.MappingRelationalConverter.readAggregate(MappingRelationalConverter.java:311)
  at org.springframework.data.jdbc.core.convert.MappingJdbcConverter.readAndResolve(MappingJdbcConverter.java:287)
  at org.springframework.data.jdbc.core.convert.JdbcConverter.readAndResolve(JdbcConverter.java:106)
  at org.springframework.data.jdbc.core.convert.EntityRowMapper.mapRow(EntityRowMapper.java:82)
  at org.springframework.data.jdbc.repository.support.JdbcQueryLookupStrategy$PostProcessingRowMapper.mapRow(JdbcQueryLookupStrategy.java:335)
  at org.springframework.data.jdbc.repository.query.AbstractJdbcQuery$ConvertingRowMapper.mapRow(AbstractJdbcQuery.java:196)
  at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:94)
  at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:61)
  at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:733)
  at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:658)
  at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:723)
  at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:748)
  at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.query(NamedParameterJdbcTemplate.java:178)
  at org.springframework.data.jdbc.repository.query.AbstractJdbcQuery.lambda$createSingleReadingQueryExecution$3(AbstractJdbcQuery.java:165)
  at org.springframework.data.jdbc.repository.query.StringBasedJdbcQuery.execute(StringBasedJdbcQuery.java:145)
  at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:170)
  at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:158)
  at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:164)
  at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
  at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
  at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:385)
  at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
  at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
  at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
  at org.springframework.data.repository.core.support.MethodInvocationValidator.invoke(MethodInvocationValidator.java:95)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
  at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:249)
  at jdk.proxy3/jdk.proxy3.$Proxy116.getExtendedEntities(Unknown Source)
  at com.example.demo.entity.RepoTests.selectExtended(RepoTests.kt:15)

On 3.1.5 this case worked. Shared test project with failing scenario.

Versions:
Kotlin 1.9.20
JVM Target 21
Spring Data JDBC 3.2.0

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Nov 27, 2023
@mp911de mp911de added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels Nov 27, 2023
@schauder
Copy link
Contributor

schauder commented Dec 4, 2023

Thanks for reporting this. This is a bug triggered when there are multiple levels of embedded entities, with @Embedded.Nullable annotation and we need to check the presence of a lower level in order to decide if we need to load the upper level.

schauder added a commit that referenced this issue Dec 5, 2023
@mp911de mp911de changed the title NullPointerException during building custom response object in crud repository NullPointerException when loading deeply nested embedded entities Dec 5, 2023
mp911de pushed a commit that referenced this issue Dec 13, 2023
mp911de added a commit that referenced this issue Dec 13, 2023
Also, avoid falling back into hasValue(…) code path.

See #1676
Original pull request: #1685
mp911de added a commit that referenced this issue Dec 13, 2023
Also, avoid falling back into hasValue(…) code path.

See #1676
Original pull request: #1685
@mp911de mp911de added this to the 3.2.1 (2023.1.1) milestone Dec 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants