By default, Jongo uses Jackson to (un)marshall objects: a no-arg constructor, even private, is enough to write an object into Mongo and read from it.
Note that every Mongo document have an _id field. Just name id property _id or annotate it with
@JsonProperty("_id"). The object type can be ObjectId – it will be generated
Mongo side – or any Java type, other than array, so long as it is unique.
Careful, saving a document with a custom _id type (ie: String, ...) involve always to set it before persisting.
public class Friend {
private ObjectId _id;
}
public class Friend {
@JsonProperty("_id")
private ObjectId key;
}
public class Friend {
// you must generate it!
private String _id;
}
As expected with Jackson, fields are automatically mapped to their equivalent json properties (and complex types mapped as sub-properties). No extra annotations needed. For fine grained control over (un)marshalling, refer to Jackson documentation or use your own (un)marshaller.
To handle polymorphism of Java objects, a field containing the class name must be set into the Mongo
document. One can persist a Fox (sub-class of Friend) and manipulate it
as a Friend. This feature is provided out-of-the-box by Jackson. Using another
(un)marshaller implies extra work.
@JsonTypeInfo(use = Id.CLASS, property = "_class")
public class Friend {
int age;
}
public class Fox extends Friend {
String name, color;
}
friends.save(new Fox("fantastic", "red-haired"));
Friend friend = friends.findOne("{name:'fantastic'}").as(Friend.class);
Fox fantastic = (Fox)friend;
Using a singular reserved name, like _class, for this field is a good idea.
Simply pass an object to the save(...) method, built-in Jackson marshaller will be used to
convert it to json. Object mapping section defines what objects have to look
like.
friends.save(new Friend("Joe", 27));
Update syntax is a bit different than in Mongo shell: 'modifier' query has to be passed using with(...) method.
Upsert and multi options are no longer boolean but flag methods
friends.update("{name: 'Joe'}").with("{$inc: {age: 1}}");
friends.update("{name: 'Joe'}").upsert().multi().with("{$inc: {age: 1}}");
friends.update("{name: 'Joe'}").upsert().with("{$inc: {age: 1}}");
Remove works as if you were in Mongo shell.
friends.remove("{name: 'Joe'}");
friends.remove(new ObjectId("4c...e"));
Query syntax is almost the same as in Mongo shell: copy/paste, it just works.
Strings have to be escaped with single quotes "{name: 'John'}", numbers don't "{age:
18}".
Iterable<Friend> all = friends.find("{name: 'John'}").as(Friend.class);
Friend john = friends.findOne("{name: 'John'}").as(Friend.class);
Friend john = friends.findOne(new ObjectId("4c...e")).as(Friend.class);
Field names only need quotes when using dot notation "{'address.city': 'London'}".
Field selection aka. partial loading is not written as in Mongo shell:
Jongo exposes a fields(...) method. A json selector must be provided: {field:
1} to include it, {field: 0} to exclude it.
friends.find("{}").fields("{lastname: 1, address: 1}").as(User.class);
Documents can be sorted in ascending or descending order using a json selector: {field: 1}
for ascending, {field: -1} for descending.
friends.find("{}").sort("{firstname: 1}").as(User.class);
friends.find("{}").sort("{lasttname: -1}").as(User.class);
Behaviour similar to Java driver DBCursor.
friends.find("{}").skip(20).as(User.class);
friends.find("{}").limit(10).as(User.class);
Almost all queries in Jongo can be templated: add anchors # as follow. Binded parameters can be BSON
Primitives or any complex type made of those.
friends.find("{name: #, age: #}", "John", 18); //→ will produce {name: 'John', age: 18}
List<String> ages = Lists.newArrayList(22, 63);
friends.find("{age: {$in:#}}", ages); //→ will produce {age: {$in:[22,63]}}
Behaviour similar to Java driver DBCollection.count(...).
friends.count("{name: 'John'}");
Distinct syntax is almost the same as Find/FindOne operations. Optional query can be setted using query(...) method
List<String> names = friends.distinct("name").as(String.class);
List<Address> addresses = friends.distinct("address").query("{name: 'John'}").as(Address.class);
This feature will only be available in the Mongo v2.2 release and Jongo already supports it!
Just use a v2.1.0+ (unstable) Mongo server.
All aggregation operators are supported $project, $match, $limit, $skip, $unwind, $group, $sort and can be piped using and(...) method
collection.aggregate("{$project:{sender:1}}")
.and("{$match:{tags:'read'}}")
.and("{$limit:10}")
.as(Email.class);
Using Mongo geospacial capacities requires an index and some BSON operators.
friends.ensureIndex("{address: '2d'}");
friends.find("{address: {$near: [0, 0], $maxDistance: 5}}").as(Address.class);
Mongo uses a behind the scenes
syntactic sugar for methods like sort(...), limit(...) and others. The
complete expression can also be used in Jongo.
friends.find("{$query: {}, $maxScan: 2}").as(Friend.class);
friends.find("{$query: {}, $orderby: {name: 1}}").as(Friend.class);
If one prefers to manually map objects (ie. without unmarshaller), he can implements
ResultMapper. Each result entity will be passed to the inherited map(DBObject result) method.
Iterable<Integer> ages = col.find("{'name':'John'}").map(
new ResultMapper<Integer>() {
@Override
public Integer map(DBObject result) {
//do manual stuff here
return result.get("age");
}
}
);
Instead of Jackson, one can provide his own implementation of Marshaller and Unmarshaller.
class CustomMarshaller implements Marshaller {
<T> String marshall(T obj) { ... }
};
class CustomUnmarshaller implements Unmarshaller {
<T> T unmarshall(String json, Class<T> clazz) { ... }
};
Jongo jongo = new Jongo(mongo.getDB("dbname"), customMarshaller, customUnmarshaller);
Because Jongo uses Jackson by default, the same objects cannot be exposed with Jersey if they have an ObjectId. To make Jongo and Jersey work together, use this custom Jackson Provider, it exposes ObjectId as String.
If in doubt, this full working sample put everything together.
Jongo is lighting fast. Not because it is made of ancient wood and magic stones, but because it binds Jackson — the fastest Java json (un)marshalling library — to Mongo Java driver with the slightest glue code possible.
Jongo's performance is currently under our microscope and we already noticed some great improvements over our main competitor: Morphia. Keep tuned.
save() and update() can be configured with a WriteConcernsave(), insert(), update() and remove() return a WriteResult for every callObjectId is generated Mongo-side, it is setted in the object _id attributeupdate(String, String) becomes update(String).with(String)distinct() returns List<T> instead of Iterable<T>find() and findOne()Jongo is deployed into OSS Sonatype (Maven repository hosting service for open source projects).
<dependency>
<groupId>org.jongo</groupId>
<artifactId>jongo</artifactId>
<version>0.2</version>
</dependency>