First install Mongo, then choose a driver's version, finally download Jongo and you're all set.
DB db = new MongoClient().getDB("dbname");
Jongo jongo = new Jongo(db);
MongoCollection friends = jongo.getCollection("friends");
MongoCursor<Friend> all = friends.find("{name: 'Joe'}").as(Friend.class);
Friend one = friends.findOne("{name: 'Joe'}").as(Friend.class);
Passing an object to the save(..) method will do the job. See mapping
section for objects defintion.
Friend joe = new Friend("Joe", 27);
friends.save(joe);
joe.age = 28;
friends.save(joe);
Update syntax is a bit different than in Mongo shell: modifier query has to be passed
using
with(..). You can update with a String or with an object. You can also
parameterize queries with # (using primitives or complex objects), see query templating for more details.
friends.update("{name: 'Joe'}").with("{$inc: {age: 1}}");
friends.update("{name: 'Joe'}").upsert().multi().with("{$inc: {age: 1}}");
friends.update("{name: 'Joe'}").with(new Friend(..));
friends.update("{name: 'Joe'}").with("{$set: {address: #}}", new Address(..));
Insert works as in Mongo shell.
friends.insert("{name: 'Joe', age: 18}");
friends.insert(new Friend(..));
friends.insert(new Friend(..), new Friend(..));
Remove works as in Mongo shell.
friends.remove("{name: 'Joe'}");
friends.remove(new ObjectId("4c...e"));
Query results are automatically mapped to objects. By default, this relies on Jackson; it respects
document structure, handles lists and ignores missing attributes. It just needs a no-arg
constructor, even private (if the object has to be immutable, @JsonCreator
annotation can be used instead).
_id is a unique identifier available on every Mongo document. If it isn't
setted,
it is generated. To handle it with Jongo, one attribute has to be named _id or
annotated with @Id (alias for @JsonProperty("_id")). It can be
handled
with the dedicated ObjectId class or with a simple String — annotated
@ObjectId.
Careful, saving a document with a custom _id (ie: any Java type, other than array,
so
long as it is unique) always involves to set it manually before persisting.
public class Friend {
// manual
private long _id;
}
public class Friend {
@Id // manual
private long key;
}
public class Friend {
@Id // manual
private String key;
}
public class Friend {
@ObjectId // auto
private String _id;
}
public class Friend {
@Id @ObjectId // auto
private String key;
}
public class Friend {
// auto
private ObjectId _id;
}
JSON properties are automatically mapped to their equivalent object attributes (and
complex ones mapped as sub-attributes). No extra annotations needed. To ignore
a
pojo attribute, annotate it with @JsonIgnore.
For fine grained control over
(un)marshalling, configure Jongo mapper or create
your own.
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.
Query syntax is almost the same as in Mongo shell: copy/paste, it just works.
Strings have to be escaped with single quotes "{name: 'Joe'}", numbers don't
"{age:
18}".
MongoCursor<Friend> all = friends.find("{name: 'Joe'}").as(Friend.class);
Friend joe = friends.findOne("{name: 'Joe'}").as(Friend.class);
Friend joe = friends.findOne(new ObjectId("4c...e")).as(Friend.class);
Field names need quotes when using dot notation "{'address.city': 'London'}".
Field selection aka. partial loading is not written as in Mongo shell:
Jongo exposes a projection(..) method. A json selector must be provided: {field:
1} to include it, {field: 0} to exclude it.
friends.find().projection("{lastname: 1, address: 1}").as(Friend.class);
Documents can be sorted in ascending or descending order using a json selector: {field:
1}
for ascending, {field: -1} for descending.
friends.findOne().orderBy("{firstname: 1}").as(Friend.class);
friends.find().sort("{firstname: 1}").as(Friend.class);
friends.find().sort("{lasttname: -1}").as(Friend.class);
Behaviour similar to Java driver DBCursor.
friends.find().skip(20).as(Friend.class); friends.find().limit(10).as(Friend.class);
Behaviour similar to Java driver Find.hint(..).
friends.find().hint("{age: 1}").as(Friend.class);
Query modifiers can be setted on DBCursor by implementing QueryModifier.
friends.find().with(new QueryModifier() {
public void modify(DBCursor cursor) {
cursor.batchSize(10);
}
}).as(Friend.class);
You can close cursor manually by calling close() method on MongoCursor
MongoCursor<Friend> cursor = friends.find().as(Friend.class); //iterate over it cursor.close();
As said in the mapping section, _id identifier can be
manipulated
with a String annotated with @ObjectId. If you want to completly avoid
using
driver's ObjectId, Jongo offers the Oid class.
import static org.jongo.Oid.withOid; Friend joe = new Friend(); // @ObjectId String _id friends.save(joe); friends.find(withOid(joe.id)).as(Friend.class); // instead of new ObjectId(joe.id)
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:#}", "Joe", 18); //→ will produce {name: 'Joe', age: 18}
friends.find("{address: #}", new Address(..)); //→ will marshall Address object
List<String> ages = Lists.newArrayList(22, 63);
friends.find("{age: {$in:#}}", ages); //→ will produce {age: {$in:[22,63]}}
Projections can also be templated
friends.find().projection("{name:#",1).as(Friend.class);
friends.find().projection("{family:{$elemMatch:{name:#}}}","Daddy").as(Friend.class);
Classic $regex: 'jo.*' operator works in Jongo. It worth mentioning that Pattern.compile("jo.*")
also does. Unluckily, JavaScript syntax /jo*/g is not available in Mongo Java Driver
and
Jongo cannot use it yet.
collection.findOne("{name: {$regex: #}}", "jo.*").as(Friend.class);
collection.findOne("{name:#}", Pattern.compile("jo.*")).as(Friend.class);
Behaviour similar to Java driver DBCollection.count(..).
friends.count("{name: 'Joe'}");
Note that a count() method is also available on MongoCursor, behaviour similar to DBCursor.count()
MongoCursor<Friend> cursor = friends.find().as(Friend.class); int nbResults = cursor.count();
Behaviour pretty similar to Java driver db.runCommand(..).
jongo.runCommand("{geoNear: 'friends', near: [48,9]}").as(GeoNearResult.class);
You can map command results to a single result field.
jongo.runCommand("{geoNear: 'friends', near : [48.690,9.140]}")
.throwOnError()
.field("results")
.as(GeoNearResult.class);
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: 'Joe'}").as(Address.class);
Careful, this feature is only available in the Mongo v2.2 release. 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);
Read and write
behaviors can be configured on a query basis. ReadPreference defines how multiple
instances are readed, WriteConcern defines how writes are done.
friends.withWriteConcern(SAFE).withReadPreference(primaryPreferred()).find(..);
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);
Jongo is lighting fast. Not because it is made of ancient wood and magic stones, it just binds Jackson — the fastest Java json (un)marshalling library — to Mongo Java driver with the slightest glue code possible.
Since 0.3, Jongo uses Mongo Java driver at a deeper level to enhance performance. Instead of
converting
results into strings and unmarshalling strings into objects, it works at byte[] level (query
marhalling works the same since 1.0).
Several microbenchmarks (using Caliper & Yourkit) highlight this release net improvements: Jongo 0.3 is 6 times faster than Jongo 0.2, which make it as fast as the driver! Have a look at find microbenchmark and save microbenchmark, and at their implementations (find & save).
Not all benchmarks accurately reflect real world performance; more on microbenchmarks.
Jongo comes with a custom Jackson configuration that can be extended.
DB db = new Mongo().getDB("dbname");
Jongo jongo = new Jongo(db,
new JacksonMapper.Builder()
.registerModule(new JodaModule())
.enable(MapperFeature.AUTO_DETECT_GETTERS)
.withView(Views.Public.class)
.build()
);
Jongo constructor comes with an optional parameter: Mapper. This interface has a
provided
JacksonMapper implementation which defines the defaut (un)marshalling behavior of
Jongo.
JacksonMapper.Builder allows to alter this configuration as you see fit.
Default JacksonMapper.Builder() comes with predefined features for the embedded Jackson
ObjectMapper (see VisibilityModifier and PropertyModifier).
Those can be overridden and extended with enable(..) and disable(..)
methods.
Jackson Module is a set of (un)marshalling configuration. Hanlding Joda dates, for
example,
can be done with this Jackson
module and the registerModule(..) method.
Jackson @JsonView allows to filter an object attributes on (un)marshalling. Once Jongo
is
configured to use the Public view (as in the previous configuration), the gender
attribute will be ignored even if it exists in Mongo.
Careful, when no view is defined, Jackson ignore annotations — which means every attribute is visible. Views can be used for marshalling-only or unmarshalling-only if needed.
public class Friend {
private String _id;
@JsonView(Views.Private.class)
private String gender;
}
public class Views {
public static class Public {}
public static class Private extends Public {}
}
Jongo uses the MapperModifier interface to configure a JacksonMapper.
Looking
at one implementation give a good feeling of how it can be used to further
configure a JacksonMapper.
In certain circumstances, modifying Jackson ObjectReader or ObjectWriter
will
be useful. JacksonMapper offers one default ReaderCallback and one WriterCalback
for that purpose, they can be replaced if needed.
For more options, refer to Jackson documentation.
Instead of Jackson, one can provide his own implementation of Mapper.
public class GsonMapper extends Mapper {
Marshaller getMarshaller() { ... }
Unmarshaller getUnmarshaller() { ... }
ObjectIdUpdater getObjectIdUpdater() { ... }
QueryFactory getQueryFactory() { ... }
}
Jongo jongo = new Jongo(mongo.getDB("dbname"), new GsonMapper());
If one prefers to manually handle objects (ie. without unmarshaller), he can implements
ResultHandler. Each result entity will be passed to the inherited handle(DBObject
result) method.
MongoCursor<Integer> ages = friends.find("{name: 'Joe'}").map(
new ResultHandler<Integer>() {
@Override
public Integer handle(DBObject result) {
//do manual stuff here
return result.get("age");
}
}
);
Because Jongo uses Jackson by default, the same objects can be exposed through Jersey (or Spring
or
any web services library). If you decide to keep ObjectId in pojo attributes, the
library (de)serialization have to be configured to support them (using @ObjectId
annotation on String avoid this problem).
For Jersey, use this Jackson Provider to expose ObjectId as String
(and ignore @ObjectId annotation). For other web services libraries, adapting it
won't be to hard.
Previous releases: 1.0, 0.4, 0.3, 0.2, 0.1
Jongo relies upon Jackson 2.4.1, Bson4Jackson 2.4.0 and Mongo Java Driver 2.11+. Its Maven dependency, an OSGI compliant jar, comes with the first two, you have to provide a dependency to the driver.
Jongo is deployed into OSS Sonatype (Maven repository hosting service for open source
projects).
Add the following dependency to your pom.xml
<dependency>
<groupId>org.jongo</groupId>
<artifactId>jongo</artifactId>
<version>1.1</version>
</dependency>