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

Codec not found for requested operation using a Set of mapped User Defined Type #1473

Closed
soheilrahsaz opened this issue Feb 3, 2024 · 3 comments
Labels
type: bug A general bug

Comments

@soheilrahsaz
Copy link

soheilrahsaz commented Feb 3, 2024

Hi
I have test application working with Cassandra repositories.
When I try to write a custom query and a partial update on a column which it's type is a collection of UDT, I get this error:

org.springframework.data.cassandra.CassandraUncategorizedException: Query; CQL [UPDATE vet SET specialities = ? WHERE id = ?]; Codec not found for requested operation: [UDT(spring_cassandra.speciality) <-> com.example.cassandratest.entity.Speciality]
at org.springframework.data.cassandra.core.cql.CassandraExceptionTranslator.translate(CassandraExceptionTranslator.java:162) ~[spring-data-cassandra-4.2.0.jar:4.2.0]
at org.springframework.data.cassandra.core.cql.CassandraAccessor.translate(CassandraAccessor.java:421) ~[spring-data-cassandra-4.2.0.jar:4.2.0]
at org.springframework.data.cassandra.core.cql.CqlTemplate.translateException(CqlTemplate.java:551) ~[spring-data-cassandra-4.2.0.jar:4.2.0]
at org.springframework.data.cassandra.core.cql.CqlTemplate.query(CqlTemplate.java:409) ~[spring-data-cassandra-4.2.0.jar:4.2.0]
at org.springframework.data.cassandra.core.cql.CqlTemplate.query(CqlTemplate.java:424) ~[spring-data-cassandra-4.2.0.jar:4.2.0]
at org.springframework.data.cassandra.core.CassandraTemplate.doQuery(CassandraTemplate.java:852) ~[spring-data-cassandra-4.2.0.jar:4.2.0]
at org.springframework.data.cassandra.core.CassandraTemplate.select(CassandraTemplate.java:371) ~[spring-data-cassandra-4.2.0.jar:4.2.0]
at org.springframework.data.cassandra.repository.query.CassandraQueryExecution$SingleEntityExecution.execute(CassandraQueryExecution.java:182) ~[spring-data-cassandra-4.2.0.jar:4.2.0]
at org.springframework.data.cassandra.repository.query.CassandraQueryExecution$ResultProcessingExecution.execute(CassandraQueryExecution.java:270) ~[spring-data-cassandra-4.2.0.jar:4.2.0]
at org.springframework.data.cassandra.repository.query.AbstractCassandraQuery.execute(AbstractCassandraQuery.java:86) ~[spring-data-cassandra-4.2.0.jar:4.2.0]
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:170) ~[spring-data-commons-3.2.0.jar:3.2.0]
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:158) ~[spring-data-commons-3.2.0.jar:3.2.0]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:164) ~[spring-data-commons-3.2.0.jar:3.2.0]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143) ~[spring-data-commons-3.2.0.jar:3.2.0]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.1.jar:6.1.1]
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:70) ~[spring-data-commons-3.2.0.jar:3.2.0]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.1.jar:6.1.1]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-6.1.1.jar:6.1.1]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.1.jar:6.1.1]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:249) ~[spring-aop-6.1.1.jar:6.1.1]
at jdk.proxy2/jdk.proxy2.$Proxy79.updateSpecialities(Unknown Source) ~[na:na]
at com.example.cassandratest.CassandraTestApplication.run(CassandraTestApplication.java:25) ~[classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:786) ~[spring-boot-3.2.0.jar:3.2.0]
... 14 common frames omitted
Caused by: com.datastax.oss.driver.api.core.type.codec.CodecNotFoundException: Codec not found for requested operation: [UDT(spring_cassandra.speciality) <-> com.example.cassandratest.entity.Speciality]
at com.datastax.oss.driver.internal.core.type.codec.registry.CachingCodecRegistry.createCodec(CachingCodecRegistry.java:656) ~[java-driver-core-4.17.0.jar:na]
at com.datastax.oss.driver.internal.core.type.codec.registry.DefaultCodecRegistry$1.load(DefaultCodecRegistry.java:95) ~[java-driver-core-4.17.0.jar:na]
at com.datastax.oss.driver.internal.core.type.codec.registry.DefaultCodecRegistry$1.load(DefaultCodecRegistry.java:92) ~[java-driver-core-4.17.0.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3527) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2276) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2154) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache$Segment.get(LocalCache.java:2044) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache.get(LocalCache.java:3951) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache.getOrLoad(LocalCache.java:3973) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4957) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:4963) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.internal.core.type.codec.registry.DefaultCodecRegistry.getCachedCodec(DefaultCodecRegistry.java:117) ~[java-driver-core-4.17.0.jar:na]
at com.datastax.oss.driver.internal.core.type.codec.registry.CachingCodecRegistry.codecFor(CachingCodecRegistry.java:190) ~[java-driver-core-4.17.0.jar:na]
at com.datastax.oss.driver.internal.core.type.codec.registry.CachingCodecRegistry.getElementCodecForCqlAndJavaType(CachingCodecRegistry.java:587) ~[java-driver-core-4.17.0.jar:na]
at com.datastax.oss.driver.internal.core.type.codec.registry.CachingCodecRegistry.createCodec(CachingCodecRegistry.java:619) ~[java-driver-core-4.17.0.jar:na]
at com.datastax.oss.driver.internal.core.type.codec.registry.DefaultCodecRegistry$1.load(DefaultCodecRegistry.java:95) ~[java-driver-core-4.17.0.jar:na]
at com.datastax.oss.driver.internal.core.type.codec.registry.DefaultCodecRegistry$1.load(DefaultCodecRegistry.java:92) ~[java-driver-core-4.17.0.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3527) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2276) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2154) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache$Segment.get(LocalCache.java:2044) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache.get(LocalCache.java:3951) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache.getOrLoad(LocalCache.java:3973) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4957) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.shaded.guava.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:4963) ~[java-driver-shaded-guava-25.1-jre-graal-sub-1.jar:na]
at com.datastax.oss.driver.internal.core.type.codec.registry.DefaultCodecRegistry.getCachedCodec(DefaultCodecRegistry.java:117) ~[java-driver-core-4.17.0.jar:na]
at com.datastax.oss.driver.internal.core.type.codec.registry.CachingCodecRegistry.codecFor(CachingCodecRegistry.java:252) ~[java-driver-core-4.17.0.jar:na]
at com.datastax.oss.driver.internal.core.data.ValuesHelper.encodePreparedValues(ValuesHelper.java:112) ~[java-driver-core-4.17.0.jar:na]
at com.datastax.oss.driver.internal.core.cql.DefaultPreparedStatement.boundStatementBuilder(DefaultPreparedStatement.java:187) ~[java-driver-core-4.17.0.jar:na]
at org.springframework.data.cassandra.core.PreparedStatementDelegate.bind(PreparedStatementDelegate.java:59) ~[spring-data-cassandra-4.2.0.jar:4.2.0]
at org.springframework.data.cassandra.core.CassandraTemplate$PreparedStatementHandler.bindValues(CassandraTemplate.java:1004) ~[spring-data-cassandra-4.2.0.jar:4.2.0]
at org.springframework.data.cassandra.core.cql.CqlTemplate.query(CqlTemplate.java:402) ~[spring-data-cassandra-4.2.0.jar:4.2.0]
... 33 common frames omitted

Here is my entity:

@Table
public class Vet {

    @PrimaryKey
    private UUID id;

    private String firstName;

    private String lastName;

    private Set<Speciality> specialities;

}

@UserDefinedType
public class Speciality {
    private String name;
}

public interface VetRepository extends CassandraRepository<Vet, UUID> {

    @Query("UPDATE vet SET specialities = :specialities WHERE id = :id")
    void updateSpecialities(UUID id, Set<Speciality> specialities);
}

 @Override
    public void run(String... args) throws Exception {
        vetRepository.updateSpecialities(UUID.randomUUID(), Set.of(new Speciality("Ophthalmology")));
    }

But if i dont put Speciality in a collection and update it using the same way, there is no problem.

Tested with spring boot 3.2.0 and spring-data-cassandra 4.2.0

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Feb 3, 2024
@mp911de
Copy link
Member

mp911de commented Feb 5, 2024

Care to provide the Table and UDT CQL definitions so we have a complete reproducer?

@mp911de mp911de added the status: waiting-for-feedback We need additional information before we can continue label Feb 5, 2024
@soheilrahsaz
Copy link
Author

Sure, I used spring.cassandra.schema-action=create_if_not_exists to create the schemas:

CREATE KEYSPACE spring_cassandra WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}
  AND durable_writes = true;

CREATE TYPE spring_cassandra.speciality (
    name text
);

CREATE TABLE spring_cassandra.vet (
    id uuid PRIMARY KEY,
    firstname text,
    lastname text,
    specialities set<frozen<speciality>>
)

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Feb 6, 2024
@mp911de mp911de added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged status: feedback-provided Feedback has been provided labels Feb 6, 2024
@mp911de mp911de changed the title Codec not found for requested operation, When updating collection of UDT in custom query Codec not found for requested operation using a Set of mapped User Defined Type Feb 6, 2024
mp911de added a commit that referenced this issue Feb 6, 2024
We now deeply introspect collection and map elements when obtaining write values to ensure proper UDT and tuple conversions.

Previously, collections containing UDT values did a pass-thru of values instead of applying UDT mapping.

Closes #1473
mp911de added a commit that referenced this issue Feb 6, 2024
We now deeply introspect collection and map elements when obtaining write values to ensure proper UDT and tuple conversions.

Previously, collections containing UDT values did a pass-thru of values instead of applying UDT mapping.

Closes #1473
@mp911de mp911de closed this as completed in 22c236c Feb 6, 2024
@mp911de
Copy link
Member

mp911de commented Feb 6, 2024

Thanks a lot. We were missing collection introspection of elements if the element type wasn't sufficient. That's fixed now.

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

No branches or pull requests

3 participants