Morphia / Mongo java driver 2.14 compatibility

378 views
Skip to first unread message

Daniel R

unread,
Apr 14, 2016, 7:42:49 AM4/14/16
to Morphia
Hello,

Confused dev here. We cannot upgrade to java driver 3.2 for the time being since it requires major refactoring of our current code base but we would like to upgrade java driver 2.14.2
so whats the recommended version of Morphia to go with it?

Current setup:
  • Morphia 0.111 + Mongo java driver 2.12.5
Tested combinations (just build)
  • Morphia 0.111 + Mongo java driver 2.14.2 : builds ok
  • Morphia 1.0.1 + Mongo java driver 2.14.2 : builds ok
  • Morphia 1.1.0 + Mongo java driver 2.14.2 : builds breaks on org.mongodb.morphia.Morphia class:
    • public <T> T fromDBObject(final Class<T> entityClass, final DBObject dbObject)  is now -->
    • public <T> T fromDBObject(final Datastore datastore, final Class<T> entityClass, final DBObject dbObject)

So is it ok to got with Morphia 1.0.1 + Mongo java driver 2.14.2, I haven't tested the application yet, just the build.


Thanks,


Daniel

Justin Lee

unread,
Apr 14, 2016, 9:15:09 AM4/14/16
to mor...@googlegroups.com
If it builds you *should* be fine but you should certainly run all your tests to be sure.

--------------------------------

name     : "Justin Lee", 
  title    : "Software Engineer",
  twitter  : "@evanchooly",
  web      : [ "mongodb.com", "antwerkz.com" ],
  location : "New York, NY" }

--
You received this message because you are subscribed to the Google Groups "Morphia" group.
To unsubscribe from this group and stop receiving emails from it, send an email to morphia+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jeff Yemin

unread,
Apr 14, 2016, 9:54:13 AM4/14/16
to Morphia
Hi Daniel,

Which of the incompatibilities between the 2.x and 3.x drivers are causing you the most pain and preventing an upgrade?


Regards,
Jeff

Daniel R

unread,
Apr 14, 2016, 12:04:49 PM4/14/16
to Morphia

Jeff, thanks for your interest.


We're are planning for a limited production release in a few months and the current MongoDB server used on beta-testing (real users/real data but limited numbers) turns out to be recently EOL'd as it happens (v2.4) so the idea is to go with something that does not break the code, is not too old and preferably go with MongoDB 3.2 server).

This is an edited version of a write up I made for the decision makers. Although long-ish it might help other users:

Existing stack at beta testing envs

  • Morphia 0.111 - Mongo Java Driver (MJD) 2.12 - MongoDB server (MDB) 2.4 (EOL'd).

Upgrade options

  1. Morphia 1.0.1 - MJD 2.12 - MDB 2.6 (EOL April 2017?)
  2. Morphia 1.0.1 - MJD 2.14 - MDB 2.6 (EOL April 2017?)
  3. Morphia 1.0.1 - MJD 2.14 - MDB 3.2
  4. Morphia 1.0.1 - MJD 3.2   - MDB 3.2
  5. Morphia 1.1.0 - MJD 2.14 - MDB 3.2

Option 1 & 2

Lowest risk but these will just buys us some time. See support policy.

Option 3

Builds OK and from what I've gathered it's doable as it supposed to be forward-compatible but just lacking the new v3.2 bells & whistles:

  • Mongo seems to have updated their MJD 2.x to 2.14 for this purpose, see r.2.14.0 note
    • It's recommended that users upgrade instead to the 3.2 Java driver when upgrading to the 3.2 server. The 2.14.0 driver is only being provided to assist users who are not yet in a position to upgrade to the 3.x Java driver series.
  • Java driver compatibility matrix indicates that driver 2.14 support server 3.2. See note: The 2.14 driver does not support all MongoDB 3.2 features (e.g., read concern); however, if you are currently on a version 2.x driver and would like to run against MongoDB 3.2 but cannot upgrade to driver version 3.2, use the 2.14 driver.
  • Morphia: the scenario is even more confusing so don't ask me for authoritative references because I couldn't find any. I had to actually dig in the Morphia repo to get and idea of the dependencies and locally do try & error build tests to see what builds OK.

Option 4

MJD 3.2 breaks the build and requires non-trivial refactoring for which we don't have time/resources at the moment. Some issues are:

  • public class EntityId<T> extends ObjectId
    • ObjectId has been made final so our EntityId class breaks and it's used all over the place. THIS IS THE BIGGEST PROBLEM
  • ObjectId.massageToObjectId(String) has been removed

Option 5

Morphia 1.1.0 breaks the build

  • Anything of version 1.1.0 upwards series seems to require MJD 3.x ??? and also breaks the build on class org.mongodb.morphia.Morphia
    • public <T> T fromDBObject(final Class<T> entityClass, final DBObject dbObject) is changed to –>
    • public <T> T fromDBObject(final Datastore datastore, final Class<T> entityClass, final DBObject dbObject) and then the javadoc for the new version goes on saying This method is primarily an internal method. Reliance on this method may break your application in future releases.

Daniel R

unread,
Apr 15, 2016, 3:48:42 AM4/15/16
to Morphia
Hi again Jeff,

One of my colleagues tracked down the offending commit on github:

https://github.com/mongodb/mongo-java-driver/commit/6fb1830a3dd42997efc4ed8068ec267c20a247be#diff-f7f229538ce532517ba2202d8aa1b244

What's the reason for making ObjectId final and is it possible to revert that in a future release?

Thanks,

Daniel


El jueves, 14 de abril de 2016, 15:54:13 (UTC+2), Jeff Yemin escribió:

Jeff Yemin

unread,
Apr 15, 2016, 9:47:40 AM4/15/16
to Morphia
Immutable value classes like ObjectId are generally final.  See for example Integer, Long, BigDecimal, etc.  

Can you explain your use case for sub-classing ObjectId? 


Regards,
Jeff

Daniel R

unread,
Apr 18, 2016, 3:33:44 AM4/18/16
to Morphia

Hello Jeff,


Thanks for you reply.  We have made our own type-safe ID class first and foremost to prevent usage errors and secondly to improve readability.

We send a lot of IDs between different services in the system so our code prevents from sending let's say, a User ID into a method that expects a Project ID.

So a method called:

void doStuff(EntityId<User> userId, EntityId<Project> projectId, EntityId<Task> taskId)

is IMHO far less error prone and more readable than its non type-safe equivalent:

void doStuff(ObjectId userId, ObjectId projectId, ObjectId taskId)


Especially since the latter would still work even if we, for example, switch userId with projectId ; it would just unknowingly return the wrongful data.

The fact that not everyone seems to make their IDs type-safe is kind of mystifying as in our case the alternative would be to unit-test everything just to check that IDs haven’t been accidentally swapped… a huge pain in the neck.


Regards,

Daniel

Daniel R

unread,
Apr 21, 2016, 4:02:59 AM4/21/16
to Morphia

Any comments/suggestions please?

Jeff Yemin

unread,
Apr 21, 2016, 9:44:16 AM4/21/16
to Morphia
Hi Daniel,

Is EntityId the class that is sub-classing ObjectId?   Can you show the full class definition and an example of how it's used within the context of Morphia?


Jeff

Daniel R

unread,
Apr 21, 2016, 11:25:00 AM4/21/16
to Morphia
Hi Jeff,

Yes, EntityId extends ObjectId. I have attached the following Java source files to clarify and illustrate our use case:
  • EntityId.java
  • EntityIdConverter.java, the converter that internally casts between ObjectId and EntityId
  • CWProject.java, one of our simple entity classes that have EntityId as id field.
As an example of how it is used is the removeAssignedRoleForTasks()from our TaskDAO for which I include an excerpt below:

import org.mongodb.morphia.query.Criteria;

import org.mongodb.morphia.query.Query;


public class TaskDAO extends AbstractDAO<CWTask> {

   public static final String PROJECT_ID_KEY = "projectId";

   public static final String ASSIGNEES_FIELD = "assignees";

   public static final String ASSIGNEES_ROLE_ID_KEY = "assignees.roleId";

...

   public void removeAssignedRoleForTasks(EntityId<CWProject> projectId, EntityId<CWRole> roleId) {

      Query<CWTask> q = q();

      applyProjectCriteria(q, projectId);

      applyAssignedToRoleCriteria(q, roleId);

      set(q, ASSIGNEES_FIELD, new ArrayList<>());

   }

...

   protected CriteriaContainer applyProjectCriteria(Query<CWTask> q, EntityId<CWProject> projectId) {

      if (projectId == null) {

         return q.or(

            q.criteria(PROJECT_ID_KEY).doesNotExist(),

            q.criteria(PROJECT_ID_KEY).equal(null)

        );

      } else {

         return q.criteria(PROJECT_ID_KEY).equal(projectId);

      }

   }

   ...

   protected CriteriaContainer applyAssignedToRoleCriteria(Query<CWTask> q, EntityId<CWRole> roleId) {

      return q.criteria(ASSIGNEES_ROLE_ID_KEY).hasThisOne(roleId);

   }


The set() invocation to parent class at the end of removeAssignedRoleForTasks()just creates the UpdateOperations, sets/unsets/adds and performs the update on the Morphia Datastore

So in this removeAssignedRoleForTasks() method it can be seen that the ID arguments cannot be passed in the wrong order thanks to the EntityId type safety. This is a real problem with plain ObjectIds

Please do not hesitate to ask for further clarifications.

Thank you very much for looking into this.

Daniel
EntityId.java
EntityIdConverter.java
CWProject.java

Daniel R

unread,
May 2, 2016, 4:03:48 AM5/2/16
to Morphia
Any feedback on this?

Daniel R

unread,
May 16, 2016, 2:47:45 AM5/16/16
to Morphia
Bump

Justin Lee

unread,
May 16, 2016, 8:30:00 AM5/16/16
to mor...@googlegroups.com
I can't really comment on ObjectId's finalness but if you want a workaround, two of them come to mind:
  1. Use composition rather inheritance.  Your local ID entity types would contain the ID value rather than extend.  You could fetch the actual value with a get().  e.g.:  public class EntityId<T> { private T t; public T get() { return t; }}.  You could then subclass and get your type safe compile checks like you've been doing.
  2. A similar approach that might be a bit more palatable is to use Morphia's Key class.  This does, essentially, what #1 is suggesting but
    • It's already done for you
    • Has Converter support already should you want to persist these entities as fields on another class (a reference essentially)

I know both of options involve a bit of refactoring but it's what comes to mind.


--------------------------------

name     : "Justin Lee", 
  title    : "Software Engineer",
  twitter  : "@evanchooly",
  web      : [ "mongodb.com", "antwerkz.com" ],
  location : "New York, NY" }

--
Reply all
Reply to author
Forward
0 new messages