spring-projects / spring-data-couchbase

Provides support to increase developer productivity in Java when using Couchbase. Uses familiar Spring concepts such as a template classes for core API usage and lightweight repository style data access.
https://spring.io/projects/spring-data-couchbase
Apache License 2.0
277 stars 191 forks source link

Spring boot: 3.2.0, Spring data Couchbase: 5.2.0: java.lang.IllegalArgumentException: Attribute of type java.util.Collections.SingletonList cannot be stored and must be converted. #1875

Closed ilangranet closed 11 months ago

ilangranet commented 11 months ago

Hi,

I am using Spring boot: 3.2.0 with Spring data Couchbase: 5.2.0

I have an entity defined with a field of type Object (this field can be everything (String or List...)):

public class Person {
    @NotNull
    @Id
    private String id;

    @NotNull
    @Field
    private String name;

    @Field
    private int age;

    @Field
    private Object myObject;
}

while trying to save it using this: personRepository.save(new Person("1", "Alice", 25, Collections.singletonList("my issue")));

It failed on java.lang.IllegalArgumentException: Attribute of type java.util.Collections.SingletonList cannot be stored and must be converted.

it was working with previous version: Spring boot: 2.7.13, Spring data Couchbase: 4.4.13. demo.zip

Find attached the project where it failed: to reproduce, run PersonRepositoryTest

Any suggestion?

Thank you

mikereiche commented 11 months ago

This should work and I'll fix it. In the meantime, you might try a JsonArray instead of a List

ilangranet commented 11 months ago

This should work and I'll fix it. In the meantime, you might try a JsonArray instead of a List

I tried and get the same issue: java.lang.IllegalArgumentException: Attribute of type com.couchbase.client.java.json.JsonArray cannot be stored and must be converted.

mikereiche commented 11 months ago

I haven't investigated this yet, but this is more likely the issue. How is Object supposed to be serialized? Deserialized? What constructor should be used?

@Field
private Object myObject;
roman-sinyakov commented 11 months ago

@mikereiche this is a regression in v5. Could you make it work like it used to be in v4 ?

mikereiche commented 11 months ago

The issue is that conversion.isSimpleType() considers Object to be a simple type, while the

MappingCouchbaseConverter

if (!conversions.isSimpleType(prop.getType())) {
    writePropertyInternal(propertyObj, target, prop, accessor);
} else {
    writeSimpleInternal(prop, accessor, target, prop.getFieldName());
}

While verifyValue (called from target.put() called from writeSimpleInternal()) does not consider it to be a simpletype.
CouchbaseDocument

private void verifyValueType(final Object value) {
    if (value == null) {
        return;
    }
    final Class<?> clazz = value.getClass();
    if (CouchbaseSimpleTypes.DOCUMENT_TYPES.isSimpleType(clazz)) {
        return;
    }
    throw new IllegalArgumentException(
            "Attribute of type " + clazz.getCanonicalName() + " cannot be stored and must be converted.");
}
mikereiche commented 11 months ago

@roman-sinyakov

this is a regression in v5. Could you make it work like it used to be in v4 ?

I can make the serialization (save) work. But it appears that the deserialization (findById etc), did not work in v4 and won't work in v5 without other changes.

The breaking change is in MappingCouchbaseConverter.java. old line 579 vs new line 616 - propertyObj.getClass() (the object) vs prop.getType() (the field on the class)

https://github.com/spring-projects/spring-data-couchbase/commit/deca2465e0a64faa4f698fbe1853dfee418ed9df#diff-ed94230cdca258107738571365038a574bfe8d12f6b1fef0dea3ca4f6b19507fL579

mikereiche commented 11 months ago

@roman-sinyakov @ilangranet - are you ok with saving working, but reading (still) not working?

ilangranet commented 11 months ago

@roman-sinyakov @ilangranet - are you ok with saving working, but reading (still) not working?

with spring data 4.x, save and read were working. we still need both.

mikereiche commented 11 months ago

I thought I tried reading with 4.x and it didn't work. I'll look again

ilangranet commented 11 months ago

I tried with spring boot 2.7.13, which brings

<dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-couchbase</artifactId>
      <version>4.4.13</version>
      <scope>compile</scope>
</dependency>

PersonRepositoryTest.java.zip

it worked both save and read.

mikereiche commented 11 months ago

Thanks. I'll investigate on Monday.

mikereiche commented 11 months ago

@ilangranet - I can't get the read to work in 4.4.x or in anything else. Since the field type is Object, it doesn't have a componentType (line 766). So when readCollection() is called at line 776 with componentType==null, it throws an exception because it does not accept a null targetType.

https://github.com/spring-projects/spring-data-couchbase/blob/061f7ab8ed530ecd9337b7d56bfcabba194729f6/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java#L755:L783

korkutkose commented 9 months ago

@mikereiche Hi, I've just run across to this issue on starter-data-couchbase 3.2.2 with data-couchbase 5.2.2. I see that this has been merged to 5.2.x and main branch already. When will it be shipped, any plans? Any workarounds you'd suggest until that time rather than rolling back? Thanks.

mikereiche commented 9 months ago

Tomorrow. https://calendar.spring.io/