Skip to content

Problem deserializing EmbeddedId after reastart #897

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

Closed
tkuprevich opened this issue Oct 10, 2019 · 4 comments
Closed

Problem deserializing EmbeddedId after reastart #897

tkuprevich opened this issue Oct 10, 2019 · 4 comments

Comments

@tkuprevich
Copy link

We have an entity having @OneToMany relation to another entity having composite key mapped as @EmbeddedId.

@TypeName("Agreement")
class Agreement {
        @Id
        private UUID agreementId;

        private UUID locationId;

        @OneToMany(mappedBy = "agreement", cascade = CascadeType.ALL, orphanRemoval = true)
        @ShallowReference
        private List<AgreementMember> agreementMembers;

        //other fields ...
 }

Many side:

@TypeName("AgreementMember")
class AgreementMember {

     public AgreementMemberId getId() {
         return agreementMemberId;
     }

     @Embeddable
     @TypeName("AgreementMemberId")
     public static class AgreementMemberId implements Serializable {
         private UUID agreementId;
         private UUID memberId;

         UUID getAgreementId() {
             return agreementId
         }

         void setAgreementId(UUID agreementId) {
             this.agreementId = agreementId
         }

         UUID getMemberId() {
             return memberId
         }

         void setMemberId(UUID memberId) {
             this.memberId = memberId
         }
     }

     @EmbeddedId
     private AgreementMemberId agreementMemberId;

     //other fields ...
}

And also we have registered TypeAdapter for EmbeddedId:

class AgreementMemberIdTypeAdapter extends TypeAdapter<AgreementMember.AgreementMemberId> {

        @Override
        public void write(JsonWriter jsonWriter, AgreementMember.AgreementMemberId agreementMemberId) throws IOException {
            if (agreementMemberId != null)//todo
            {
                jsonWriter.beginObject();
                if (agreementMemberId.getAgreementId() != null) {
                    jsonWriter.name("agreementId").value(agreementMemberId.getAgreementId().toString());
                }
                if (agreementMemberId.getMemberId() != null) {
                    jsonWriter.name("memberId").value(agreementMemberId.getMemberId().toString());
                }
                jsonWriter.endObject();
            }
        }

        @Override
        public AgreementMember.AgreementMemberId read(JsonReader jsonReader) throws IOException {
            AgreementMember.AgreementMemberId agreementMemberId = new AgreementMember.AgreementMemberId();

            jsonReader.beginObject();
            while (jsonReader.hasNext()) {
                switch (jsonReader.nextName()) {
                    case "agreementId":
                        agreementMemberId.setAgreementId(UUID.fromString(jsonReader.nextString()));
                        break;
                    case "memberId":
                        agreementMemberId.setMemberId(UUID.fromString(jsonReader.nextString()));
                        break;
                    default:
                        break;
                }
            }
            jsonReader.endObject();

            return agreementMemberId;
        }
    }

We read snapshots using id and typeName for parent entity;

JqlQuery query = QueryBuilder.byInstanceId(agreement.agreementId, "Agreement").build()

And everything is read correctly until we restart the server. After that the collection of embedded ids comes row (doesn't get deserialized). And managedType references java.lang.Object as baseJavaClass instead of expected Agreement user class in downloaded snapshots.

If we create a query like QueryBuilder.byInstanceId(agreement.agreementId, Agreement.class) everything works correctly before and after restart.

Please see the test case demonstrating the problem: #896
And related stackoverflow discussion: https://stackoverflow.com/questions/58269576/javers-is-it-possible-to-get-cdosnapshotserialized-instance

@bartoszwalacik
Copy link
Member

bartoszwalacik commented Nov 3, 2019

@tkuprevich there is a lot of noise in your test. I did the cleanup here:
https://github.com/javers/javers/pull/909/files

In the first read, in snapshots1.get(0).state.getPropertyValue("agreementMembers")[0] you get InstanceId, which is right because you are using ShallowReference here.

In the second read, in snapshots2.get(0).state.getPropertyValue("agreementMembers")[0] you get badly deserialized InstanceId, I admit. Can you confirm that this is your case?

@bartoszwalacik
Copy link
Member

This is a documentation issue and not a bug in javers :)

In 5.8.4 I have added the warning when @TypeName is used on a class without declaring its package name in packagesToScan. Docs for @TypeName and JaversBuilder.withPackagesToScan() are updated.

07:12:58.253 [main] WARN  o.j.core.metamodel.type.UnknownType - Missing class definition with @TypeName 'Agreement', 
cant't properly deserialize its Snapshots from JaversRepository.
To fix this issue provide the fully-qualified package name of the class named 'Agreement' in the packagesToScan property.

@tomekstankowski
Copy link

It's worth to mention that classes annotated with @TypeName must be public.

@bartoszwalacik
Copy link
Member

@tomekstankowski feel free to contribute a PR that would add this info

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants