I'm not sure if its the way Morphia is designed but here it goes...
Student.class (methods omitted)
#Entity(value = "students", noClassnameStored = true)
public class Student
{
#Id
private String id = new ObjectId().toString();
private String name;
private String city = "London"; // Default Value
}
NOTE: That I have assigned a DEFAULT value to Instance variable city.
Now the code...
Student s1 = new Student("James Bond"); // London IS his default city
ds.save(s1);
Student s2 = new Student("Jason Bourne");
s2.setCity("New York"); // Set a city for Mr. Bourne
ds.save(s2);
System.out.println("----- Normal -----");
List<Student> studentsNormal = ds.createQuery(Student.class).asList();
for(Student s: studentsNormal)
System.out.println(s);
System.out.println("----- Projected -----");
Query<Student> query = ds.find(Student.class);
query.retrievedFields(false, "city");
List<Student> studentsProjected = query.asList();
for(Student s: studentsProjected)
System.out.println(s);
And, now the output...
----- Normal -----
Student{id='57337553db4f0f0f10a93941', name='James Bond', city='London'}
Student{id='57337553db4f0f0f10a93942', name='Jason Bourne', city='New York'}
----- Projected -----
Student{id='57337553db4f0f0f10a93941', name='James Bond', city='London'}
Student{id='57337553db4f0f0f10a93942', name='Jason Bourne', city='London'}
Now, 4 questions about the Morphia behaviour...
Q1. For Mr. Bond, I did not change the city and excluded city from the projected list, yet the default value of the city is printed as London. Should'nt this be null?
Q2. For Mr. Bourne, I did change the city to New York but still during the projection, it picked up the default value and showed London. Should'nt this be null as well?
I havent yet seen the Projection.class from Morphia (intend to do so tonight) but it seems that for exclusions, Morphia seems to pick up the default value and does not over-ride with null. Now this becomes a hindrance since I do not have any way to provide default value to my Instance Variables without exposing them to client's...
Q3. Is this Morphia or a MongoD Java Driver behavior/issue?
And the final question...
Q4. Any known workaround for this?
I have googled around but did not come around any solution or explanation so far...
Thanks in advance...
SG
When Morphia is reading your documents from the query results, the first thing it does is create a new instance of your entity, Student. It just invokes the no argument constructor. There's no magic involved. The city field is initialized with a value. Once that's done, Morphia will take each key in the document returned from the database, find that mapped field, and set it. In your case, there is not city key in the document and so that field is never set by Morphia leaving the initialized value in place.
In general, initializing fields on entities like this is a bad practice. For every entity loaded from the database, the JVM has to initialize those fields to some value only to overwrite them later. In cases such as yours where certain fields don't come back in a query result, those values remain after Morphia returns the new instances back to your application.
Related
I can't wrap my head around how should i get my data without much boilerplate.
The problem:
I have a database that i cannot alter. Which has multiple field of same type almost same for example i have text_en and text_fr (both are the same word in different language English and French) and i got + 71 same field but different languages.
What I need is something like
#Entitiy(tableName = "blabla")
class X {
private String textTarget;
private String textMain;
...
}
How should I do my Dao interface to get desired language and map into x class
what should work is to update entity ColumnInfo(name ="text_en") for example.
#Query("select :main , :target from phrases where :id ")
List<X> getPhrase(String main,String target);
usage : getPhrase("text_en","text_esp");
// for example returning object X with field main = "hello" and target " holla")
The above example return the following error:
error: Not sure how to convert a Cursor to this method's return type
What you put in #Query is an SQL-statement, you can actually test them in sqlite command line utility or any desktop software to verify their correctness. So if you want a translation to the desired language, it should look like this:
#Query("SELECT :main AS text_main, :target AS text_target FROM `phrases` WHERE id = :id)
List<Translation> getTranslationById(String firstLang, String secondLang, long id);
Where Translation should be something like this:
class Translation {
#ColumnInfo("text_main")
String main;
#ColumnInfo("text_target")
String target;
//setters, getters, etc
}
This class is used only as a return value from the method.
Problem Statement:
Imagine a nested object like below:
class Company{
...
List<Department> departments;
}
class Department{
...
List<Employee> employees;
}
class Employee{
String name;
...
}
A company has many departments, and each department has many employees.
Json body is unmarshalled by a library, to create the Java object Company as shown above.
Suppose I had an employee with name:"John", I am looking for an api, which when I pass in the hash of Employee object or the attribute name returns a Path to that attribute.
search(Object attributeName, Object attributeValue) i.e search("name", "John") should return
company.departments[0].employees[5]
Is there a good open source library exposing similar api or what is the best method around to walk over a complex object graph
JSR 303 Hibernate Validator, which adds property path automatically to a ConstraintViolation doesn't expose the behavior on how it gets property path from complex object graphs via any object
Kindly advice if anyone has run through a similar need
I haven't seen a library that does exactly this, but you can modify the code from my object iterator blog to do this.
https://blog.stackhunter.com/2014/07/09/convert-java-objects-to-string-with-the-iterator-pattern/
The iterator navigates an object graph to produce output like the following, but you can make it do anything -- including searching for a key-value pair.
com.stackhunter.example.employee.Department#129719f4
deptId = 5775
employeeList = java.util.ArrayList#7037717a
employeeList[0] = com.stackhunter.example.employee.Employee#17a323c0
firstName = Bill
id = 111
lastName = Gates
employeeList[1] = com.stackhunter.example.employee.Employee#57801e5f
firstName = Howard
id = 222
lastName = Schultz
employeeList[2] = com.stackhunter.example.employee.Manager#1c4a1bda
budget = 75000.0
firstName = Jeff
id = 333
lastName = Bezos
name = Sales
[I#39df3255
object[0] = 111
object[1] = 222
object[2] = 333
Happy coding!
You can use SOJO (Simplified Old Java Objects).
According to theirs documentation i think PathRecordWalkerInterceptor is what you are search:
Car car = new Car("Ferrari");
ObjectGraphWalker walker = new ObjectGraphWalker();
PathRecordWalkerInterceptor interceptor = new PathRecordWalkerInterceptor();
walker.addInterceptor(interceptor);
walker.walk(car);
Map visitedPathes = interceptor.getAllRecordedPathes();
Person has a foreign key of the field "Language". How can I avoid loading the whole "Language" object if I just got the PK of a language ("json.get("language").asInt()) and want to insert a new Person?
#Entity
public class Person {
String prename;
String lastname;
Language language;
}
Person p = new Person("Robert", "Meyer");
Language l = JPA.em().find(Language.class, json.get("language").asInt();
p.setLanguage(l);
Is it possible to do it in a way without having to load the whole Language object?
You could use EntityManager#getReference().
Get an instance, whose state may be lazily fetched. If the requested instance does not exist in the database, the EntityNotFoundException is thrown when the instance state is first accessed.
Person p = new Person("Robert", "Meyer");
Language l = JPA.em().getReference(Language.class, json.get("language").asInt();
p.setLanguage(l);
I'm trying to delete an object from datastore but it's not deleting it. Code is given below:
MoIADocument moIADoc = new MoIADocument();
// update fields in moIADoc object
ds.save(moIADoc);
printDBDocs();
// create a query to retrieve objects
QueryResults<MoIADocument> foundResults = dao.find(query);
List<MoIADocument> list = foundResults.asList();
for (MoIADocument obj : list) {
ds.delete(obj);
MoDBIATopic topicBO = obj.getData().get(EnumChannelType.FACEBOOK).getData().get(GraphConstants.Topic_Default);
topicBO.setInfluence(topicBO.getInfluence() * 25);
ds.save(obj);
}
printDBDocs();
###########################
the result of first print (in JSON format) is:
###########################
obj in for loop is:
###########################
and after for loop the print statement gives two objects as:
Why is it not deleting old object by mongoConstants.ds.delete(obj);? And I have annotated id field ad #Id but still two objects are saved with same id. How is this possible? How can we force to overwrite an object if id is same?
#Id
#Indexed
private String id;
Note: id is the top level id as indicated in JSON.
And I tried using query as:
Query<MoIADocument> query1 = ds.createQuery(MoIADocument.class).disableValidation();
query1.field("id").equal(obj.getId());
ds.delete(query1);
This too not working as expected.
EDIT:
Problem looks like in query result. Using above query1, it returned zero results.
I tried multiple approaches but nothing worked as expected.. tried following ways:
Approach# 1:
Query<MoDBIADocument> query1 = dao.createQuery(MoDBIADocument.class).disableValidation();
query1.field("id").equal(moDoc.getId());
QueryResults<MoDBIADocument> foundResults1 = dao.find(query1);
printDBDocs(foundResults1);
Approach# 2: (using _id instead of id)
Query<MoDBIADocument> query1 = dao.createQuery(MoDBIADocument.class).disableValidation();
query1.field("_id").equal(moDoc.getId());
QueryResults<MoDBIADocument> foundResults1 = dao.find(query1);
printDBDocs(foundResults1);
Approach# 3:
using `ds` instead of `dao` in `Approach# 1` and `Approach# 2`
Approach# 4:
DBObject dbObj = new BasicDBObject("id", moDoc.getId());
DBCursor cursor = COLLECTION.find(dbObj);
None of above approaches worked.. I think problem is with using top level id field (somehow?)..
Using internal id (of FACEBOOK) worked as expected
query1.field("data.FACEBOOK.id").equal(str);
Problem was using a String id as #Id field. Actually it should be ObjectId id. WIth this change class become as below:
#Entity("Collection_IAGlobals")
public class MoDBIADocument {
#Id
#Indexed
private ObjectId id;
// some more fields
}
and updating as:
QueryResults<MoIADocument> foundResults = dao.find(query);
List<MoIADocument> list = foundResults.asList();
for (MoIADocument obj : list) {
//do modifications in obj
dao.save(obj);
}
this way there is no need to delete old object. It replaces old object with new one (since they are having same id).
Works fine using both ds and dao.
But now, new problem with setting id field. If I set using some string as:
moIADoc.setId(new ObjectId("123456"));
it is resulting into following error:
Exception in thread "main" java.lang.IllegalArgumentException: invalid ObjectId [123456]
at org.bson.types.ObjectId.<init>(ObjectId.java:128)
at org.bson.types.ObjectId.<init>(ObjectId.java:122)
It works fine any of other methods, like:
// don't set id field
//OR
//set using default constructor of ObjectId
moIADoc.setId(new ObjectId());
//OR use other constructors e.g.
moIADoc.setId(new ObjectId(new Date(), 123456));
So, the question is why ObjectId is not taking String argument?
In Grails/Gorm I am storing a physical site which holds objects at defined positions.
The domain class would simply be:
EXAMPLE A
SITE DC
sitename
positionmap (1: dog, 2: cat, 3:foo, 4:bar, ... up to an average of 30 items)
... but the objects change their positions and I need to later be able to see WHICH positions were occupied by WHAT at a given time (WHEN).
Thus I added another domain class
EXAMPLE B
POSITIONS DC
validfrom
validto
positionmap (1: dog, 2: cat, 3:foo, 4:bar)
belongsTo SITE
SITE DC
sitename
As all the objects will have a description AND there will be loads of SITEs using the same objects in it, I added a domain class to hold each and every OBJECT. Result:
EXAMPLE C
OBJECT DC
name
description
POSITIONS DC
validfrom
validto
positionmap (1: reference_to_dog, 2: reference_to_cat, 3:reference_to_foo, 4:reference_to_bar)
belongsTo SITE
SITE DC
sitename
Now the Map, to me, doesn't seem reasonable anymore.
I am thinking about removing the map, replacing it by yet another domain class:
EXAMPLE D
OBJECT DC
name
description
OBJECT-TO-POSITION DC
positionnumber (1, 2, 3, 4, ...)
object (reference_to_cat, reference_to_dog, reference_to_foo, ...)
belongsTo POSITIONS
POSITIONS DC
validfrom
validto
belongsTo SITE
SITE DC
sitename
QUESTIONS
Is it generally a good/bad idea to put maps into the database?
How could this be achieved easier?
What's the most performant way to do it?
EDIT: A NEW APPROCH BASED ON ROBS ANSWER
Inspired by your suggestion, Rob, I drafted this data model which grants a "front-row" role to the "SeasonPlan" (amended "ResidenceHistory").
class Zoo {
static hasMany = [seasons: SeasonPlan]
String name
}
// one way of representing histories
class SeasonPlan = {
static belongsTo = [zoo: Zoo] // a SeasonPlan belongs to a single particular Zoo
static hasMany = [cages: Cage]
DateTime from
DateTime until
}
class Cage {
static belongsTo = [seasonPlan: SeasonPlan] // a cage belongs to a single seasonplan
Species species // a cage has a single Species
Integer cageNumber
}
class Species {
// static hasMany = [cages: Cage] // commented out - no reverse-lookup necessary
String name
}
This has one drawback: For every season plan there's a new cage - though in reality the cages stay the same! (Imagine a "Integer squareMeters" within the "Cage" to make it more obvious why this isn't desired.)
For me applying such a thing to a data model is often hard to comprehend - how do I fit "pseudo-static" data like this into the application while retaining real-world corelation?
I hope what I mean is understandable - sorry if not.
I'm still trying to understand your domain; you may be overcomplicating things a bit. Would this basic model work? If not, can you explain why? I'll update my example if I can better understand your conditions.
Edit - updated example given the comments below.
class Cage {
static belongsTo = [zoo: Zoo] // a cage belongs to a single particular Zoo
Species species // a cage has a single Species
String name
}
class Zoo {
static hasMany = [cages: Cage]
String name
}
class Species {
static hasMany = [cages: Cage] // a species can be in many different cages
String name
}
// one way of representing histories
class ResidenceHistory = {
Species species
Cage cage
DateTime from
DateTime until
}
Here's how you might use the domain, then:
def sanDiego = new Zoo(name: 'San Diego Zoo').save()
def aviary = new Cage(name: 'Aviary', zoo: sanDiego).save()
def elephantCage = new Cage(name: 'Elephant Area, Cage 5', zoo: sanDiego).save()
def bird = new Species(name: 'Blue-Footed Booby', cage: aviary).save()
def elephant = new Species(name: 'Asian Elephant', cage: elephantCage).save()
new ResidenceHistory(species: bird, cage: aviary, from: new DateTime(), to: new DateTime().plusDays(20)).save()
To answer your listed questions specifically:
It depends - usually I'd prefer using the database's relational capabilities instead of storing maps in the database. It's much easier to work with the raw data if they aren't serialized Java objects.
See my example above.
This shouldn't matter unless performance becomes an issue. Use the solution that makes the most sense and is the easiest to maintain, and if you find that it causes performance problems, profile your application and fix the problems individually.