Closed johnda98 closed 3 years ago
It would help to see code and stack traces.
here's the entity
package org.crob;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.mapping.experimental.MorphiaReference;
import org.bson.types.ObjectId;
import java.util.List;
@Entity("dataholons")
public class DataHolon {
@Id
private ObjectId id;
private String name;
private String description;
private MorphiaReference<List<DataHolon>> references;
private MorphiaReference<List<DataHolon>> referencedBy;
private MorphiaReference<List<DataHolonType>> isaclassificationOf;
private MorphiaReference<List<Interface>> interactsWith;
private MorphiaReference<List<DataHolonProperty>> consistsOf;
private MorphiaReference<List<Scene>> usedIn;
DataHolon() { }
DataHolon(final String name, final String description){
this.name = name;
this.description = description;
}
public ObjectId getId() {
return id;
}
public String getName(){
return name;
}
public String getDescription(){
return description;
}
// References to/ by - recursive
public List<DataHolon> getReferences() { return references.get();}
public List<DataHolon> getReferencedBy() {return referencedBy.get(); }
// get Data holontype
public List<DataHolonType> getIsclassificationOf() {
return isaclassificationOf.get();
}
// get uses interfaces(apis)
public List<Interface> getInteractsWith() {return interactsWith.get(); }
// get data holon properties
public List<DataHolonProperty> getConsistsOf() {return consistsOf.get(); }
// get the Scenes that its used in
public List<Scene> getUsedIn() {return usedIn.get(); }
// Setters
public void setName(final String name){
this.name = name;
}
public void setDescription(final String description){
this.description = description;
}
public void setReferences(final List<DataHolon> references) {
this.references = MorphiaReference.wrap(references);
}
public void setReferencedBy(final List<DataHolon> referencedBy) {
this.referencedBy = MorphiaReference.wrap(referencedBy);
}
public void setIsclassificationOf(final List<DataHolonType> isaclassificationOf) {
this.isaclassificationOf = MorphiaReference.wrap(isaclassificationOf);
}
public void setInteractsWith(final List<Interface> interactsWith) {
this.interactsWith = MorphiaReference.wrap(interactsWith);
}
public void setConsistsOf(final List<DataHolonProperty> consistsOf) {
this.consistsOf = MorphiaReference.wrap(consistsOf);
}
public void setUsedIn(final List<Scene> usedIn) {
this.usedIn = MorphiaReference.wrap(usedIn);
}
// toString
@Override
public String toString() {
return "DataHolon [id=" + id + ", name=" + name + ", description=" + description + "]";
}
}
the controller code snippet
for (Interface interfaces : interfacesofholon) {
dholondescfull.appendText( interfaces.getName() + " " + interfaces.getDescription() + " " + interfaces.getApiname() + "\n");
dholondescfull.appendText(" \n");
dholondescfull.appendText("Which is sourced from .. ");
DataSource source = interfaces.getIsaccessFor();
dholondescfull.appendText(source.getName() + " " + source.getDescription() + "\n");
dholondescfull.appendText( " \n");
}
// Morphia null pointer - optionality of rel handling ??
List<Scene> inscenes = eg1holon.getUsedIn();
dholondescfull.appendText("This Data Holon is included in Scenes .. \n");
dholondescfull.appendText(" \n");
basically the dataholon entity is created without a 'scene' entity.. because that relationship is optional .. basic modelling.. all good so far.
so data holon 2 NYC1 was created without having a scene entity... but when I come to read it .. it trips over .. obviously because the List is not there ie the Array in mongo.. but surely Morphia should return null or for coder or indeed, Morphia, to be able to 'detect' if the scene Array is not in mongo BEFORE it trips w null pointer
Starting DataStore on DB: modsimv1
[DataHolon [id=5f77a307e74f033e954fa440, name=NYC Marshes - Future potential: CRE., description=Maps of NY Marshlands, with Ground Penetrating Radar & Salt Ingress timeseries], DataHolon [id=5f875ee96c28101dc2104b15, name=NYC1, description=NYC working copy]]
Oct 15, 2020 12:56:42 PM javafx.scene.CssStyleHelper calculateValue
WARNING: Caught 'java.lang.ClassCastException: class java.lang.Double cannot be cast to class javafx.scene.paint.Paint (java.lang.Double is in module java.base of loader 'bootstrap'; javafx.scene.paint.Paint is in module javafx.graphics of loader 'app')' while converting value for '-fx-background-color' from inline style on AnchorPane[id=AnchorPane, styleClass=root]
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1787)
at javafx.fxml/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670)
at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3856)
at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1851)
at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2584)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:409)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:299)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:447)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:412)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:446)
at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(View.java:556)
at javafx.graphics/com.sun.glass.ui.View.notifyMouse(View.java:942)
at javafx.graphics/com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76)
at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at javafx.base/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:273)
at javafx.fxml/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83)
at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1784)
... 30 more
Caused by: java.lang.NullPointerException
at org.crob/org.crob.DataHolon.getUsedIn(DataHolon.java:71)
at org.crob/org.crob.PrimaryController.getholon(PrimaryController.java:974)
at org.crob/org.crob.PrimaryController.dhicon1clickAction(PrimaryController.java:876)
... 41 more
line 974 is .. of course fine with data holons with 'scene' entities .. but its an optional relationship 0 to * .. many in UML
// Morphia null pointer - optionality of rel handling ??
line 974 List
Was thinking over lunch there must be some morphia method in MorphiaReference that I can call to detect if that Reference 'usedIn exists or not in mongo and thereby skip the code and therefore skip the null pointer exception trip
Welcome any kind advisement.. it has to be something obvious .. as referential integrity and optionality is core to any RDBMs or ODBMs
or maybe it s aMorphiaReference annotation.. not much out there on google .. except a non-related null pointer morphia bug years ago.. and not many 2.0.1 example code snippets out there also .. some but they dont cover all cases
The problem isn't really a Morphia issue, as such. You're trying to dereference that MorphiaReference
field when it's null. The correct thing here is to do a null check before calling get()
. Since there is nothing in the returned document(s) when you query the database, Morphia does nothing to that field and so it retains the null
value given to it by the JVM when your instance was created.
Thanks Evan.. ok, I did explore that, will do so again and advise. so the ignoreMissing parm is for objects that are referenced but the document doesnt exist.. to handle broken integrity.
Ok nailed it.. i think.. your thoughts ? .. its procedural
got into Compass and added the Array and left it empty not null.
So.. in a Morphia datastore.save , the entity instance must have ALL MorphiaReference Lists set to empty.. in order to avoid NPEs when calling entity get methods to see if any optional collections exist or not.
soooo.. to set to Empty and not-null, prior to the Morphia .save .. all entity .set methods must be called with a 'new ArrayList<> ' instance to initialize
It would be nice if Morphia set(create) all MorphiaReference annotated Lists to empty if those Lists do not exist on mongo, for that instance, by default.. since there is no way of knowing(detecting) (from the App), that the List..( mongo Array ) is not there, since NPEs are thrown.. and yep poor practice to catch NPEs or to have a need to. :-) thoughts ?
You shouldn't get an NPE on a save if the reference is null. If you do see that, please file an issue and I'll fix that.
But, yes, ignoreMissing
is what to do when you have an active reference but the referenced document doesn't exist. I've debated init'ing those MorphiaReference
instance with "empty" values but it violates Morphia's general flow of serialization. If a field isn't found in the fetched document, Morphia doesn't even consider that field at all.
In general, you'd solve your get
problem something like this:
public List<Scene> getUsedIn() {
return usedIn != null ? usedIn.get() : emptyList();
}
Then you always get a legitimately iterable return value but because emptlyList()
returns an immutable List
, you couldn't accidentally add to that List
. Then you'd just need to provide an addScene()
that would do the appropriate null checks and initializations to create a populated MorphiaReference
. I'll update the docs with an example of this once I wrap up what I have going.
np .. will close.. yes solved by ALWAYS creating an empty array and save the entity.. even if no relationship to store in the List.. Morphia could possible do this under the covers for ALL entity saves.. and it may help any NPEs being avoided if a coder checks a List but its not there. No I have to get back to the Update issue. as no Docs or examples on that for 2.0.1.. well there is but the examples include depreacted methods
2.0.1 guys ..null pointer issues when trying to determine if Reference is not there (ie an optional relationship)
how are optional relationships determined ? .. If no link between entities then no say List appears in mongo.. which is ok.. but in the code Entity.getmethod() sends a null pointer.. even if I load the list with a null I cannot pickup the null as Java trips.
sooooo.. in morphia reference.. for optionality we create the List reference anyway but with an objectid say that is a meaningless object.. just so we can determine if the relationship is not there ie for that entity say it is optional
thoughts ?