First install Mongo, then download Jongo and you're all set.
DB db = new Mongo().getDB("dbname"); Jongo jongo = new Jongo(db); MongoCollection friends = jongo.getCollection("friends"); Iterable<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 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("{$set: {address: #}}", new Address(..));
Insert works as in Mongo shell.
friends.insert("{name: 'Joe', age: 18}");
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. It can be handled with the dedicated ObjectId
class — named _id
or annotated @JsonProperty("_id")
— or with a simple String
— annotated @Id
.
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 before persisting.
public class Friend { @Id private String key; }
public class Friend { // generate it! private long _id; }
public class Friend { @JsonProperty("_id") private ObjectId key; }
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}"
.
Iterable<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 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(Friend.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(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);
As said in the mapping section, _id
identifier can be manipulated with a String
annotated with @Id
. If you want to completly avoid using ObjectId
, Jongo offers the Oid
class.
import static org.jongo.Oid.withOid; Friend joe = new Friend(); // @Id String key 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]}}
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'}");
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);
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 .3, Jongo uses Mongo Java driver as a deeper level to enhance performance. Instead of converting results into strings and unmarshalling strings into objects, it works at byte[]
level (note that query marhalling has been kept the old way, because of its specificities).
Several microbenchmarks (using Caliper & Yourkit) highlight this release net improvements: Jongo .3 is 6 times faster than Jongo .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 a set of predefined features for the embedded Jackson ObjectMapper
. 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.
Iterable<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 @Id
annotation on String
avoid this problem). For Jersey, use this Jackson Provider to expose ObjectId
as String
. For other web services libraries, adapting it won't be to hard.
JacksonMapper
can be modified or replacedResultHandler
to avoid confusionsave()
can intercept the _id
with an ObjectIdUpdater
before it is setted_id
attribute can be String
instead of ObjectId
Jongo relies upon Jackson 2.1, Bson4Jackson 2.1 and Mongo Java Driver 2.8+. Its Maven dependency 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>0.3</version> </dependency>