GoogleCloudPlatform / spring-cloud-gcp

New home for Spring Cloud GCP development starting with version 2.0.
Apache License 2.0
401 stars 295 forks source link

Saving a child referenced via @LazyReference DatastoreDataException #1203

Open mmihira opened 1 year ago

mmihira commented 1 year ago

Describe the bug

Given Parent references a Child via @LazyReference.

Retrieve the parent, then access the child. Make a modification to only the child and then attempt to save the child. An error results:

error on save here: com.google.cloud.spring.data.datastore.core.mapping.DatastoreDataException: Cloud Datastore can only allocate IDs for Long and Key properties. Cannot allocate for type: class java.lang.String

Sample https://github.com/mmihira/spring-datastore-reference-example

The test is below.

    // create parent with child
    Child child = Child.builder().id(UUID.randomUUID().toString()).value("childFoo").build();
    Parent parent = Parent.builder().id(UUID.randomUUID().toString()).value("parentFoo").child(child).build();
    Parent savedParent = parentRepository.save(parent);

    // retrieve the saved parent
    Parent retrievedParent = parentRepository.findById(savedParent.getId()).get();
    assertThat(retrievedParent.getValue()).isEqualTo("parentFoo");
    assertThat(retrievedParent.getChild().getValue()).isEqualTo("childFoo");

    // try saving the child (LazyReference)
    // error on save here: com.google.cloud.spring.data.datastore.core.mapping.DatastoreDataException: Cloud Datastore can only allocate IDs for Long and Key properties.
    // Cannot allocate for type: class java.lang.String
    Child retrievedChild = retrievedParent.getChild();
    retrievedChild.setValue("bar");
    childRepository.save(retrievedChild);
zhumin8 commented 1 year ago

Thanks for reporting this, I was able to reproduce this issue and it looks like to me that writing/updating an entity that's lazy referenced is not supported in the code base as of now. We'll look into this bug a bit more. In the meantime, a less ideal temporary workaround is to do a copy of this retrievedChild and saving this copy instead. Something similar to this for your sample code:

        Child childToSave = Child.builder().id(retrievedChild.getId()).value("updated child").build();
        childRepository.save(childToSave);
mmihira commented 1 year ago

Thanks for looking into it.