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

Datastore#save throws NPE if the entity returns hashCode that depends on @Version field #801

Closed
rinfield opened this issue Jun 17, 2015 · 7 comments
Labels
Milestone

Comments

@rinfield
Copy link

I tested with Morphia v1.0.1 & Mongo Java Driver v3.0.2 on JDK 8u45.
NullPointerException occurs both on Windows & MacOS.

Morphia v0.105 works fine.

Repro code

package com.example;

import org.bson.types.*;
import org.mongodb.morphia.*;
import org.mongodb.morphia.annotations.*;

import com.mongodb.*;

public class MorphiaTest {

    public static void main(final String[] args) {
        final MongoClient mongo = new MongoClient();
        final Morphia morphia = new Morphia();
        morphia.mapPackage("com.example");
        final Datastore ds = morphia.createDatastore(mongo, "test_db");

        final Foo model = new Foo();
        model.data = "whatever";
        ds.save(model); // => throws NPE!
    }

    @Entity
    public static class Foo {
        @Id
        public ObjectId id;
        @Version
        public Long version;

        public String data;

        @Override
        public int hashCode() {
            final int dataHashCode = (data == null) ? 0 : data.hashCode();
            final int versionHashCode = (version == null) ? 0 : version
                    .hashCode();
            final int result = dataHashCode + versionHashCode; // THIS DOESN'T WORK
            // final int result = dataHashCode; // THIS WORKS
            System.out.println(result + " " + version);
            return result;
        }
    }
}

Stacktrace

Exception in thread "main" java.lang.NullPointerException
    at org.mongodb.morphia.DatastoreImpl.postSaveOperations(DatastoreImpl.java:1234)
    at org.mongodb.morphia.DatastoreImpl.postSaveOperations(DatastoreImpl.java:1223)
    at org.mongodb.morphia.DatastoreImpl.save(DatastoreImpl.java:1010)
    at org.mongodb.morphia.DatastoreImpl.save(DatastoreImpl.java:1080)
    at org.mongodb.morphia.DatastoreImpl.save(DatastoreImpl.java:1063)
    at com.example.MorphiaTest.main(MorphiaTest.java:19)
@evanchooly
Copy link
Member

OK. This isn't really a Morphia issue. Here's what's going on. As your entity makes its way through the persistence logic, it's put in to a Map to track which entities morphia has seen so far. When it gets put in to the Map, it has a hash based on the data field since there is no version yet. By the time you get that NPE, the version field has been set and so results in a different hash value. By contract, Map key/hash values must be immutable. When the hash value changes, look ups no longer find the entity in the map and so comes back null. If you want to fix this in your application code, you need to include that version with something like this:

final int versionHashCode = (id == null || version == null) ? 0 : version.hashCode();

This will at least ignore the version code when it's first saved. This however will have the same problem with subsequent saves because the hash value changes midprocess which is a bug in hashCode().

There's not a whole lot that Morphia can do if the hash value keeps changing from underneath it. I hope this helps.

@evanchooly
Copy link
Member

waiting for last known working version.

@rinfield
Copy link
Author

rinfield commented Jul 2, 2015

Hi, Here are the results.

1.0.1: FAIL(NPE)
1.0.0: FAIL(NPE)
1.0.0-rc1: FAIL(NPE)
1.0.0-rc0: FAIL(NPE)
0.111: Success
0.110: Success
0.109: Success
0.108: Success
0.107: Success
0.106: Success
0.105: Success

@evanchooly
Copy link
Member

Perfect. Thanks. I'll dig into it.
On Jul 1, 2015 9:36 PM, "Ryo Uchino" [email protected] wrote:

Hi, Here are the results.

1.0.1: FAIL(NPE)
1.0.0: FAIL(NPE)
1.0.0-rc1: FAIL(NPE)
1.0.0-rc0: FAIL(NPE)
0.111: Success
0.110: Success
0.109: Success
0.108: Success
0.107: Success
0.106: Success
0.105: Success


Reply to this email directly or view it on GitHub
#801 (comment).

@evanchooly
Copy link
Member

Probable errant commit: 182ed32

@evanchooly evanchooly added this to the 1.1.0 milestone Jul 6, 2015
@evanchooly
Copy link
Member

I tracked it down. The code that extracted the generated _id value had been moved around in the lifecycle causing problems with how the test code was deciding when to include the version in the hashcode. I consolidated the code that pulled the new version number and the new _id value to be set "simultaneously" so that this scenario works again.

@rinfield
Copy link
Author

rinfield commented Jul 7, 2015

I confirmed the fix. Thanks Justin:smile:

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

No branches or pull requests

2 participants