Objectify error "You cannot create a key for an object with a null value @Id" in JUnit

I had the following error while testing a simple piece of code in JUnit that creates a User object (Objectify Entity) and then tries to bind it as a parent to another Objectify called DownloadTask :

 java.lang.IllegalArgumentException: You cannot create a Key for an object with a null @Id. Object was com.netbase.followerdownloader.model.User@57fcbecc at com.googlecode.objectify.impl.KeyMetadata.getRawKey(KeyMetadata.java:185) at com.googlecode.objectify.impl.Keys.rawKeyOf(Keys.java:35) at com.googlecode.objectify.impl.Keys.keyOf(Keys.java:28) at com.googlecode.objectify.Key.create(Key.java:62) at com.googlecode.objectify.Ref.create(Ref.java:31) at com.netbase.followerdownloader.repository.DownloadTaskRepositoryImpl.create(DownloadTaskRepositoryImpl.java:35) at com.netbase.followerdownloader.repository.DownloadTaskRepositoryImplTest.setUp(DownloadTaskRepositoryImplTest.java:37) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24) at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) at org.junit.runners.ParentRunner.run(ParentRunner.java:309) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) 

Here essentially the code looks:

 public class DownloadTaskRepositoryImplTest { // maximum eventual consistency (see https://cloud.google.com/appengine/docs/java/tools/localunittesting) private final LocalServiceTestHelper helper = new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig() .setDefaultHighRepJobPolicyUnappliedJobPercentage(100)); private DownloadTaskRepositoryImpl downloadTaskRepositoryImpl; @Before public void setUp() { helper.setUp(); DatastoreServiceFactory.getDatastoreService(); (new ObjectifyRegistrar()).registerDataModel(); UserRepositoryImpl userRepositoryImpl = new UserRepositoryImpl(); User user = userRepositoryImpl.create("user1"); downloadTaskRepositoryImpl = new DownloadTaskRepositoryImpl(userRepositoryImpl); downloadTaskRepositoryImpl.create(user, DownloadTask.DownloadType.Followers, "tess__terr"); // THIS LINE GETS THE EXCEPTION } 

Here is the corresponding bit from DownloadTaskRepositoryImpl:

 @Override public DownloadTask create(User user, DownloadTask.DownloadType downloadType, String screenname) { DownloadTask downloadTask = new DownloadTask(downloadType, screenname); downloadTask.owner = Ref.create(user); save(downloadTask); return downloadTask; } private void save(DownloadTask downloadTask) { Closeable closeable = begin(); ofy().save().entity(downloadTask); closeable.close(); } 

Initially, I thought the problem was that I did not configure LocalServiceTestHelper . But I got an error, even after I added it.

Then I thought the problem was that I used 100% possible consistency, but I set it to 0% possible sequence, and I still had the problem.

Then I thought that this happened because I did not complete the transaction by calling closeable.close(); before creating the dependent object (i.e. DownloadTask ). So I tried to combine the creation of an object into my own transaction; it did not work. I tried PLUS to decrease to 0% in the end, and that didn't work either.

For testing purposes, I can simply force User.id to 1L and satisfy Objecitfy, thereby circumventing the problem.

But why not install @Id from User as soon as I save User ?

** EDIT **

Here is my User class; note that id is Long , not Long :

 import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Id; @Entity public class User { @Id public Long id; public String twitterScreenName; public String email; public User () { } public User (String twitterScreenName) { this.twitterScreenName = twitterScreenName; } } 
+1
source share
2 answers

This is most likely caused by persistence asynchronously, rather than synchronously:

Be careful when saving objects with the Long @Id auto-generated field. Synchronous save () will populate the generated identifier value per instance of the object. Asynchronous save () will not; the operation will be until the async operation is completed.

Source: https://code.google.com/p/objectify-appengine/wiki/BasicOperations#Saving

It should fix the problem to change the save() method

From this:

 private void save(DownloadTask downloadTask) { Closeable closeable = begin(); ofy().save().entity(downloadTask); closeable.close(); } 

For this:

 private void save(DownloadTask downloadTask) { Closeable closeable = begin(); ofy().save().entity(downloadTask).now(); // Added .now() closeable.close(); } 

This fixed the problem for me in a similar situation when I tested Junit in a data model with an attached object model.

+1
source

What is the type of 'id' in the User class? If it is "Long", keeping id = null should work. From what you explained, it looks like β€œlong” (primitive). if this happens, change it to "Long" and try again.

0
source

Source: https://habr.com/ru/post/1213761/


All Articles